From ababb9ff93254eb358dd3c5555080e0669efb060 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 1 Oct 2002 14:21:01 +0000 Subject: [PATCH] Initial revision --- lbtt/AUTHORS | 1 + lbtt/COPYING | 340 ++ lbtt/ChangeLog | 201 + lbtt/INSTALL | 229 ++ lbtt/Makefile.am | 1 + lbtt/NEWS | 191 + lbtt/README | 85 + lbtt/configure.ac | 190 + lbtt/doc/Makefile.am | 3 + lbtt/doc/gpl.texi | 392 ++ lbtt/doc/intersectioncheck.eps | 747 ++++ lbtt/doc/intersectioncheck.png | Bin 0 -> 14956 bytes lbtt/doc/intersectioncheck.txt | 55 + lbtt/doc/lbtt.texi | 4771 +++++++++++++++++++++++ lbtt/doc/testprocedure.eps | 769 ++++ lbtt/doc/testprocedure.png | Bin 0 -> 14430 bytes lbtt/doc/testprocedure.txt | 54 + lbtt/doc/texinfo.tex | 6257 ++++++++++++++++++++++++++++++ lbtt/src/Alloc.h | 159 + lbtt/src/BitArray.cc | 457 +++ lbtt/src/BitArray.h | 430 ++ lbtt/src/Bitset.h | 626 +++ lbtt/src/BuchiAutomaton.cc | 1084 ++++++ lbtt/src/BuchiAutomaton.h | 861 ++++ lbtt/src/Config-lex.ll | 258 ++ lbtt/src/Config-parse.yy | 1381 +++++++ lbtt/src/Configuration.cc | 1991 ++++++++++ lbtt/src/Configuration.h | 627 +++ lbtt/src/DispUtil.cc | 183 + lbtt/src/DispUtil.h | 163 + lbtt/src/EdgeContainer.h | 28 + lbtt/src/Exception.h | 1170 ++++++ lbtt/src/ExternalTranslator.cc | 220 ++ lbtt/src/ExternalTranslator.h | 388 ++ lbtt/src/FormulaRandomizer.cc | 233 ++ lbtt/src/FormulaRandomizer.h | 333 ++ lbtt/src/FormulaWriter.h | 452 +++ lbtt/src/Graph.h.in | 1859 +++++++++ lbtt/src/LbtWrapper.h | 135 + lbtt/src/LtlFormula.cc | 1329 +++++++ lbtt/src/LtlFormula.h | 2659 +++++++++++++ lbtt/src/Makefile.am | 22 + lbtt/src/NeverClaim-lex.ll | 187 + lbtt/src/NeverClaim-parse.yy | 301 ++ lbtt/src/NeverClaimAutomaton.cc | 323 ++ lbtt/src/NeverClaimAutomaton.h | 460 +++ lbtt/src/PathEvaluator.cc | 987 +++++ lbtt/src/PathEvaluator.h | 169 + lbtt/src/PathIterator.cc | 202 + lbtt/src/PathIterator.h | 208 + lbtt/src/ProductAutomaton.cc | 1061 +++++ lbtt/src/ProductAutomaton.h | 600 +++ lbtt/src/Random.h | 96 + lbtt/src/SccIterator.h | 752 ++++ lbtt/src/SharedTestData.h | 55 + lbtt/src/SpinWrapper.cc | 123 + lbtt/src/SpinWrapper.h | 126 + lbtt/src/StatDisplay.cc | 1455 +++++++ lbtt/src/StatDisplay.h | 117 + lbtt/src/StateSpace.cc | 459 +++ lbtt/src/StateSpace.h | 454 +++ lbtt/src/StateSpaceRandomizer.cc | 291 ++ lbtt/src/StateSpaceRandomizer.h | 158 + lbtt/src/StringUtil.cc | 227 ++ lbtt/src/StringUtil.h | 198 + lbtt/src/TestOperations.cc | 1668 ++++++++ lbtt/src/TestOperations.h | 539 +++ lbtt/src/TestRoundInfo.h | 246 ++ lbtt/src/TestStatistics.cc | 100 + lbtt/src/TestStatistics.h | 584 +++ lbtt/src/TranslatorInterface.h | 91 + lbtt/src/UserCommandReader.cc | 927 +++++ lbtt/src/UserCommandReader.h | 181 + lbtt/src/UserCommands.cc | 2282 +++++++++++ lbtt/src/UserCommands.h | 168 + lbtt/src/getopt.c | 1055 +++++ lbtt/src/getopt1.c | 188 + lbtt/src/gnu-getopt.h | 180 + lbtt/src/main.cc | 725 ++++ lbtt/src/translate.cc | 230 ++ lbtt/src/translate.h | 43 + 81 files changed, 49550 insertions(+) create mode 100644 lbtt/AUTHORS create mode 100644 lbtt/COPYING create mode 100644 lbtt/ChangeLog create mode 100644 lbtt/INSTALL create mode 100644 lbtt/Makefile.am create mode 100644 lbtt/NEWS create mode 100644 lbtt/README create mode 100644 lbtt/configure.ac create mode 100644 lbtt/doc/Makefile.am create mode 100644 lbtt/doc/gpl.texi create mode 100644 lbtt/doc/intersectioncheck.eps create mode 100644 lbtt/doc/intersectioncheck.png create mode 100644 lbtt/doc/intersectioncheck.txt create mode 100644 lbtt/doc/lbtt.texi create mode 100644 lbtt/doc/testprocedure.eps create mode 100644 lbtt/doc/testprocedure.png create mode 100644 lbtt/doc/testprocedure.txt create mode 100644 lbtt/doc/texinfo.tex create mode 100644 lbtt/src/Alloc.h create mode 100644 lbtt/src/BitArray.cc create mode 100644 lbtt/src/BitArray.h create mode 100644 lbtt/src/Bitset.h create mode 100644 lbtt/src/BuchiAutomaton.cc create mode 100644 lbtt/src/BuchiAutomaton.h create mode 100644 lbtt/src/Config-lex.ll create mode 100644 lbtt/src/Config-parse.yy create mode 100644 lbtt/src/Configuration.cc create mode 100644 lbtt/src/Configuration.h create mode 100644 lbtt/src/DispUtil.cc create mode 100644 lbtt/src/DispUtil.h create mode 100644 lbtt/src/EdgeContainer.h create mode 100644 lbtt/src/Exception.h create mode 100644 lbtt/src/ExternalTranslator.cc create mode 100644 lbtt/src/ExternalTranslator.h create mode 100644 lbtt/src/FormulaRandomizer.cc create mode 100644 lbtt/src/FormulaRandomizer.h create mode 100644 lbtt/src/FormulaWriter.h create mode 100644 lbtt/src/Graph.h.in create mode 100644 lbtt/src/LbtWrapper.h create mode 100644 lbtt/src/LtlFormula.cc create mode 100644 lbtt/src/LtlFormula.h create mode 100644 lbtt/src/Makefile.am create mode 100644 lbtt/src/NeverClaim-lex.ll create mode 100644 lbtt/src/NeverClaim-parse.yy create mode 100644 lbtt/src/NeverClaimAutomaton.cc create mode 100644 lbtt/src/NeverClaimAutomaton.h create mode 100644 lbtt/src/PathEvaluator.cc create mode 100644 lbtt/src/PathEvaluator.h create mode 100644 lbtt/src/PathIterator.cc create mode 100644 lbtt/src/PathIterator.h create mode 100644 lbtt/src/ProductAutomaton.cc create mode 100644 lbtt/src/ProductAutomaton.h create mode 100644 lbtt/src/Random.h create mode 100644 lbtt/src/SccIterator.h create mode 100644 lbtt/src/SharedTestData.h create mode 100644 lbtt/src/SpinWrapper.cc create mode 100644 lbtt/src/SpinWrapper.h create mode 100644 lbtt/src/StatDisplay.cc create mode 100644 lbtt/src/StatDisplay.h create mode 100644 lbtt/src/StateSpace.cc create mode 100644 lbtt/src/StateSpace.h create mode 100644 lbtt/src/StateSpaceRandomizer.cc create mode 100644 lbtt/src/StateSpaceRandomizer.h create mode 100644 lbtt/src/StringUtil.cc create mode 100644 lbtt/src/StringUtil.h create mode 100644 lbtt/src/TestOperations.cc create mode 100644 lbtt/src/TestOperations.h create mode 100644 lbtt/src/TestRoundInfo.h create mode 100644 lbtt/src/TestStatistics.cc create mode 100644 lbtt/src/TestStatistics.h create mode 100644 lbtt/src/TranslatorInterface.h create mode 100644 lbtt/src/UserCommandReader.cc create mode 100644 lbtt/src/UserCommandReader.h create mode 100644 lbtt/src/UserCommands.cc create mode 100644 lbtt/src/UserCommands.h create mode 100644 lbtt/src/getopt.c create mode 100644 lbtt/src/getopt1.c create mode 100644 lbtt/src/gnu-getopt.h create mode 100644 lbtt/src/main.cc create mode 100644 lbtt/src/translate.cc create mode 100644 lbtt/src/translate.h diff --git a/lbtt/AUTHORS b/lbtt/AUTHORS new file mode 100644 index 000000000..87908cad0 --- /dev/null +++ b/lbtt/AUTHORS @@ -0,0 +1 @@ +lbtt was written by Heikki Tauriainen diff --git a/lbtt/COPYING b/lbtt/COPYING new file mode 100644 index 000000000..d60c31a97 --- /dev/null +++ b/lbtt/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/lbtt/ChangeLog b/lbtt/ChangeLog new file mode 100644 index 000000000..d15f24ea2 --- /dev/null +++ b/lbtt/ChangeLog @@ -0,0 +1,201 @@ +2002-10-01 Heikki Tauriainen + + * Version 1.0.1 released. + +2002-01 -- 2002-09-25 Heikki Tauriainen + + * Alloc.h: Use preprocessor macro HAVE_SINGLE_CLIENT_ALLOC + instead of HAVE_SGI_STL in #ifdef conditionals. + + * BitArray.cc (BitArray::BitArray): Do not clear the allocated + array after initialization. All callers updated to reflect the + changed constructor semantics. + + * BitArray.cc (BitArray::bitwiseAnd, BitArray::bitwiseOr) + (BitArray::bitwiseXor): New functions. + + * BitArray.cc (BitArray::equal, BitArray::subset) + (BitArray::count): Fix `&' operator precedence in comparisons. + + * BitArray.cc (BitArray::hammingDistance): Use the `bit_counts' + array to compute the result instead of scanning the array bit by + bit. + + * BitArray.cc: Documentation fixes. + + * BitArray.h (BitArray::bitwiseAnd, BitArray::bitwiseOr) + (BitArray::bitwiseXor): New functions. + + * BitArray.h: Documentation fixes. + + * Bitset.h (Bitset::Bitset(const BitArray&, const unsigned long)) + (Bitset::Bitset(const Bitset&)): Use `memcpy' instead of + `BitArray::copy'. + + * Bitset.h (Bitset::operator=(const Bitset&)): Reallocate memory + only if necessary. + + * Bitset.h: Documentation fixes. + + * BuchiAutomaton.cc (BuchiAutomaton::regularize): Changed + semantics: instead of modifying the object itself, the function + now returns a pointer to a newly allocated BuchiAutomaton (the + regularized automaton). + + * BuchiAutomaton.cc: Documentation fixes. + + * BuchiAutomaton.h (BuchiAutomaton::regularize): Changed + semantics (see above). + + * BuchiAutomaton.h + (BuchiAutomaton::BuchiTransition::enabled(const BitArray&, + const unsigned int)): New function. + (BuchiAutomaton::BuchiTransition::enabled(const Bitset&)): + Use the above function internally. + + * BuchiAutomaton.h + (BuchiAutomaton::BuchiState::print(ostream&, const int, const + GraphOutputFormat)): New function. + + * BuchiAutomaton.h (BuchiAutomaton::BuchiTransition::operator<) + (BuchiAutomaton::BuchiTransition::operator==): Fix function + prototypes to match those of the base class. Make functions + private to prevent external comparison of plain Graph::Edges + with BuchiAutomaton::BuchiTransitions. + + * Configuration.cc (Configuration::read): Use autoconf-generated + PACKAGE_VERSION macro for displaying program version instead of + including version.h. + + * configure.in: Renamed to configure.ac. Updated to Autoconf + 2.53 and Automake 1.6. Improved checking for the presence of + the slist STL extension. Revised checking for the availability of + the GNU readline library and the libraries to include. + Replace the old preprocessor macro HAVE_SGI_STL with new macros + HAVE_SINGLE_CLIENT_ALLOC and HAVE_SLIST. Introduce new + preprocessor macro SLIST_NAMESPACE. + + * EdgeContainer.h: Remove uses of redundant preprocessor macros. + + * Graph.h: Renamed to Graph.h.in to implement the optional + inclusion of the slist header using an autoconf substitution + variable. + + * Graph.h.in: Use HAVE_SLIST macro instead of HAVE_SGI_STL in + #ifdef conditionals. + + * Graph.h.in (Graph::Edge, Graph::Node): Make classes public (to + prevent warnings from Intel C++ Compiler). + + * Graph.h.in (Graph::operator[], Graph::node, Graph::size) + (Graph::expand, Graph::stats, Graph::subGraphStats) + (Graph::Edge::targetNode, Graph::Node::operator=) + (operator<<(ostream&, const Graph::Edge&) + (operator<<(ostream&, const Graph::Node&): + Added explicit `typename' specifiers to parameter and/or return + types (to prevent warnings from gcc 3.1). + + * Graph.h.in (Graph::subGraphStats): Make `s' a const variable. + + * Graph.h.in (Graph::Edge::ptr_equal::ptr_equal): Change + definition to match that of `Graph::Edge::ptr_less'. All callers + modified accordingly. + + * Graph.h.in (Graph::Edge::operator<, Graph::Edge::operator==): + Make member functions protected. + Make class Graph::Edge a friend of Graph::Edge::ptr_equal and + Graph::Edge::ptr_less. + + * LtlFormula.cc (LtlFormula): New static variable + `eval_proposition_id_limit' stores the maximum proposition + id during the evaluation of a strictly propositional formula. + + * LtlFormula.cc (LtlFormula::sat_eval): Make `max_atom' a const + variable. + Access the identifier of a proposition through Atom::getId. + + * LtlFormula.h + (LtlFormula::evaluate(const BitArray&, const unsigned long int)): + New function. + (LtlFormula::evaluate(const Bitset&)): Use the above function + internally. + + * LtlFormula.h (ptr_less): Make class public. + + * LtlFormula.h (LtlFormula::eval): Use a BitArray instead of a + Bitset internally. + + * LtlFormula.h (Atom::eval, Constant::eval, LtlNegation::eval) + (UnaryFormula::eval, LtlNext::eval, LtlFinally::eval) + (LtlGlobally::eval, BinaryFormula::eval, LtlDisjunction::eval) + (LtlConjunction::eval, LtlImplication::eval, LtlEquivalence::eval) + (LtlXor::eval, LtlUntil::eval, LtlV::eval, LtlWeakUntil::eval) + (LtlStrongRelease::eval, LtlBefore::eval): Use BitArray instead + of a Bitset for evaluation. Make subformula parameters (if any) + `const'. + + * main.cc (testLoop): Use autoconf-generated PACKAGE_VERSION + macro for displaying program version instead of including + version.h. + + * src/Makefile.am: Remove redundant references to @LEXLIB@ (the + sources are independent of any external lexer library). + + * NeverClaimAutomaton.cc (NeverClaimAutomaton::write): Add + detection for jumps to undefined never claim labels. + + * NeverClaim-parse.yy (yyerror): Fix computation of the position + of a parse error. + + * PathEvaluator.cc (PathEvaluator::eval) Avoid creating a + temporary Bitset object during evaluation of atomic propositions. + + * ProductAutomaton.cc (ProductAutomaton::computeProduct): + Avoid creating a temporary Bitset object when checking the + enabledness of a product transition. + + * ProductAutomaton.cc (ProductAutomaton::findAcceptingExecution) + Use BitArrays instead of Bitsets. + + * ProductAutomaton.h (ProductAutomaton::operator[]) + (ProductAutomaton::node): Make state id parameter `const'. + + * ProductAutomaton.h (ProductAutomaton::ProductState::print): + Change function prototype to match that of the base class. + + * SccIterator.h: Add `typename' specifiers to prevent warnings + from gcc 3.1. + + * SpinWrapper.cc (SpinWrapper::SpinAutomaton::parseAutomaton): + Remove redundant `try' block. + + * StateSpace.cc (StateSpace::State::print): Fix typo when + displaying empty sets of propositions in dot format (the open + brace was previously missing). + + * StateSpace.h + (StateSpace::print(ostream&, const int, const GraphOutputFormat): + New function. + + * TestOperations.cc (performEmptinessCheck): Added a colon to + the end of the "Accepting cycles" message. + + * TestRoundInfo.h: Removed redundant inclusion of BitArray.h. + + * translate.cc (main): Use autoconf-generated PACKAGE_VERSION + macro for displaying program version. + + * translate.cc (main): Fix bug in checking the number of + command line arguments. + + * UserCommands.cc (printCrossComparisonAnalysisResults): Fix + bug in the construction of the witness path in which the formula + should be evaluated when the witness path is extracted directly + from the original state space (i.e., when analyzing results + against the internal model checking algorithm). + + * version.h.in: Removed. + +2001-11-12 Heikki Tauriainen + + * Version 1.0.0 released. diff --git a/lbtt/INSTALL b/lbtt/INSTALL new file mode 100644 index 000000000..a4b34144d --- /dev/null +++ b/lbtt/INSTALL @@ -0,0 +1,229 @@ +Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/lbtt/Makefile.am b/lbtt/Makefile.am new file mode 100644 index 000000000..1af203b06 --- /dev/null +++ b/lbtt/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = doc src diff --git a/lbtt/NEWS b/lbtt/NEWS new file mode 100644 index 000000000..1f3705dc8 --- /dev/null +++ b/lbtt/NEWS @@ -0,0 +1,191 @@ +lbtt NEWS -- history of user-visible changes. 01 Oct 2002 +Copyright (C) 2002 Heikki Tauriainen + + Permission is granted to anyone to make or distribute verbatim copies + of this document as received, in any medium, provided that the + copyright notice and this permission notice are preserved. + + Permission is granted to distribute modified versions + of this document, or of portions of it, under the above conditions, + provided also that they carry prominent notices stating who last + changed them. + +Please send bug reports to . + +Version 1.0.1 + +* This release does not add new functionality to the program apart from + some bug fixes and changes to sources to make them more compatible + with versions 3.1.x and 3.2 of gcc. + +Version 1.0.0 + +* lbtt is now packaged using GNU Autotools. + +* The distribution includes sources for documentation that can be + built in `info', `dvi' and `html' formats using GNU texinfo. The + documentation is also available at the program's home page at + . + +* lbtt now has direct support for the following binary LTL formula + operators: + --------------------------------------------------------------- + operator symbol used in input symbol used in + files for LTL-to-Büchi messages + translators + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + logical "exclusive or" + ^ xor + temporal "weak until" + W W + temporal "strong release" (dual of "weak until") + M M + temporal "before" + B B + + The ^ operator is also accepted in the transition guard formulas of the + Büchi automata that are given to lbtt as input. + + Please see the included documentation for a reference on the exact + semantics of these operators. + +* Changes in program behavior: + + - The default priority for LTL formula operators is now 0 (instead of + 10 as in previous versions). + + - The default interval for changing state spaces is now one test round + (instead of 10 as in previous versions). + + - The sequences of random formulas and state spaces generated by lbtt + should now be independent of each other, i.e., changing the formula + generation parameters does not affect the sequence of generated state + spaces and vice versa. Unfortunately, the changes required to + implement this functionality make the test sequences obtained using + previous versions of the tool irreproducible with version 1.0.0. In + addition, the random seed must now be set separately for the formula + and state space generation algorithms. + +* Changes in program configuration: + + - lbtt supports the following new configuration file and command line + options (`O' -- command line option, `C' -- equivalent configuration + file option (SectionName.OptionName), `D' -- description): + + O: --[no]comparisontest + C: GlobalOptions.ComparisonTest = Yes | No + D: Enable or disable the model checking result cross-comparison + test + + O: --[no]consistencytest + C: GlobalOptions.ConsistencyTest = Yes | No + D: Enable or disable the model checking result consistency test + + O: --[no]intersectiontest + C: GlobalOptions.IntersectionTest = Yes | No + D: Enable or disable the Büchi automata intersection emptiness + test + + O: --profile + D: Disable all of the above automata correctness tests + + O: --skip=N + D: Skip the first N test rounds + + O: --nogeneratennf + C: FormulaOptions.GenerateMode = Normal + D: Do not force random formulas to be generated in negation + normal form + + O: --nooutputnnf + C: FormulaOptions.OutputMode = Normal + D: Do not rewrite LTL formulas into negation normal form before + passing them to LTL-to Büchi translators + + O: --quiet, --silent + D: Run all tests silently without interruption + + O: --showconfig + D: Display program configuration and exit + + O: --showoperatordistribution + D: Compute the expected distribution of random formula operators + and display the distribution with other configuration + information + + O: --xorpriority=PRIORITY + C: FormulaOptions.XorPriority = PRIORITY + D: Set priority for the logical "exclusive or" operator + + O: --weakuntilpriority=PRIORITY + C: FormulaOptions.WeakUntilPriority = PRIORITY + D: Set priority for the temporal "weak until" operator + + O: --strongreleasepriority=PRIORITY + C: FormulaOptions.StrongReleasePriority = PRIORITY + D: Set priority for the temporal "strong release" operator + + O: --beforepriority=PRIORITY + C: FormulaOptions.BeforePriority = PRIORITY + D: Set priority for the temporal "before" operator + + - The following configuration file and command line options have been + renamed in lbtt version 1.0.0: + + Command line options: + --synchronousproduct => --modelcheck + --localproduct => --localmodelcheck + --globalproduct => --globalmodelcheck + --randomseed => --formularandomseed, + --statespacerandomseed + --formulalength => --formulasize + --vpriority => --releasepriority + + Configuration file options: + GlobalOptions.FormulaChangeInterval + => FormulaOptions.ChangeInterval + GlobalOptions.StateSpaceChangeInterval + => StateSpaceOptions.ChangeInterval + GlobalOptions.SynchronousProduct + => GlobalOptions.ModelCheck + GlobalOptions.RandomSeed => FormulaOptions.RandomSeed + StateSpaceOptions.RandomSeed + FormulaOptions.Length => FormulaOptions.Size + FormulaOptions.VPriority => FormulaOptions.ReleasePriority + + Please see the included documentation for more information on these + options. + +* Changes in the user command interface: + + - lbtt's internal command interface now supports redirecting the + output of some internal commands to a pipe to be processed by an + external program. This can be achieved by ending the command line + with `| '. + + - If using random or enumerated paths as state spaces for the tests, + the user commands `resultanalysis' and `eval' now support referring + to results computed using lbtt's internal model checking algorithm. + + - The tool includes a new user command `consistencyanalysis' that can + be used for analyzing failures in the model checking result + consistency check. + + - The user command `resultanalysis' now accepts an optional state + space identifier which can be used to force the analysis to be + performed in a certain state of the state space. + + - The user command `pathinconsistencies' has been removed. (The `eval' + command provides equivalent functionality.) + + - lbtt now supports the GNU readline library that provides command + line editing enhancements when using the user commands. The support + can be optionally disabled by running the `configure' script with + the parameter `--without-readline'. + +* The additional programs `aasa_lbt' and `spin_lbt' have been replaced with + a common `lbtt-translate' utility. This program does not implement an + LTL-to-Büchi translation algorithm, however; a free LTL-to-Büchi + translator that provides similar functionality to the `aasa_lbt' tool + included in previous lbtt releases can be obtained via + . diff --git a/lbtt/README b/lbtt/README new file mode 100644 index 000000000..fcaf03f9f --- /dev/null +++ b/lbtt/README @@ -0,0 +1,85 @@ +lbtt version 1.0.1 +------------------ + +lbtt is a tool for testing programs which translate formulas +expressed in propositional linear temporal logic (LTL) into +Büchi automata. The goal of the tool is to assist in the +correct implementation of LTL-to-Büchi translation algorithms +by providing an automated testing environment for LTL-to-Büchi +translators. Additionally, the testing environment can be used +for very basic profiling of different LTL-to-Büchi translators +to evaluate their performance. + +The latest version of the program is available at +. + +lbtt is free software, you may change and redistribute it under +the terms of the GNU General Public License. lbtt comes with +NO WARRANTY. See the file COPYING for details. + + +Quick installation instructions: +-------------------------------- + + The basic procedure to build lbtt, the associated tools + and GNU info documentation is to enter the following + commands in the top-level package directory (the directory + that contains this file): + + ./configure + make + + These commands should create two executable files + (`lbtt' and `lbtt-translate') in the `src' subdirectory + and GNU info documentation under the `doc' subdirectory. + The program and documentation can be used directly in + their build directories. Optionally, the program binaries + and documentation can be installed in their default + location (under `/usr/local/') by entering the command + + make install + + after the build process is complete. (To change the + default installation directory, the `configure' script + should be invoked with an appropriate `--prefix=PATH' + option before running `make'. Please see the file INSTALL + for generic information about running `configure'.) + + +Documentation: +-------------- + + The default build procedure builds the program documentation + in the `doc' subdirectory only in `info' format. Assuming + you have the GNU Info documentation browser installed, the + documentation can be viewed by running the command + `info -f doc/lbtt.info' after the program build is + complete. + + The documentation can be optionally generated in DVI + or HTML formats using the TeX typesetting program and the + GNU texinfo tools. To create DVI documentation, run the + command + + make dvi + + in the top-level package directory. If you have TeX and + the GNU texinfo tools properly installed, this command + should generate a file `lbtt.dvi' in the `doc' subdirectory. + + The documentation can be also generated in HTML format by + running `makeinfo --html' on the file `lbtt.texi' in the + `doc' subdirectory. + + Note: The generated DVI or HTML files refer to two + auxiliary picture files (`testprocedure.EXT' and + `intersectioncheck.EXT', where EXT stands for `eps' for + DVI documentation, `png' for HTML documentation) + residing in the `doc' subdirectory. To see the figures + included in the documentation correctly, make sure that + the program used for viewing the documentation can find + these files when needed. + + The documentation is also available in various formats + at the program's home page at + . diff --git a/lbtt/configure.ac b/lbtt/configure.ac new file mode 100644 index 000000000..57a31b35f --- /dev/null +++ b/lbtt/configure.ac @@ -0,0 +1,190 @@ +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.53]) +AC_INIT([lbtt], [1.0.1], [heikki.tauriainen@hut.fi]) +AC_REVISION([Revision: 1.1]) +AC_CONFIG_SRCDIR([src/main.cc]) +AM_INIT_AUTOMAKE +AM_CONFIG_HEADER([config.h]) + + + +# Checks for programs. + +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_CXXCPP +AM_PROG_LEX +AC_PROG_YACC + +YACC="${YACC} -d" + + + +# Check whether the user has explicitly disabled the test for the availability +# of the GNU readline library. + +readline=yes + +AC_ARG_WITH([readline], + [AC_HELP_STRING([--without-readline], + [disable check for the GNU readline library])], + [if test x"${withval}" = xno; then readline=no; fi]) + + + +# Check for the availability of headers. + +AC_HEADER_STDC +AC_CHECK_HEADERS([fcntl.h obstack.h stdlib.h unistd.h]) + +# Check for the availability of the GNU readline headers. + +if test "${readline}" = yes; then + rl_history_h="readline/history.h" + rl_readline_h="readline/readline.h" + AC_CHECK_HEADERS([${rl_history_h} ${rl_readline_h}], + [], + [readline=no]) +fi + +AC_LANG([C++]) + +# Check for the availablility of the sstream or strstream header. + +AC_CHECK_HEADERS([sstream], + [], + [AC_CHECK_HEADERS([strstream], + [], + [AC_MSG_ERROR([missing one or more standard C++ headers])])]) + + + +# Checks for typedefs, structures, and compiler characteristics. + +# Check for the availability of the slist header (an extension to the C++ +# Standard Template Library). (In GCC 3.x the header is in the ext/ +# subdirectory of the directory containing the standard C++ headers.) + +AC_MSG_CHECKING([for slist]) +for slist_header in slist ext/slist no; do + if test "${slist_header}" != no; then + AC_TRY_CPP([#include <${slist_header}>], [break]) + fi +done + +# Try to determine the C++ namespace in which the class slist resides. +# (For example, GCC versions 3.1, 3.1.1 and 3.2 put slist into the +# __gnu_cxx namespace.) + +if test "${slist_header}" != no; then + for slist_namespace in std __gnu_cxx error; do + if test "${slist_namespace}" != error; then + AC_TRY_LINK([#include <${slist_header}>], + [${slist_namespace}::slist s;], + [break]) + fi + done + if test "${slist_namespace}" != error; then + AC_MSG_RESULT([header <${slist_header}>, typename ${slist_namespace}::slist]) + AC_DEFINE([HAVE_SLIST], + [1], + [Define to 1 if you have the or header file.]) + AC_DEFINE_UNQUOTED([SLIST_NAMESPACE], + [${slist_namespace}], + [Define as the name of the C++ namespace containing slist.]) + AC_SUBST([INCLUDE_SLIST_HEADER], ["#include <${slist_header}>"]) + else + slist_header=no + fi +fi + +if test "${slist_header}" = no; then + AC_MSG_RESULT([no]) + +# Check for the availability of the single_client_alloc memory allocator +# available in some compilers supporting the SGI extensions to the Standard +# template library (for example, pre-3.0 versions of gcc). This check is not +# needed if no suitable slist header was found above, since in that case the +# compiler does not support the SGI extensions. + +else + AC_MSG_CHECKING([for single_client_alloc]) + AC_TRY_LINK([#include <${slist_header}>], + [using namespace std; + ${slist_namespace}::slist s;], + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_SINGLE_CLIENT_ALLOC], + [1], + [Define if your C++ compiler supports the single_client_allocator memory allocator.])], + [AC_MSG_RESULT([no])]) +fi + +AC_LANG(C) + +AC_C_CONST +AC_C_INLINE + + + +# Checks for library functions. + +AC_TYPE_SIGNAL +AC_CHECK_FUNCS([mkdir strchr strtod strtol strtoul getopt_long]) +if test x"${ac_cv_func_getopt_long}" = xno; then + AC_LIBOBJ([getopt]) + AC_LIBOBJ([getopt1]) + AC_CHECK_HEADERS([libintl.h string.h strings.h]) + AC_CHECK_FUNCS([memset]) +fi + +# Determine which libraries to link with for readline support. + +if test "${readline}" = yes; then + AC_MSG_CHECKING([for readline libraries]) + oldlibs=${LIBS} + for READLINELIBS in "-lreadline" "-lreadline -lcurses" "-lreadline -ltermcap" error; do + if test "${READLINELIBS}" != error; then + LIBS="${oldlibs} ${READLINELIBS}" + AC_TRY_LINK([#include <${rl_history_h}> + #include <${rl_readline_h}>], + [using_history(); readline(""); add_history("");], + [break]) + fi + done + LIBS=${oldlibs} + if test "${READLINELIBS}" != error; then + AC_DEFINE([HAVE_READLINE], + [1], + [Define if you have the GNU readline library.]) + AC_SUBST([READLINELIBS]) + AC_MSG_RESULT([${READLINELIBS}]) + else + AC_MSG_RESULT([no suitable libraries found, readline support disabled]) + READLINELIBS="" + fi +fi + +# Check for the availability of the `rand48' family of random number +# generation functions. + +have_rand48=yes + +AC_CHECK_FUNCS([srand48 lrand48 seed48], + [], + [have_rand48=no + AC_CHECK_FUNCS([rand srand], + [], + [AC_MSG_ERROR([missing library functions for random number generation])])]) + +if test "${have_rand48}" = yes; then + AC_DEFINE([HAVE_RAND48], + [1], + [Define if you have the `rand48' family of random number generation functions.]) +fi + + + +AC_CONFIG_FILES([Makefile doc/Makefile src/Makefile src/Graph.h]) +AC_OUTPUT diff --git a/lbtt/doc/Makefile.am b/lbtt/doc/Makefile.am new file mode 100644 index 000000000..8c515df49 --- /dev/null +++ b/lbtt/doc/Makefile.am @@ -0,0 +1,3 @@ +info_TEXINFOS = lbtt.texi +lbtt_TEXINFOS = gpl.texi +EXTRA_DIST = intersectioncheck.txt intersectioncheck.eps intersectioncheck.png testprocedure.txt testprocedure.eps testprocedure.png diff --git a/lbtt/doc/gpl.texi b/lbtt/doc/gpl.texi new file mode 100644 index 000000000..e1c0f8b96 --- /dev/null +++ b/lbtt/doc/gpl.texi @@ -0,0 +1,392 @@ +@unnumbered GNU GENERAL PUBLIC LICENSE +@center Version 2, June 1991 + +@c This file is intended to be included in another file. + +@display +Copyright @copyright{} 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@unnumberedsec Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software---to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + +@iftex +@unnumberedsec TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end iftex +@ifinfo +@center TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +@end ifinfo + +@enumerate 0 +@item +This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The ``Program'', below, +refers to any such program or work, and a ``work based on the Program'' +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term ``modification''.) Each licensee is addressed as ``you''. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +@item +You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +@item +You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +@enumerate a +@item +You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + +@item +You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any +part thereof, to be licensed as a whole at no charge to all third +parties under the terms of this License. + +@item +If the modified program normally reads commands interactively +when run, you must cause it, when started running for such +interactive use in the most ordinary way, to print or display an +announcement including an appropriate copyright notice and a +notice that there is no warranty (or else, saying that you provide +a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this +License. (Exception: if the Program itself is interactive but +does not normally print such an announcement, your work based on +the Program is not required to print an announcement.) +@end enumerate + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +@item +You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + +@enumerate a +@item +Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections +1 and 2 above on a medium customarily used for software interchange; or, + +@item +Accompany it with a written offer, valid for at least three +years, to give any third party, for a charge no more than your +cost of physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +@item +Accompany it with the information you received as to the offer +to distribute corresponding source code. (This alternative is +allowed only for noncommercial distribution and only if you +received the program in object code or executable form with such +an offer, in accord with Subsection b above.) +@end enumerate + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +@item +You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +@item +Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +@item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +@item +If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +@item +The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and ``any +later version'', you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +@item +If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + +@iftex +@heading NO WARRANTY +@end iftex +@ifinfo +@center NO WARRANTY +@end ifinfo + +@item +BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM ``AS IS'' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +@item +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS +@end ifinfo + +@page +@unnumberedsec Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the ``copyright'' line and a pointer to where the full notice is found. + +@smallexample +@var{one line to give the program's name and a brief idea of what it does.} +Copyright (C) @var{yyyy} @var{name of author} + +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 2 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, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. +@end smallexample + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +@smallexample +Gnomovision version 69, Copyright (C) 19@var{yy} @var{name of author} +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +This is free software, and you are welcome to redistribute it +under certain conditions; type `show c' for details. +@end smallexample + +The hypothetical commands @samp{show w} and @samp{show c} should show +the appropriate parts of the General Public License. Of course, the +commands you use may be called something other than @samp{show w} and +@samp{show c}; they could even be mouse-clicks or menu items---whatever +suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the program, if +necessary. Here is a sample; alter the names: + +@smallexample +Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + +@var{signature of Ty Coon}, 1 April 1989 +Ty Coon, President of Vice +@end smallexample + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/lbtt/doc/intersectioncheck.eps b/lbtt/doc/intersectioncheck.eps new file mode 100644 index 000000000..9cb22e5bf --- /dev/null +++ b/lbtt/doc/intersectioncheck.eps @@ -0,0 +1,747 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%Title: intersectioncheck.eps +%%Creator: fig2dev Version 3.2 Patchlevel 3a +%%CreationDate: Mon Aug 6 18:07:57 2001 +%%For: htauriai@lattice (Heikki Tauriainen,TB349,451 3263,) +%%BoundingBox: 0 0 333 459 +%%Magnification: 1.0000 +%%EndComments +/$F2psDict 200 dict def +$F2psDict begin +$F2psDict /mtrx matrix put +/col-1 {0 setgray} bind def +/col0 {0.000 0.000 0.000 srgb} bind def +/col1 {0.000 0.000 1.000 srgb} bind def +/col2 {0.000 1.000 0.000 srgb} bind def +/col3 {0.000 1.000 1.000 srgb} bind def +/col4 {1.000 0.000 0.000 srgb} bind def +/col5 {1.000 0.000 1.000 srgb} bind def +/col6 {1.000 1.000 0.000 srgb} bind def +/col7 {1.000 1.000 1.000 srgb} bind def +/col8 {0.000 0.000 0.560 srgb} bind def +/col9 {0.000 0.000 0.690 srgb} bind def +/col10 {0.000 0.000 0.820 srgb} bind def +/col11 {0.530 0.810 1.000 srgb} bind def +/col12 {0.000 0.560 0.000 srgb} bind def +/col13 {0.000 0.690 0.000 srgb} bind def +/col14 {0.000 0.820 0.000 srgb} bind def +/col15 {0.000 0.560 0.560 srgb} bind def +/col16 {0.000 0.690 0.690 srgb} bind def +/col17 {0.000 0.820 0.820 srgb} bind def +/col18 {0.560 0.000 0.000 srgb} bind def +/col19 {0.690 0.000 0.000 srgb} bind def +/col20 {0.820 0.000 0.000 srgb} bind def +/col21 {0.560 0.000 0.560 srgb} bind def +/col22 {0.690 0.000 0.690 srgb} bind def +/col23 {0.820 0.000 0.820 srgb} bind def +/col24 {0.500 0.190 0.000 srgb} bind def +/col25 {0.630 0.250 0.000 srgb} bind def +/col26 {0.750 0.380 0.000 srgb} bind def +/col27 {1.000 0.500 0.500 srgb} bind def +/col28 {1.000 0.630 0.630 srgb} bind def +/col29 {1.000 0.750 0.750 srgb} bind def +/col30 {1.000 0.880 0.880 srgb} bind def +/col31 {1.000 0.840 0.000 srgb} bind def + +end +save +newpath 0 459 moveto 0 0 lineto 333 0 lineto 333 459 lineto closepath clip newpath +-32.0 585.0 translate +1 -1 scale + +/cp {closepath} bind def +/ef {eofill} bind def +/gr {grestore} bind def +/gs {gsave} bind def +/sa {save} bind def +/rs {restore} bind def +/l {lineto} bind def +/m {moveto} bind def +/rm {rmoveto} bind def +/n {newpath} bind def +/s {stroke} bind def +/sh {show} bind def +/slc {setlinecap} bind def +/slj {setlinejoin} bind def +/slw {setlinewidth} bind def +/srgb {setrgbcolor} bind def +/rot {rotate} bind def +/sc {scale} bind def +/sd {setdash} bind def +/ff {findfont} bind def +/sf {setfont} bind def +/scf {scalefont} bind def +/sw {stringwidth} bind def +/tr {translate} bind def +/tnt {dup dup currentrgbcolor + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} + bind def +/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul + 4 -2 roll mul srgb} bind def +/reencdict 12 dict def /ReEncode { reencdict begin +/newcodesandnames exch def /newfontname exch def /basefontname exch def +/basefontdict basefontname findfont def /newfont basefontdict maxlength dict def +basefontdict { exch dup /FID ne { dup /Encoding eq +{ exch dup length array copy newfont 3 1 roll put } +{ exch newfont 3 1 roll put } ifelse } { pop pop } ifelse } forall +newfont /FontName newfontname put newcodesandnames aload pop +128 1 255 { newfont /Encoding get exch /.notdef put } for +newcodesandnames length 2 idiv { newfont /Encoding get 3 1 roll put } repeat +newfontname newfont definefont pop end } def +/isovec [ +8#055 /minus 8#200 /grave 8#201 /acute 8#202 /circumflex 8#203 /tilde +8#204 /macron 8#205 /breve 8#206 /dotaccent 8#207 /dieresis +8#210 /ring 8#211 /cedilla 8#212 /hungarumlaut 8#213 /ogonek 8#214 /caron +8#220 /dotlessi 8#230 /oe 8#231 /OE +8#240 /space 8#241 /exclamdown 8#242 /cent 8#243 /sterling +8#244 /currency 8#245 /yen 8#246 /brokenbar 8#247 /section 8#250 /dieresis +8#251 /copyright 8#252 /ordfeminine 8#253 /guillemotleft 8#254 /logicalnot +8#255 /hyphen 8#256 /registered 8#257 /macron 8#260 /degree 8#261 /plusminus +8#262 /twosuperior 8#263 /threesuperior 8#264 /acute 8#265 /mu 8#266 /paragraph +8#267 /periodcentered 8#270 /cedilla 8#271 /onesuperior 8#272 /ordmasculine +8#273 /guillemotright 8#274 /onequarter 8#275 /onehalf +8#276 /threequarters 8#277 /questiondown 8#300 /Agrave 8#301 /Aacute +8#302 /Acircumflex 8#303 /Atilde 8#304 /Adieresis 8#305 /Aring +8#306 /AE 8#307 /Ccedilla 8#310 /Egrave 8#311 /Eacute +8#312 /Ecircumflex 8#313 /Edieresis 8#314 /Igrave 8#315 /Iacute +8#316 /Icircumflex 8#317 /Idieresis 8#320 /Eth 8#321 /Ntilde 8#322 /Ograve +8#323 /Oacute 8#324 /Ocircumflex 8#325 /Otilde 8#326 /Odieresis 8#327 /multiply +8#330 /Oslash 8#331 /Ugrave 8#332 /Uacute 8#333 /Ucircumflex +8#334 /Udieresis 8#335 /Yacute 8#336 /Thorn 8#337 /germandbls 8#340 /agrave +8#341 /aacute 8#342 /acircumflex 8#343 /atilde 8#344 /adieresis 8#345 /aring +8#346 /ae 8#347 /ccedilla 8#350 /egrave 8#351 /eacute +8#352 /ecircumflex 8#353 /edieresis 8#354 /igrave 8#355 /iacute +8#356 /icircumflex 8#357 /idieresis 8#360 /eth 8#361 /ntilde 8#362 /ograve +8#363 /oacute 8#364 /ocircumflex 8#365 /otilde 8#366 /odieresis 8#367 /divide +8#370 /oslash 8#371 /ugrave 8#372 /uacute 8#373 /ucircumflex +8#374 /udieresis 8#375 /yacute 8#376 /thorn 8#377 /ydieresis] def +/Times-Roman /Times-Roman-iso isovec ReEncode +/Times-Italic /Times-Italic-iso isovec ReEncode + /DrawEllipse { + /endangle exch def + /startangle exch def + /yrad exch def + /xrad exch def + /y exch def + /x exch def + /savematrix mtrx currentmatrix def + x y tr xrad yrad sc 0 0 1 startangle endangle arc + closepath + savematrix setmatrix + } def + +/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def +/$F2psEnd {$F2psEnteredState restore end} def + +$F2psBegin +%%Page: 1 1 +10 setmiterlimit + 0.06299 0.06299 sc +/Times-Roman-iso ff 180.00 scf sf +2925 3150 m +gs 1 -1 sc (LTL-to-B\374chi) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +2925 3375 m +gs 1 -1 sc (translator 2) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +15.000 slw +n 2295 2970 m 3555 2970 l 3555 3465 l 2295 3465 l + cp gs col0 s gr +7.500 slw +% Ellipse +n 3847 3195 23 23 0 360 DrawEllipse gs 0.00 setgray ef gr gs col0 s gr + +% Ellipse +n 4027 3195 23 23 0 360 DrawEllipse gs 0.00 setgray ef gr gs col0 s gr + +% Ellipse +n 4207 3195 23 23 0 360 DrawEllipse gs 0.00 setgray ef gr gs col0 s gr + +/Times-Italic-iso ff 180.00 scf sf +2340 2227 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1305 2227 m +gs 1 -1 sc (LTL formula) col0 sh gr +% Polyline +n 4830 2154 m 4867 2154 l + 4867 2184 l gs col0 s gr +/Times-Italic-iso ff 180.00 scf sf +4905 2227 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3150 2227 m +gs 1 -1 sc (Negated LTL formula) col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +2700 5265 m +gs 1 -1 sc (f) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +2385 5265 m +gs 1 -1 sc (2 for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +2475 5040 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +900 4500 m +gs 1 -1 sc (1 for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +1215 4500 m +gs 1 -1 sc (f) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +990 4275 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 2287 4423 m 2324 4423 l + 2324 4453 l gs col0 s gr +/Times-Italic-iso ff 180.00 scf sf +2362 4496 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +2025 4499 m +gs 1 -1 sc (1 for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +2160 4274 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +4334 4497 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4117 4499 m +gs 1 -1 sc (for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +3892 4499 m +gs 1 -1 sc (n) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4140 4274 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 5452 4424 m 5489 4424 l + 5489 4454 l gs col0 s gr +/Times-Italic-iso ff 180.00 scf sf +5527 4497 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +5265 4499 m +gs 1 -1 sc (for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +5040 4499 m +gs 1 -1 sc (n) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +5310 4274 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +5512 3375 m +gs 1 -1 sc (n) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4702 3375 m +gs 1 -1 sc (translator) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +5130 3150 m +gs 1 -1 sc (LTL-to-B\374chi) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +15.000 slw +n 4500 2970 m 5760 2970 l 5760 3465 l 4500 3465 l + cp gs col0 s gr +% Polyline +7.500 slw +n 3950 5189 m 3987 5189 l + 3987 5219 l gs col0 s gr +/Times-Italic-iso ff 180.00 scf sf +4025 5262 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3690 5264 m +gs 1 -1 sc (2 for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3825 5039 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1170 3375 m +gs 1 -1 sc (translator 1) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1170 3150 m +gs 1 -1 sc (LTL-to-B\374chi) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +15.000 slw +n 540 2970 m 1800 2970 l 1800 3465 l 540 3465 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +1575 5940 m +gs 1 -1 sc (Intersection) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1575 6165 m +gs 1 -1 sc (emptiness) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1575 6390 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 1080 5760 m 2070 5760 l 2070 6480 l 1080 6480 l + cp gs col0 s gr +% Polyline +n 1035 5715 m 2115 5715 l 2115 6525 l 1035 6525 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +4725 5940 m +gs 1 -1 sc (Intersection) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4725 6165 m +gs 1 -1 sc (emptiness) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4725 6390 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 4230 5760 m 5220 5760 l 5220 6480 l 4230 6480 l + cp gs col0 s gr +% Polyline +n 4185 5715 m 5265 5715 l 5265 6525 l 4185 6525 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +1575 7200 m +gs 1 -1 sc (Intersection) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1575 7425 m +gs 1 -1 sc (emptiness) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1575 7650 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 1080 7020 m 2070 7020 l 2070 7740 l 1080 7740 l + cp gs col0 s gr +% Polyline +n 1035 6975 m 2115 6975 l 2115 7785 l 1035 7785 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +4725 7200 m +gs 1 -1 sc (Intersection) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4725 7425 m +gs 1 -1 sc (emptiness) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4725 7650 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 4230 7020 m 5220 7020 l 5220 7740 l 4230 7740 l + cp gs col0 s gr +% Polyline +n 4185 6975 m 5265 6975 l 5265 7785 l 4185 7785 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +1575 8370 m +gs 1 -1 sc (Intersection) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1575 8595 m +gs 1 -1 sc (emptiness) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1575 8820 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 1080 8190 m 2070 8190 l 2070 8910 l 1080 8910 l + cp gs col0 s gr +% Polyline +n 1035 8145 m 2115 8145 l 2115 8955 l 1035 8955 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +4725 8370 m +gs 1 -1 sc (Intersection) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4725 8595 m +gs 1 -1 sc (emptiness) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4725 8820 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 4230 8190 m 5220 8190 l 5220 8910 l 4230 8910 l + cp gs col0 s gr +% Polyline +n 4185 8145 m 5265 8145 l 5265 8955 l 4185 8955 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +3150 7200 m +gs 1 -1 sc (Intersection) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3150 7425 m +gs 1 -1 sc (emptiness) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3150 7650 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 2655 7020 m 3645 7020 l 3645 7740 l 2655 7740 l + cp gs col0 s gr +% Polyline +n 2610 6975 m 3690 6975 l 3690 7785 l 2610 7785 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +3150 5940 m +gs 1 -1 sc (Intersection) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3150 6165 m +gs 1 -1 sc (emptiness) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3150 6390 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 2655 5760 m 3645 5760 l 3645 6480 l 2655 6480 l + cp gs col0 s gr +% Polyline +n 2610 5715 m 3690 5715 l 3690 6525 l 2610 6525 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +3150 8460 m +gs 1 -1 sc (Intersection) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3150 8685 m +gs 1 -1 sc (emptiness) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3150 8910 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 2655 8280 m 3645 8280 l 3645 9000 l 2655 9000 l + cp gs col0 s gr +% Polyline +n 2610 8235 m 3690 8235 l 3690 9045 l 2610 9045 l + cp gs col0 s gr +% Polyline +7.500 slw +gs clippath +1001 2966 m 1052 2998 l 1134 2871 l 1044 2956 l 1083 2838 l cp +eoclip +n 1440 2340 m + 1035 2970 l gs col0 s gr gr + +% arrowhead +n 1083 2838 m 1044 2956 l 1134 2871 l 1083 2838 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4505 3003 m 4523 2945 l 4379 2900 l 4485 2965 l 4361 2957 l cp +eoclip +n 2475 2340 m + 4500 2970 l gs col0 s gr gr + +% arrowhead +n 4361 2957 m 4485 2965 l 4379 2900 l 4361 2957 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2689 3001 m 2731 2959 l 2625 2852 l 2689 2959 l 2582 2895 l cp +eoclip +n 2070 2340 m + 2700 2970 l gs col0 s gr gr + +% arrowhead +n 2582 2895 m 2689 2959 l 2625 2852 l 2582 2895 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1773 2949 m 1799 3003 l 1936 2937 l 1815 2963 l 1910 2883 l cp +eoclip +n 3105 2340 m + 1800 2970 l gs col0 s gr gr + +% arrowhead +n 1910 2883 m 1815 2963 l 1936 2937 l 1910 2883 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3117 2963 m 3164 3000 l 3257 2881 l 3160 2957 l 3210 2844 l cp +eoclip +n 3645 2340 m + 3150 2970 l gs col0 s gr gr + +% arrowhead +n 3210 2844 m 3160 2957 l 3257 2881 l 3210 2844 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4841 2997 m 4893 2968 l 4818 2836 l 4852 2956 l 4766 2866 l cp +eoclip +n 4500 2340 m + 4860 2970 l gs col0 s gr gr + +% arrowhead +n 4766 2866 m 4852 2956 l 4818 2836 l 4766 2866 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +960 4110 m 1020 4110 l 1020 3959 l 990 4079 l 960 3959 l cp +eoclip +n 990 3465 m + 990 4095 l gs col0 s gr gr + +% arrowhead +n 960 3959 m 990 4079 l 1020 3959 l 960 3959 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2146 4125 m 2192 4086 l 2093 3972 l 2149 4083 l 2048 4011 l cp +eoclip +n 1620 3465 m + 2160 4095 l gs col0 s gr gr + +% arrowhead +n 2048 4011 m 2149 4083 l 2093 3972 l 2048 4011 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +5280 4110 m 5340 4110 l 5340 3959 l 5310 4079 l 5280 3959 l cp +eoclip +n 5310 3465 m + 5310 4095 l gs col0 s gr gr + +% arrowhead +n 5280 3959 m 5310 4079 l 5340 3959 l 5280 3959 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4107 4086 m 4153 4125 l 4251 4011 l 4151 4083 l 4206 3972 l cp +eoclip +n 4680 3465 m + 4140 4095 l gs col0 s gr gr + +% arrowhead +n 4206 3972 m 4151 4083 l 4251 4011 l 4206 3972 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2760 4875 m 2820 4875 l 2820 4724 l 2790 4844 l 2760 4724 l cp +eoclip +n 2790 3465 m + 2790 4860 l gs col0 s gr gr + +% arrowhead +n 2760 4724 m 2790 4844 l 2820 4724 l 2760 4724 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3482 4879 m 3542 4870 l 3517 4720 l 3507 4844 l 3458 4730 l cp +eoclip +n 3285 3465 m + 3510 4860 l gs col0 s gr gr + +% arrowhead +n 3458 4730 m 3507 4844 l 3517 4720 l 3458 4730 l cp gs 0.00 setgray ef gr col0 s +% Polyline +15.000 slw + [15 45] 45 sd +n 1260 2025 m 2475 2025 l 2475 2340 l 1260 2340 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 3105 2025 m 5040 2025 l 5040 2340 l 3105 2340 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 540 4095 m 1440 4095 l 1440 4590 l 540 4590 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 1710 4095 m 2610 4095 l 2610 4590 l 1710 4590 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 3690 4095 m 4590 4095 l 4590 4590 l 3690 4590 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 4860 4095 m 5760 4095 l 5760 4590 l 4860 4590 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 2025 4860 m 2925 4860 l 2925 5355 l 2025 5355 l + cp gs col0 s gr [] 0 sd +% Polyline +7.500 slw + [15 45] 45 sd +n 3375 4860 m 4275 4860 l 4275 5355 l 3375 5355 l + cp gs col0 s gr [] 0 sd +% Polyline +n 675 4590 m + 675 9270 l gs col0 s gr +% Polyline +gs clippath +1050 7410 m 1050 7350 l 899 7350 l 1019 7380 l 899 7410 l cp +eoclip +n 675 7380 m + 1035 7380 l gs col0 s gr gr + +% arrowhead +n 899 7410 m 1019 7380 l 899 7350 l 899 7410 l cp gs 0.00 setgray ef gr col0 s +% Polyline +n 1710 4590 m 1035 5355 l 855 5400 l + 855 6750 l gs col0 s gr +% Polyline +n 1035 5355 m + 1215 5400 l gs col0 s gr +% Polyline +gs clippath +1185 5730 m 1245 5730 l 1245 5579 l 1215 5699 l 1185 5579 l cp +eoclip +n 1215 5400 m + 1215 5715 l gs col0 s gr gr + +% arrowhead +n 1185 5579 m 1215 5699 l 1245 5579 l 1185 5579 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1901 5711 m 1952 5743 l 2033 5615 l 1944 5701 l 1982 5583 l cp +eoclip +n 2160 5355 m + 1935 5715 l gs col0 s gr gr + +% arrowhead +n 1982 5583 m 1944 5701 l 2033 5615 l 1982 5583 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4347 5743 m 4398 5711 l 4317 5583 l 4356 5701 l 4266 5615 l cp +eoclip +n 4140 5355 m + 4365 5715 l gs col0 s gr gr + +% arrowhead +n 4266 5615 m 4356 5701 l 4317 5583 l 4266 5615 l cp gs 0.00 setgray ef gr col0 s +% Polyline +n 4590 4590 m 5265 5355 l 5445 5400 l + 5445 6750 l gs col0 s gr +% Polyline +gs clippath +5055 5730 m 5115 5730 l 5115 5579 l 5085 5699 l 5055 5579 l cp +eoclip +n 5085 5400 m + 5085 5715 l gs col0 s gr gr + +% arrowhead +n 5055 5579 m 5085 5699 l 5115 5579 l 5055 5579 l cp gs 0.00 setgray ef gr col0 s +% Polyline +n 5265 5355 m + 5085 5400 l gs col0 s gr +% Polyline +gs clippath +2809 5736 m 2867 5722 l 2831 5575 l 2831 5699 l 2772 5589 l cp +eoclip +n 2745 5355 m + 2835 5715 l gs col0 s gr gr + +% arrowhead +n 2772 5589 m 2831 5699 l 2831 5575 l 2772 5589 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3432 5722 m 3490 5736 l 3527 5589 l 3469 5699 l 3469 5575 l cp +eoclip +n 3555 5355 m + 3465 5715 l gs col0 s gr gr + +% arrowhead +n 3469 5575 m 3469 5699 l 3527 5589 l 3469 5575 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2096 8117 m 2103 8176 l 2253 8157 l 2131 8143 l 2246 8098 l cp +eoclip +n 3915 5355 m 3915 7920 l + 2115 8145 l gs col0 s gr gr + +% arrowhead +n 2246 8098 m 2131 8143 l 2253 8157 l 2246 8098 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2641 9055 m 2599 9013 l 2492 9119 l 2599 9056 l 2535 9162 l cp +eoclip +n 675 9270 m 2385 9270 l + 2610 9045 l gs col0 s gr gr + +% arrowhead +n 2535 9162 m 2599 9056 l 2492 9119 l 2535 9162 l cp gs 0.00 setgray ef gr col0 s +% Polyline +n 5625 4590 m + 5625 9270 l gs col0 s gr +% Polyline +gs clippath +5250 7350 m 5250 7410 l 5401 7410 l 5281 7380 l 5401 7350 l cp +eoclip +n 5625 7380 m + 5265 7380 l gs col0 s gr gr + +% arrowhead +n 5401 7350 m 5281 7380 l 5401 7410 l 5401 7350 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3700 9013 m 3658 9055 l 3764 9162 l 3701 9056 l 3807 9119 l cp +eoclip +n 5625 9270 m 3915 9270 l + 3690 9045 l gs col0 s gr gr + +% arrowhead +n 3807 9119 m 3701 9056 l 3764 9162 l 3807 9119 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1020 7005 m 1067 6967 l 973 6849 l 1025 6962 l 926 6887 l cp +eoclip +n 855 6750 m + 1035 6975 l gs col0 s gr gr + +% arrowhead +n 926 6887 m 1025 6962 l 973 6849 l 926 6887 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +5232 6967 m 5279 7005 l 5373 6887 l 5275 6962 l 5326 6849 l cp +eoclip +n 5445 6750 m + 5265 6975 l gs col0 s gr gr + +% arrowhead +n 5326 6849 m 5275 6962 l 5373 6887 l 5326 6849 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1050 8580 m 1050 8520 l 899 8520 l 1019 8550 l 899 8580 l cp +eoclip +n 675 8550 m + 1035 8550 l gs col0 s gr gr + +% arrowhead +n 899 8580 m 1019 8550 l 899 8520 l 899 8580 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +5250 8520 m 5250 8580 l 5401 8580 l 5281 8550 l 5401 8520 l cp +eoclip +n 5625 8550 m + 5265 8550 l gs col0 s gr gr + +% arrowhead +n 5401 8520 m 5281 8550 l 5401 8580 l 5401 8520 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4196 8176 m 4203 8117 l 4053 8098 l 4169 8143 l 4046 8157 l cp +eoclip +n 2385 5355 m 2385 7920 l + 4185 8145 l gs col0 s gr gr + +% arrowhead +n 4046 8157 m 4169 8143 l 4053 8098 l 4046 8157 l cp gs 0.00 setgray ef gr col0 s +% Polyline +n 1665 6599 m 4748 6599 l 4748 6591 l 1665 6591 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 1665 6644 m 4726 6644 l 4726 6629 l 1665 6629 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 1665 6765 m 4731 6765 l 4731 6810 l 1665 6810 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 1665 6877 m 4748 6877 l 4748 6885 l 1665 6885 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 1665 6832 m 4726 6832 l 4726 6847 l 1665 6847 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 1670 6763 m 4731 6763 l 4731 6666 l 1670 6666 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +gs clippath +3523 6964 m 3565 7006 l 3672 6900 l 3566 6964 l 3629 6857 l cp +eoclip +n 5445 6750 m 3780 6750 l + 3555 6975 l gs col0 s gr gr + +% arrowhead +n 3629 6857 m 3566 6964 l 3672 6900 l 3629 6857 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2734 7006 m 2776 6964 l 2670 6857 l 2734 6964 l 2627 6900 l cp +eoclip +n 855 6750 m 2520 6750 l + 2745 6975 l gs col0 s gr gr + +% arrowhead +n 2627 6900 m 2734 6964 l 2670 6857 l 2627 6900 l cp gs 0.00 setgray ef gr col0 s +$F2psEnd +rs diff --git a/lbtt/doc/intersectioncheck.png b/lbtt/doc/intersectioncheck.png new file mode 100644 index 0000000000000000000000000000000000000000..596e95c4b26d594cc2308bfdef7fe80a7e860d88 GIT binary patch literal 14956 zcmeAS@N?(olHy`uVBq!ia0y~yVA5n@VEV(s#K6F?ifPkT1_lPs0*}aI1_mBq5N5n| zZmk~!1A}adYeY$Kep*R+Vo@rCyI-h+dq#e7NpW&fWr*aGpf-=R-3>nGym{n!gGU*+CsrE#Up<&yF<9AN| z?S8fXFw6b--#1N|w{(4%)7RrA%eYMZI2N`32-~{u(c8?E{ADN2U-7jkG51{y_WHjj zj?p%C`tQI0KQ;aOy!OrHm-EZlpVHsI+r_lp?%U6rV`3QrVlPUn>u1;3eX7ZxesJl2 zRc_m)*Y65?-U;doR-IDq+Zj9I_xTCeHlMzs;{E+`O-|tQd3&dMPUDH&T;1~fnDDmc zDNJXp+uALQcCYLTP@Jxu-{O_gx##WnfI#;ZcRnBJQJeHmf~#HX)MWOXhI4Xm%@ds@ zD=KKGu;$*4v-e*!T!~HBdGh6Qah-?V=}E5Js$?Sc{`Wq7m|fcL{6;YA?!y^3m0ONh zzE#ug+nMRDe(U-F^K8@iewy58bW{7~^Ue0>T~oZpuj$72F0S5UwVtcDxH2O&`-08Z zRoi=Fqb3&xTzYmTa-ZhacLg1`PHU}YzE>Z4zW&v2_aE~?7I)XA@_al)utE}ik^KiW47yk*XsvZ zGQR|GO$vXv_Ph9v&l|&j+VwrWp!|n(N_9=ip_?mIBmMvWVLSDzQ~qK1q0UAB7Q~#J zS14|qB-FR_Vg0;PW2)*<=5lZrI;- zO4rUhTd_$a+&^KDZx> zPyTtep2hCnovmjl=KsmLT=sjp7*j5nXcF(o9S_!@oOMg-ce%_>z1K3woO0N&CUG5{ zP?5WaZTsm2p<9NpWz?Fp*{3GE@5tsY`15^*MDW}VHS4Y)x_aB>wM@_28|=DUPu7~7 z?Q8tydBW*d^~nk!-347e3HO4<7dA@G`MYG5zs0;Q+kX3%1WjAanUb-lv~btQxM-`I ztVhbPWp*De)O~e|>6xwQ!dc5c-tcZ!?TekU?rP=?o7u;H$+cZ&t!MP>xY{|3eYhlw@N(<{$ZksSMx4g&e znn?LQXY1YBSKk~Dx%M;APS+*6)!y;8$=siYXIB}|D2;lis(5RaT2{30ol5?B7ia#> znNnKU6R*(jSLT7-OZrN`?Z8dY_b+rp&4?}lM^G%mat?+#}uaxDyn)8;c zp(`p`1)tkWBt3X@yztu3$^w_Zhy44FX5Iht{9#3Z>?-ypI zRoMLIoLyR{H-D4Vwlo2k1!{iv^PY#@Wjk-hv~Hqtt6=gfjj7(ov(_$eJ83n&?y857 zQBz^^Jcr|V45rq6=v{O9@fwFZU6$`oDWzp`S5iXHUZ^~s`pIsEXK9bz(D>- z>}r{Hz72(T$61f2tR9Qd~PQ0;NK(|*@HE%_Eb z_7y(j>16oW`pC4;a*zHtoL%!-IPsgX*PNLG{I6vQxG$<|i1oC;xh=#mt&2v?=&)k6(H2%xbf!=YcJr zd#~TxV7w+K=}utUX6p+)c|Z4G)h>uWu;7@@tUrJ2R=fY#<-TDt{l~xMHLpMaT$82c z@_$-xQUAonyM8_Cd$P~T%**~=a$@fVxv*4)TMql@t}Eu2xUQgHk@9jb9}m~=@Ra+L zydUg%v-a}wisQ!;b7B^+j7VNp`C_r-yBELzzB75>srF+A)0Y=7)t;y~|9c|GX080D zTy$0!>$lsTnkG@)=iGLm-O9$mV1B}ufgu;v@G&dmXK;wVUYa+3ztK*c=aGA#-92$v z-^lrz*N?O9kLNf4$W}l8WSP+BxicwbOveV=ZcE0YwwaIV7Eqd%@y zt*+ZO)E0nCwa=YXzRVZpVF89U36PgPA75UTp1J5J0=_M z{>s?WR&@Txs|^7=XEc^I$@1o8+DW}__-yO!*JY=d@>cWar$4t>nQqiCvepbw`1xzW z@mp@zS*&ilwQo-CIcv+EeA$Zod)jgby@ge)boUyZJpS(l&$(NjF&?ic3C}tecTG+1 z`{U^McNMJq4Nj%6Xp{M9!~Jsq79YLmMW(CfPtpIAu{Nb@+lNita{2uKp54AB*;??L zy0%M(`ti`?->&=lNM6cVJzr#2$!dX_e?R(WYYz8&{-dCa%7RP zXzFpv?%w+Q%nUcCEPQ>3LE>WZk@F|SH)=k!W%%dO|LML}=(_)|Z@TX=GhA>3HNTa= zGcar^T+7&Cf9`(l9ox6-L9&(n4PD1G9sYOU5ZuYmu%H*DdfVTha@_Wwk1f86g_%yh z=KG(av2|kZs<+Q=vxCo_UnP1#*L&8f%I~XQtyOFkTd(=tMEm=VH_6G@U&#b~h?r0| z>EH2ZYKz-%-k2F{c<|e2xdS?U$Cz}3&wc+hsi5lqr+D=P{+V>n`T3z_ z^{Ceh>)Dh~@Yue6lEEIk7L*nQZ*iVIHaYC@3z?fO&$Mb*KR4d)W@df(){#p8vYy?0 zViUe9{P_9s)7^8<&p#dCbbbH-%(cgMTOBZ6I9KLv#;UE8opZFdOwKsfcF}kf=h@I7 zVXrF}PTA=@ldI&J7q+Cg_OA`=`Lt;zPrmx`-OHRFeayXgV%w6PY2NbIPI|kq zJvjK~?VW$8_50>ZC#~Imam!6VyO?r;!+R$B)D?s&(88wbihZ4xMJyO7*HF*m_DZbG%(b?@`y*4*po zb+6vn*E-L1YUA4{5?4Yt@o&ET_|l$FOh3?gVPaXM}8zUS4 zFEWlbTI}nZ4_{m#em#9wCbV&y@WUIWlW(8A*!S#i*(I%2IzKgTdG4H7^n2%>0~~LU zObHY%Ew^};xz64F^`zOao0F; znt@?AI8{xoW?;C5T98SDlGEGswQGLvG_|@IvO!jAt>zh!vYva_@1Es-emCcYn9lWW zPfMMrE}tNIN9>tx@kb9U291f;%as2~2THJqU zGuumcgQg>!S$3vNUgF;%)0r+>blz);e1f9Td8a3G41yCYt|@)jId(-|B$<8Zaj{GM2ArM8MT+aQ}p@+r+vC#Tj32xV+og{e^jt%Ok_?JIPWn z*(-XEBunl*Zgq+O14HL=iz5C7y?;Azdf0rMSZ#J7fc1;=$DGDHo7pe%Gx(pdy{+(< zVfu_OZ<_9G7QV!YE;>K$ zMmAfWhs|uI?{B)kFek)6nb$k9PC#bzyxNJ?-`svVMySkNJF$A3`>&fKjn&OZHd`0< zv-c!%v>tiPu#^3OpNEazgz7m7ubMeLZ1N^l|55nMaD#Q{anVcsGRc=7bM8DYed)X# zN0_}(zwr~fc8QA*csh^E7xnuuk>BC?sIc=6^8r1f{_rPq+h_brC|8j?e!_O%#5#r> zU(UZ0{h=(YasL12$Dfbs{|#ur!T0u??P+DL&vO4a-Q+h`44yH2-lnjul#Y^Pers=? zvMqacbY^iw@{DHR%-<>>T@tjvPswOvp4-{Z!?M}K#xdf&twd7_^XX0BJd0(I7Ky2U z{}KN7WJ!L6+wVCCL$r*hSNmM;;}6l)S-nhBYHjtiBLA*$=BK{@*43UI>#e-wimvnV z{sSfN4n~w@Mg*(HM!k$kdp-G%u(ugwQn_?lNcwY$qfvLG&e&dibwWN%LTt%yyDcX( zbe_$Vzx602{j1T}gHb-^NttEUkZTdu3vRoAIJUtY25R_MTjN z@!-{I71zGK{cOv_b!tUH#-hEN+|dus*A-{ae8|W=!$1i*JTiH#6p6%ODAG=l+*yz-{U*4}>XXCGj>*fc|-@ha8 zFNb#Z-k8(ZdS|bypB7yadF6(xp8Ri~upg&gUxfTffBswQkIqe&oqPB4e_!=2ZF9MN zl&D>bMeY70VXgPApN08W{e3dgB--n>f2-;($5!Xbr@y7`+^8AyZj$0UXR;%#b58w@9&Sk=l|CF`sMC%?=^LSGoNyaXZ^k@lpeC9wfWimmAS6< z)xuKIk$rQ&rL8L89+nW-+ArlNud{Lap~}3MQXxNhp2#gc)BJ5&2iN{1#v%+1w<^-U zZI#?@5VE78_82_9fXn2Hv~Q+miBkd}UT~?tTyo9qv)qAc`uykJ7|iF*>}`a^^5R4K zhVK~}G9FuP&yHbR-1GWQ+hQZuk^-8cX94oR-F z*9vyNP=4ajAbRrjb z_2%w=QPFif*C<|rZEA?cuf6icnfd%S>%MK?wsrkRzvMerE-pL0Uj8|E zi|?HA=WR(@yxgiw-dA-^;XiHtBlYyc=KG7TomKA(@t!9VRJSau)^_c;B2E5%8FMB| zt((UaZ(Z}IuFftsCHm50?Z56(Eyu3?`h4a7AGw;-YZSl6?EAE7_VnI@gsrnJo?VmL zu<6V1%kgi1KHMGuB|V5YSx))+`Do6?@&321-)smHw&>li)2Dyny8mnEo(aF6>TA@rR43TVPu~6Wg!5dxrtqivDx1z6 zig=*^=-;*sk*u`N#;fnXS(!hc{dn1-%e;v?2B&?ie{bBcdrs?5`WvyC7Wa(>oF59k zX>nrQTixk-^u>Qqe zZ=Y3V`8(6uqV^bR$;`^gpLOomx3YcgA5tEy-OCURZh)LkPy4?A(~s_(A>lu}`@`!x z%wO**`}{Sz{kz-N?Y*}j6mlgQ7+ZFYj3GwG8MqF{U)mHw#Lg^zz+Qd4OiPc{wR4_dD zu+KVS>*Zm?P@wcZL*XOC?u1v1o9{3))K91rnNYnf`BL+A39he!&o@u3ex~%1VYbp= zAJ=y?JB~2t^nNks-n3WqWSok( znaB1wEb#Nb9jxz4Yq@tmXOj|p_HvJQ?D^wtbr%m=<+Oac{NB^_=$bokx83@b_pj)h zR{HL1OVf_ex>-2){_fpZ=e&QpDLL7z-|?V@(o@dobyWpt-rnl#WH(~k#jW&I^z%G! z6*-1-!>>DC-Z5U=EwfzW#5U(&6DL-yMEh6S&F5F1rOuLVy+odI=kmpNZLF0Of*le16zp!Owy(d#=yEj(<&rKu{eS;gu*QAu`^ntlP0`t6WaN_evazA zbC+N0xo?@?ec}45|0^Ru7mCKt^OD);B5(0=veI{#BOTkOpRYe>e|_Egg%t}IwD!t& zd?|hu)m(2m`-_aMa?v$*ohuEu9P6{w#eenJM@j5lbM;=_j&~2*E6*3+kSLp4`#);0 z)$xNJVFI(Bo=#peOGm{!d&XaxC-ao!9woY1&$;&`dfnt}lACsay%7J|dD;toANwWq zU+h$mdQ|E5LS=Se#CZT>fSM~bqx)^Fpz3BNvn|Mi~TTg2?b z-Ulz$zr3%$ljAY<&ehA;qr*Ewt=+QMteiGs@6^(5@9)ol5OQBDd|7?x)zGac%Y$1E z_`Z0b=W*xG<790Xy>#Zdo%y{2KRW*1UCU8*=B)dMR6YO9o<(Jy260M0ajs`<1%>#( zIW_m_JnNj_v6DR^Xh*j~BG-(%Lr>+@Y#DgErqv}ql@sF6;WB-(KsDqS=alM2s&anI znQwPau3PkEUPdRok0p%=jW6^U3t&R_uqy?yDZ;Lo37KY z7G3hhVDFRPCuTptBfRG>zvbMzu!Qpx;dR-QO2TiscgVY(O3MiPcWBA^!e526tDDs4 zeeVfh@U(RH*@}#y_t!7KK5_ee_OFkX`_dPmvpj!V>c#)6Z#B~wZ&Z)7JG-zZph+s* z{O7){YWtj~=PT~tuOK;{Fa74@vP<@xkN3&;mt~$`Z(#IohSaQi?izy20{<+P z&)<1Gvf|AS(~d26b6lT0E^&L?U!A40+9}xA?0NL!O?}Hf_CC@1yTj`KV@(6Ly(^<1 z8Hq0BJal=dy#3DhBa+je}SlF5xJlkYh5&pCdjPV6~q=m`j_zT<#=foN5(XcGIV~t~@;L{hj1= z-v!Mb`HcoEHwIrVzn=0qIj>Ah)~d|@gu;g{ne(?OW*^Bp`(dBwj6JvB&$CjQ=a-jt@@yN2OwQtF-y7Haw%@*4 zbdPz@5#iJ_#~D_+$NEBK}Tx1{Ue1^K*;%JD=-;(@Nr$*h}Zx zu1nUw54f3|JB-%GOUxYMgCY?D$S{-_2{UL;?;R zkZnJ07;O4oW6r-@FQ#l_{uL9vLx;pmNOj*dIReVaY`YZU&vIKN{w zJA98>M&bLBN2NEC*571olHl5@F0j6n{Xp3XTZT0WuY}oms&lE?+U@99eyPq;8Ez+-Gv!UFp2KY#wQ=(s&Wm4q?>vrN zafCTR`H9?*Cu!4ve*U@R=Cs{2epxL0btcC&v)cB*d@h?n!KKgLTTfqnb!SCJqVN3G zvgx;#dlKx5{Ub6bmtG4>)%)!0Jx9@^^j!qYowDhRWQw_O#5waGXM8N}wc?nJ%al99 zjyLUJZq#e_+G2RSKr*S`@%zl`KSw#Ib5sc!Zk}jwSIl20(f7Ag>~h9!Y4?sh!V7;+ zvu+mLW+hxOSy^F?Uh4^4#@4s%_L<-LevNYzZ-2oJ-McB%3(IF$$GEKyev;cBz9lRD z&a-&|hyJZuekz-{;_kY-D-G)Ziv3r7lgdgKym0f7vP{|Mv-R&fn~xNK3K#vi?Zrlm z(2uu+*57_@`Mshy^4WG~iIn8V=4)E_sc5+f@dmVoZehlE)Zd_M%UVl!D8K06I*TXWkUxwz~ z;%aIBvP88J=7*Z=8ZGqurF>5^@8Q?QVtI+OQU1+hw-SHmIXEAxUGw@u;^DXT_ll4G zoAY+s!E?7{fvje}rek$~IuKt(nodO@{ z6>Rr%4++dx1L>3glnO+EnatmMMN6`0mFS?e9ISTBpZXZqxDqZO3if_d{}b z7-!4v1-e!hD+(r8|J$*Z`RmV`Q?}yktu^Dm`gg2;xNe$2`i~oYvUmD4j^9Z;@vZ)$ z^uxZk4u98oKc^R{1^m1A^XO@zcVhn@On&iQ;nCB`?1e0ca=UKw$(h}GGB4)qlZ78P z&J#K%tFiM#@aqab;s1?QO-lR%k(@2JjjTZ~i+wuJ4YxhZZxAL8ZQ_Sb9J$et; z^e=KRlhmfYSKh3>-+c3O`DXi8|7B0PzZ;0}(_RzzwKOrhKxow#<2!5l;8GIxnTP}F44UIJD=~Ux{&^D-G=pw)0L-x*cb8T_05B8{@si} zm&!SJz3{v5CWr58wG`Zc`e#P;E_1^R<}>5>U))($J~=XFn({xjJBMCRwB-0MxV72( zysiDQ#W%7l_dBz^WM8GfC}n5Sufw`~`ldXcCHPIHsr-Y$@jGUrw)^f_z392SRmuCv zUaorw=RTkJO4Hifac|7tSCPjRmMhKQDDyG>p;+AUYY**`$_t+Fd_E89Wbxlyr zy11=3f97>3NA^{u?`XX~@uv12c3GQ4Kj(eklDOJ7b^Y}EQ^AiHTX@~h3Vj)V&c5~P z?m3;i?UnxY$b9@)Am4lI9cTI075D$WSzExp{;|8|9e3;8J6z@2g@uRS+JBe1E6x`i z*QaUHsw28r-|X2^{l#Cd+)Is#(w9rx@m!Zfch*N@cYFnpKVThj_Fw;bpHv$6k%(kwmmR&zcTR!(f^c)BzniYG?!UTi-p+u1 zs(1dxXuSQjJ3xVb#jkZTo6a#s%OA*@B|E1e-0=NMi}`yCugFvuJhJgyDK1L7f-Q~sWya*q9nTa6(XsDA0a(>&2a{sD^Wnuh!%;)^GMrXSD}21EzS6xrX!du-mF zY!tQ+F=>k9tX;M9`R~7VcJgjAe=bPqC~#S1nz6l2&fEWG@t%MiF3AynN17)WB}WwO zYZqi!mpbgEA~)xYH1l<-1j9?`g;eC`OfzXS-P!(-P4iZ>kr4m1XtVc;I~=>dEoeK^ zd}Y6n^U$zQLg*ftYcG? zTeM;Q`FcL--%4sl=MS#a!wRb*W5z1bxX~Auql70XEbSs{9GOoQQ zoX$I+FIc@GAXn(klqqNU3^yFU!jP@~{m+e+vJ#1_&Pz;Dm0QJapLux0H5Hv_^ORSr zeJd#RvgYSq+_k1zt9k9AjCYnkVPey~kB414e?F4GL~=4eG33Cw?z@EH?Y0-A&tPYz!Z=`YX>T z+JRa<=hRSIJ^ON>*)G=&+7)^GZLH8c^KIPU9gp0VZIuPpeyg1OpPty&EQfM0 zh{@W7>^Ny#f9F=?uNu3j^SZCk6Y*MIQ*QZ4ZD)0(XTOb^YD=CJci=N0*{tNIl1Bf_8aDc_Ws zA1h03+B^R{*g8&R?^wffD0k*ukjvH;`Abz)%B$SYKkM|bdg-lso9@nevwN*j%l1vB z8FRPjOjpg15^%~`CI7m!qJBrTD#!ZiuP3a1mAa#KZE?Qaj;BfKptj(v>mD=14JIrF4mg!yEtibr z%EC2%xb0Z1_{bSrdL=LU?Kr)^=J-2~a*d~Fmv{cC29=51>ciGIwB_iVR=@t9@D0*f z$^3c9_Dt)0uGQhSu{Xr#v}nEMDSv&~-F-*v`%|yWUYE|9aya(y=e!-!`A0dwpQ=2s zZdj`6{&CCWFKhijtvUI5r~lLBBd7e+xU=@E)^Fchb@h5{?m^FWm*y30x0|Eq+R^mk zZZxPd;c0p2X*B<#yH(!CcdQPtKXA%$`$|#8H+fUubN!YovMl0!-|f18p6r=d^Z!1L znRel))x$b}<2!4ewsCy_R5N)~#hKg(YmD#CmUuDy%=-H;V)pJnsbh3nuwFRt;nx!~ zEx%{nIjFm2=BoCc?zz=%IwvZY=Wg9o{Qb!vhp(>UresLHjC=cX+$zVXUw^YzpAtIxVxXxxXjlUD2HJ!qAPJ1O{{|NF%i zZ|4<+cmMkO4IFa)MLYV-ZtW0#ckR)X!+STlM(#P6yCdvx+WOP=Tu)Q-c{R8Fcb!s| z_1;KttvJ8oozG$|VrL8X|M`5AYsZ#c(&w1aSWZREA9%)v*^J@lZ zsR!<4b@}z4<&$qR8D6`=Aei@U24^!D{~B&|1AsbI#veXC-&>{eza> zUCDX%Q{FCHesM|v(b@nh-{PqoAHLI>P@uo${z1-i?w#op4-af?kY~(!b+f@~$M>%b znuYTikKYlSe4hEB%6#A`R zy}VC9_5QACDc+EqHwy0SPAGUCed8c!dE1@I-nWBqPKx?%u{QtYQchN<oqz9ee~Yu?or?k=6%bLk&GOl?oyQ?O*Tg<%h;V+_n11b8RgusKXOL8M|l@rnlO!d{R!I@yMKGv{fzE$G+TK$x`VBO zXD54o$ugOup8WU4LElc^pKroH=Y;K#=@Uv%ZI^IKs;b;?z0A>KV)dfelBN=x3cKfg z54pcI)^(fXyUMahjLe0XB#bX?U2b$Q-TZ{@jOXv(X?+m#<+H#2MDDR=jdhGp!^(GN zDpL0xqL&oUyU)yUVcXd~&$qaGnRoB?mT+gUPhQM-4ir7GQ4x+Z^^Jm7KVol}7J%as zTw=%e?>xBiZJ*qmCg#Wu&7gEMdwykq|L@qM`KyJe8cb+jsUmwv(thW2aX%;S9WLzU z;E|Ev%`dHL_Q(GcHQ4(3l_nwOV!V(e!GKwN5Sk zpb5^nbpsR(Z{@)XfvZ9zG49r>lKJPuBok-@jM; z{--dT{gpZExqVMd)maM~pp`s_dM3l0K0Z6qozLU1I&Ybt`_9H{&fNd&rxk4fz&@Yn zX>i0|H~%-gpH&;)K6uz|{+-FI=h=wZT?=;F$$YhPuh(lE72*Gn-=F`Y=P$@?UuISD z;oe)V?@Jn|e|J2Ut=hkzcg3Dd8+FX?-qoA^V0rDt)UT6fT$_333-i@wKP#+6C%69c zyxzFxrvHo1tNl0MKbXEZ`o^xiJ9k}MaH~C9{P*`imET<-em%E-Po$W$M2&Xzk5usu z_YeN&F1dCv_Nm;1AMXp?w|%bOSNbnnZr^#kpKpIm-TmQL;G2fiuP<0_j-7V%mQ{lC zy1t!^&ri&or+UtA^4`$8O*S@4^_zCBiGCdr8?g1v=}C>c6RIBs{LgW)sGO$Ouv;~x z|3`e?-p?KO#j+L$49!`t?%MGGZtjkjx69hQ``2;@em|Jl{Fm?%nqYkskNUJy+^--_Nr; zb1(JdcU$K4xi9Y>ak%eTF8&VG2I1j2RK)&1e)~$p(Z{n;Do{ZmOSDV*WFM#+bB zjUmS8=dPLa-g44$7rr;g4xOy$)(c->b!}ocZ-e}4**)oZO{{8F)g^8J zd-~Zw*5&Q2x|){0BzU1!P=exV**&}RvaNTt+HCn0#XR?{-}4PuThrE0uMeB;9pzUx zW!u$Nn|{83@1OW}Rkw77BHM$8I|qMPf4}%5=Bee3e|+~g=iPkt#rnsp#3PSN^A2+^ z|NZN~$SKRNkT3gg{1Xq%Dt&N%j=Xf(cj;C4r(S=1?X|_%l4+;&Zd85D3$*3lwVO## z(&|1lPhnb<&ekQ%3(jx1TVJ0O@s5@I{?zMh|Cc6|KasPzQe3e3S9-nWbk&l5epZe? zihVm3`@S^Xa9De}VtdKD+jmpg-gMl#+?uyv{Pf2x*%#~ot=RC@>e!;ZuZ3rL4Hi}{ zo&3RSgJ}_e%*uI_&5g3+*WGS@E!E&VvHHQ~ID4}kRnbYjdmh#B?+N_3ap45ByuUXl z-2bj1Qp7KF>CeW8Py1Wh|E&@%Xim=CdoDJ1Gb>X?PEP%j@^~e9Q^?{zb4bA=))`%Q zg0Jt|zVXAf0^3Oex1N?W@+`l3@a5f5nc~#~@8Wdsf7|i(+S7dI{r_!Gw=d&7v+NE? z^2xkA&);vp-2d+Gv{&mU#6*=YWO#P*{ev&>wEA}b4cs<+p4;VJ_J!%0CcX~sb?nQ_ zw@;{U6XcWO{vpV)p_N^B&OKewn9dz#p$}d6bQQjb+}X>$siywX=}Q?4%sx7ouQ#!_ z*t@&+&S&?ZYoe@^|~ zbf8&&7xNQ2mhzvwznoLny}J4Ap9``X4`)a}lKQRv{T!30%>l=EjPoj|+3&c-`QzaX z=>_faQcU(w;BvKsftJF=6*4&bYL&fzkY|g=TfEU*Qf9AJ>C9eq4k7Y`rj|)Gai5Oz_DN-e8uru zL-TXCOnny*E=~mXptl{|(_Y-)kX-U%2Dm+xYw(`YLXyGaFsN4Tt>pK4oWk%x2sFq9 zUNkR#{7!<%b#aD1aC0j89A^L9;?lEgiNR}2D2#Zaq-f-{v=R}8)33iue za~+R*BDZD=Z<9||+Ua>WeAy0ZJ&{`z$XBB>Pu0UdqUlaEtHgUTtzsKtg%`@-9YV8b zBwyMr^OAkm30nt?lqwzfca5uFtvDXAV*Q%pMGU@AVLulL^=UFU~Ownk)Ig7;KR|{)Rp4gl&M+qnk`G*{x34 zUT}DHlj9{j*9qGheMdISUgA$v{+^&GbbgtK{fw?V&7B@L(aL`lzopr0E05Fn*aa+ literal 0 HcmV?d00001 diff --git a/lbtt/doc/intersectioncheck.txt b/lbtt/doc/intersectioncheck.txt new file mode 100644 index 000000000..405f12dce --- /dev/null +++ b/lbtt/doc/intersectioncheck.txt @@ -0,0 +1,55 @@ + ,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,, + : LTL formula `f' :_____ : Negated LTL formula `!f' : + '''''''T''''''T'''' \ ___'''''''T'''''''''''T'''''''' + | \ ___X / | + | \ ___/ \______ / | + | ___X X_______ | + | / \ / \ | + V V V V V V + :::::::::::::::: :::::::::::::::: :::::::::::::::: + : LTL-to-Buchi : : LTL-to-Buchi : . . . : LTL-to-Buchi : + : translator 1 : : translator 2 : : translator n : + :::::::::::::::: :::::::::::::::: :::::::::::::::: + | | | | / | + | | | | / | + V V | | V V + ,,,,,,,,,,,,, ,,,,,,,,,,,,,, | | ,,,,,,,,,,,,, ,,,,,,,,,,,,,, + : Automaton : : Automaton : | | : Automaton : : Automaton : + : 1 for `f' : : 1 for `!f' : | | : n for `f' : : n for `!f' : + ''''''''''''' ''T''''''''''' | | ''''''''''T'' '''''''''''''' + / ________/ | | \_________ \ + / / V V \ \ + / / ,,,,,,,,,,,,, ,,,,,,,,,,,,,, \ \ +| | : Automaton : : Automaton : | | +| / \ : 2 for `f' : : 2 for `!f' : / \ | +| | | ''T''''T''T'' ''T''T''''T''' | | | +| | | | | | | | | | | | +| | V V | | | | V V | | +| | ################# | \ / | ################# | | +| | # Intersection # | \ / | # Intersection # | | +| | # emptiness # | X | # emptiness # | | +| | # check # ! / \ ! # check # | | +|\ | ################# : / \ : ################# | /| +| \ \___________ _____/ \_____ ___________/ / | +| \ \ / . . \ / / | +| \ / \ | : : | / \ / | +| V V \ | | | | / V V | +| ################# \ / V V \ / ################# | +| # Intersection # X ################# X # Intersection # | +| # emptiness # / \ # Intersection # / \ # emptiness # | +| # check # | | # emptiness # | | # check # | +| ################# | | # check # | | ################# | +|\ | | ################# | | /| +| \ / \ / \ / | +| V V V V V V | +| ################# ################# ################# | +| # Intersection # # Intersection # # Intersection # | +| # emptiness # # emptiness # # emptiness # | +| # check # # check # # check # | +| ################# ################# ################# | + \ / + \ ################# / + \ # Intersection # / + +---------------------># emptiness #<---------------------+ + # check # + ################# diff --git a/lbtt/doc/lbtt.texi b/lbtt/doc/lbtt.texi new file mode 100644 index 000000000..167a02439 --- /dev/null +++ b/lbtt/doc/lbtt.texi @@ -0,0 +1,4771 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename lbtt.info +@settitle @command{lbtt} +@afourpaper +@c %**end of header + +@smallbook + +@ifnottex +@ifhtml +@section @command{lbtt} +@end ifhtml +This file documents how to use the LTL-to-B@"uchi +translator testbench @command{lbtt}. + +Copyright @copyright{} 2001 Heikki Tauriainen +@ifinfo +@email{heikki.tauriainen@@hut.fi} +@end ifinfo +@ifnotinfo +<@email{heikki.tauriainen@@hut.fi}> +@end ifnotinfo + +@ifhtml +@html +
+@end html +@end ifhtml +Permission is granted to make and distribute verbatim +copies of this manual provided the copyright notice and +this permission notice are preserved on all copies. + +@ignore +Permission is granted to process this file through TeX +and print the results, provided the printed document +carries a copying permission notice identical to this +one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified +versions of this manual under the conditions for +verbatim copying, provided also that the section +entitled ``GNU General Public License'' is included exactly +as in the original, and provided that the entire resulting +derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute +translations of this manual into another language, +under the above conditions for modified versions. +@ifhtml +@html +
+@end html +@end ifhtml +@end ifnottex + +@iftex +@titlepage +@title @command{lbtt} + +@subtitle LTL-to-B@"uchi Translator Testbench +@subtitle @today, @command{lbtt} Versions 1.0.x +@author Heikki Tauriainen <@email{heikki.tauriainen@@hut.fi}> +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 2001 Heikki Tauriainen +<@email{heikki.tauriainen@@hut.fi}> + +The latest version of this manual can be obtained from@* +<@url{http://www.tcs.hut.fi/%7Ehtauriai/lbtt/}>. + +Permission is granted to make and distribute verbatim +copies of this manual provided the copyright notice and +this permission notice are preserved on all copies. + +Permission is granted to copy and distribute modified +versions of this manual under the conditions for +verbatim copying, provided also that the section +entitled ``GNU General Public License'' +is included exactly as in the original, and provided +that the entire resulting derived work is distributed +under the terms of a permission notice identical to this +one. + +Permission is granted to copy and distribute +translations of this manual into another language, +under the above conditions for modified versions. +@end titlepage + +@contents +@end iftex + +@ifnottex +@node Top, Copying, , (dir) +@top @command{lbtt} + +@command{lbtt} is a tool for testing implementations of algorithms +for translating propositional linear temporal logic formulas into +B@"uchi automata. + +This is edition 1.0.0 of the @command{lbtt} documentation. This edition +applies to @command{lbtt} versions 1.0.x. + +@command{lbtt} is free software, you may change and redistribute it +under the terms of the GNU General Public License. @command{lbtt} +comes with NO WARRANTY. See @ref{Copying} for details. + +@menu +* Copying:: GNU General Public License. + +* Overview:: A short introduction to @command{lbtt}. +* Test methods:: Description of the tests @command{lbtt} + performs. + +* Invocation:: How to run the program. +* Interpreting the output:: Explanation of @command{lbtt}'s output messages. +* Analyzing test results:: Working with @command{lbtt}'s internal + commands. + +* Interfacing with @command{lbtt}:: Interfacing LTL-to-B@"uchi translators + with @command{lbtt}. +* The @command{lbtt-translate} utility:: An interface for two LTL-to-B@"uchi + translators. + +* References:: List of references. + +* Definitions:: A reference of the formal definitions of + the various objects that @command{lbtt} + manipulates. + +* Configuration file option index:: +* Command line option index:: +* User command index:: +* Concept index:: +@end menu +@end ifnottex + + +@node Copying, Overview, Top, Top +@include gpl.texi + + + +@node Overview, Test methods, Copying, Top +@chapter Overview + +@cindex model checking +@command{lbtt} is a tool for automatic testing of programs which translate +formulas expressed in propositional linear temporal logic (@dfn{LTL}) into +B@"uchi automata. These finite-state automata over infinite words are +used e.g.@: in automata-theoretic model checking +@ifnottex +@ref{[VW86]}, +@ref{[Var96]}, +@end ifnottex +@iftex +[VW86, Var96], +@end iftex +where they can help in detecting errors in the specifications of finite-state +hardware or software systems. Usually the model checking procedure involves +first composing an +automaton with a formal model of a given system, and the result of the +composition reveals whether any execution path of the system violates +some property that the automaton represents. +(For an introduction to model checking techniques in general, see, +for example, +@ifnottex +@ref{[CGP99]}.) +@end ifnottex +@iftex +[CGP99].) +@end iftex + +The property to be model checked is often specified as an +LTL formula, and the B@"uchi automaton used for model checking is obtained +automatically from the formula with a translation algorithm. (Detailed +descriptions and optimization techniques for such algorithms can be found, for +example, in +@ifnottex +@ref{[GPVW95]}, +@ref{[Cou99]} +@ref{[DGV99]}, +@ref{[Ete99]}, +@ref{[SB00]}, +@ref{[EH00]}, +@ref{[EWS01]}, +@ref{[GO01]}.) +@end ifnottex +@iftex +[GPVW95, Cou99, DGV99, Ete99, SB00, EH00, EWS01, GO01].) +@end iftex +In practice, the implementation correctness of such a translation algorithm +is crucial to the soundness of the model checking procedure. + +The goal of @command{lbtt} is to assist in the correct implementation of +LTL-to-B@"uchi translation algorithms by providing an automated +testing environment for LTL-to-B@"uchi translators. Testing consists of running +LTL-to-B@"uchi translators on randomly generated (or user-specified) LTL +formulas as input and then performing simple consistency checks on the +resulting automata to test whether the translators seem to operate correctly +in practice. (See +@ifnottex +@ref{[Tau00]} +@end ifnottex +@iftex +[Tau00] +@end iftex +for a description of the theory behind the testing methods.) If the tests +suggest an implementation +error in a translator, @command{lbtt} can generate sample data which causes +a test failure and which may also be useful for debugging the +implementation. + +Additionally, the testing environment can be used for very basic profiling of +different LTL-to-B@"uchi translators to evaluate their performance. + +@noindent +@emph{ +Note: although @command{lbtt} might be able to detect implementation errors +in an LTL-to-B@"uchi translator, it is only a testing tool and is therefore +incapable of formally proving any translation algorithm implementation to be +correct. Therefore, the test results should never be used as the sole basis +for any formal conclusions about the correctness of an implementation. +} + + + + + +@node Test methods, Invocation, Overview, Top +@chapter Test methods + +This chapter describes the algorithms @command{lbtt} uses for generating +input for the tests and introduces some terminology. +A short description of each test is also included +together with the outline of @command{lbtt}'s testing procedure. However, the +chapter is not intended to be a thorough introduction to the theoretical +background of the different tests; see, for example, +@ifnottex +@ref{[Tau00]} +@end ifnottex +@iftex +[Tau00] +@end iftex +for more information. + +@menu +* Random input generation:: How @command{lbtt} generates input for the + tests. +* Testing procedure:: Outline of @command{lbtt}'s testing + procedure. +* Model checking result cross-comparison test:: + Model checking the same LTL formula in + a fixed state space using different + LTL-to-B@"uchi translators should + give the same model checking result. +* Model checking result consistency check:: + Model checking two complementary LTL + formulas in the same state space using + an LTL-to-B@"uchi translator should + give consistent results. +* B@"uchi automata intersection emptiness check:: + The intersection of the languages + accepted by two B@"uchi automata + constructed from two complementary + LTL formulas should be empty. +@end menu + + + +@node Random input generation, Testing procedure, Test methods, Test methods +@section Random input generation + +By default, all tests @command{lbtt} makes are based on randomly generated +input. However, the LTL formulas used as input for the LTL-to-B@"uchi +translators can be optionally given by the user by telling @command{lbtt} to +read LTL formulas from a file +(@pxref{--formulafile,,@samp{--formulafile} command line option}). + +@cindex state space +Additionally, some of the tests make use of randomly generated +``state spaces'', which are basically directed labelled graphs with labels on +nodes with the additional requirement of having at least one arc leaving +each node. The label of each node is a subset of a finite collection of atomic +propositions +@cindex atomic proposition +(representing a set of conditions which may or may not hold in a +state), which occur also in the LTL formulas used in the tests. + +The following sections describe how @command{lbtt} generates input for the +tests and list the parameters which can be used to adjust the behavior of +the input generation algorithms. + +@menu +* Random LTL formulas:: How @command{lbtt} generates random + LTL formulas. +* Random state spaces:: How @command{lbtt} generates random + state spaces. +@end menu + + + +@node Random LTL formulas, Random state spaces, Random input generation, Random input generation +@subsection Random LTL formulas + +@cindex random LTL formula +@cindex LTL formula, random + +The LTL formulas used by @command{lbtt} are built from +atomic propositions (with names of the form +@ifhtml +@samp{p}@var{n} +@end ifhtml +@ifnothtml +@samp{p@var{n}} +@end ifnothtml +for some nonnegative integer @var{n}), the Boolean constants +@ifnottex +@samp{true} and @samp{false}, +@end ifnottex +@iftex +@tex +\sc{True} +@end tex +and +@tex +\sc{False}, +@end tex +@end iftex +and logical or temporal operators. @command{lbtt} supports the +following logical operators: + +@cindex supported LTL formula operators +@cindex LTL formula operators, supported +@itemize @bullet +@item +@cindex @samp{\/} (LTL formula operator) +@cindex @emph{or} (LTL formula operator) +@cindex disjunction (LTL formula operator) +logical disjunction +@iftex +(@tex +$\vee$ +@end tex +--- @samp{\/} as shown in output messages), +@end iftex +@ifnottex +(@samp{\/} as shown in output messages), +@end ifnottex + +@item +@cindex @samp{/\} (LTL formula operator) +@cindex @emph{and} (LTL formula operator) +@cindex conjunction (LTL formula operator) +logical conjunction +@iftex +(@tex +$\wedge$ +@end tex +--- @samp{/\}), +@end iftex +@ifnottex +(@samp{/\}), +@end ifnottex + +@item +@cindex @samp{!} (LTL formula operator) +@cindex @emph{not} (LTL formula operator) +@cindex negation (LTL formula operator) +logical negation +@iftex +(@tex +$\neg$ +@end tex +--- @samp{!}), +@end iftex +@ifnottex +(@samp{!}), +@end ifnottex + +@item +@cindex @samp{->} (LTL formula operator) +@cindex implication (LTL formula operator) +logical implication +@iftex +(@tex +$\rightarrow$ +@end tex +--- @samp{->}) +@end iftex +@ifnottex +(@samp{->}) +@end ifnottex + +@item +@cindex @samp{<->} (LTL formula operator) +@cindex equivalence (LTL formula operator) +logical equivalence +@iftex +(@tex +$\leftrightarrow$ +@end tex +--- @samp{<->}) +@end iftex +@ifnottex +(@samp{<->}) +@end ifnottex + +@item +@cindex @samp{xor} (LTL formula operator) +@cindex exclusive or (LTL formula operator) +logical ``exclusive or'' +@iftex +(@tex +$\oplus$ +@end tex +--- @samp{xor}) +@end iftex +@ifnottex +(@samp{xor}) +@end ifnottex + +@end itemize + +@noindent +and the following temporal operators: + +@itemize @bullet +@item +@cindex @samp{X} (LTL formula operator) +@cindex next time (LTL formula operator) +``Next time'' +@iftex +(@tex +$\bf{X}$ +@end tex +--- @samp{X}), +@end iftex +@ifnottex +(@samp{X}), +@end ifnottex + +@item +@cindex @samp{U} (LTL formula operator) +@cindex until (LTL formula operator) +@cindex strong until (LTL formula operator) +``(Strong) Until'' +@iftex +(@tex +$\bf{U}$ +@end tex +--- @samp{U}), +@end iftex +@ifnottex +(@samp{U}), +@end ifnottex + +@item +@cindex @samp{W} (LTL formula operator) +@cindex weak until (LTL formula operator) +@cindex unless (LTL formula operator) +``Weak Until'' (also known as ``Unless'') +@iftex +(@tex +$\bf{W}$ +@end tex +--- @samp{W}), +@end iftex +@ifnottex +(@samp{W}), +@end ifnottex + +@item +@cindex @samp{F} (LTL formula operator) +@cindex finally (LTL formula operator) +@cindex eventually (LTL formula operator) +``Finally'' (``Eventually'') +@iftex +(@tex +$\bf{F}$ +@end tex +--- @samp{<>}) +@end iftex +@ifnottex +(@samp{<>}) +@end ifnottex + +@item +@cindex @samp{B} (LTL formula operator) +@cindex before (LTL formula operator) +``Before'' +@iftex +(@tex +$\bf{B}$ +@end tex +--- @samp{B}) +@end iftex +@ifnottex +(@samp{B}) +@end ifnottex + +@item +@cindex @samp{V} (LTL formula operator) +@cindex release (LTL formula operator) +@cindex weak release (LTL formula operator) +``(Weak) Release'', the dual of ``(Strong) Until'' +@iftex +(@tex +$\bf{V}$ +@end tex +--- @samp{V}), +@end iftex +@ifnottex +(@samp{V}), +@end ifnottex + +@item +@cindex @samp{M} (LTL formula operator) +@cindex strong release (LTL formula operator) +``Strong Release'', the dual of ``Weak Until'' +@iftex +(@tex +$\bf{M}$ +@end tex +--- @samp{M}), +@end iftex +@ifnottex +(@samp{M}), +@end ifnottex + +@item +@cindex @samp{G} (LTL formula operator) +@cindex globally (LTL formula operator) +@cindex always (LTL formula operator) +@cindex henceforth (LTL formula operator) +``Globally'' (``Always'', ``Henceforth'') +@iftex +(@tex +$\bf{G}$ +@end tex +--- @samp{[]}). +@end iftex +@ifnottex +(@samp{[]}). +@end ifnottex + +@end itemize + +@noindent +See @ref{LTL formulas}, for a reference on the exact semantics of these +operators. + +@cindex parameters for random LTL formula generation algorithm +@cindex random LTL formula, parameters for generation algorithm +@cindex LTL formula, parameters for generation algorithm +The behavior of @command{lbtt}'s random formula generation algorithm can be +adjusted with the following parameters: + +@itemize @bullet + +@item +@cindex LTL formula, size +@cindex formula size +@cindex random LTL formula, size +Number of nodes in the parse tree of a formula (i.e., the total number of +occurrences of propositions, Boolean constants and operators in the formula). + +@item +Number of different atomic propositions that can be used for generating a +formula. (Note that this does not restrict the total number of atomic +propositions in the formula, nor the number of occurrences of any individual +proposition. However, none of the generated formulas will have more than this +number of @emph{different} atomic propositions.) + +@item +@cindex priorities for formula constants, atomic propositions and operators +@cindex constants, priorities for +@cindex atomic propositions, priorities for +@cindex operators, priorities for +Priorities for the Boolean constants and atomic propositions. The priority of +a symbol determines the relative likelihood of its occurrence in a generated +formula. The higher the priority of a symbol, the more likely it will occur +(with respect to the other symbols) in a generated formula; a zero priority +will exclude the symbol altogether. + +@item +Priorities for the logical and temporal operators. + +@end itemize + +@noindent +Note that the priorities for atomic symbols (Boolean constants and atomic +propositions) and the priorities of the logical and temporal operators are +independent, i.e., changing the priority of an atomic symbol does not affect +the occurrence likelihood of any logical or temporal operator and vice versa. + +@ifnottex +For further details, see @ref{The formula generation algorithm} for a +pseudocode description of the algorithm used for generating random LTL +formulas. +@end ifnottex + +@node The formula generation algorithm, , Random LTL formulas, Random LTL formulas + +@subsubsection The formula generation algorithm +@cindex random LTL formula, generation algorithm +@cindex LTL formula, generation algorithm + +@command{lbtt} uses an algorithm similar to the one outlined in +@ifnottex +@ref{[DGV99]} +@end ifnottex +@iftex +[DGV99] +@end iftex +for generating random LTL formulas. The algorithm can be described in +pseudocode as follows: + +@smallexample +@r{1} function RandomFormula(@var{n} : Integer) : LtlFormula; +@r{2} if @var{n} = 1 then begin +@r{3} @var{p} := @r{random atomic proposition or} @r{TRUE} @r{or} @r{FALSE}; +@r{4} return @var{p}; +@r{5} end +@r{6} else if @var{n} = 2 then begin +@r{7} @var{op} := @r{random unary operator}; +@r{8} @var{f} := RandomFormula(1); +@r{9} return @var{op} @var{f}; +@r{10} end +@r{11} else begin +@r{12} @var{op} := @r{random unary or binary operator}; +@r{13} if @var{op} @r{is a unary operator} then begin +@r{14} @var{f} := RandomFormula(@var{n}-1); +@r{15} return @var{op} @var{f}; +@r{16} end +@r{17} else begin +@r{18} @var{x} := @r{random integer in the interval} [1,@var{n}-2]; +@r{19} @var{f1} := RandomFormula(@var{x}); +@r{20} @var{f2} := RandomFormula(@var{n}-@var{x}-1); +@r{21} return (@var{f1} @var{op} @var{f2}); +@r{22} end; +@r{23} end; +@r{24} end; +@end smallexample + +Each invocation of the algorithm returns an LTL formula with @var{n} +nodes in the formula parse tree. The behavior of the algorithm can be adjusted +by giving values for the parameters @var{n} (the number of nodes in the +formula parse tree), +@iftex +@tex +$|\it{AP}|$ +@end tex +@end iftex +@ifnottex +@math{|AP|} +@end ifnottex +(the number of different atomic propositions), and +@iftex +@tex +$\it{pri}(\it{SYMBOL})$ +@end tex +@end iftex +@ifnottex +pri(@var{symbol}) +@end ifnottex +(the priorities for the different symbols). + +@cindex priorities for formula constants, atomic propositions and operators +@cindex constants, priorities for +@cindex probabilities for formula constants and atomic propositions +@cindex constants, computing probabilities for +In @command{lbtt}'s implementation of the above algorithm, the priority of a +symbol determines the probability with which the symbol is chosen +into a generated formula each time line 3, 7 or 12 of the algorithm is +executed. For Boolean constants +@ifnottex +@samp{true} and @samp{false} +@end ifnottex +@iftex +@tex +\sc{True} +@end tex +and +@tex +\sc{False} +@end tex +@end iftex +(line 3 of the algorithm), the probability is given by the equation + +@ifnottex +@math{pri(@var{CONSTANT}) / (pri(AP) + pri(@samp{true}) + pri(@samp{false}))} +@end ifnottex +@iftex +@tex +\smallskip +\center{$\it{pri}(\it{CONSTANT}) / \big(\it{pri}(\it{AP}) + \it{pri}($\sc{True}$) + \it{pri}($\sc{False}$)\big)$} +\smallskip +@end tex +@end iftex + +@noindent +where @var{CONSTANT} is either +@ifnottex +@samp{true} or @samp{false}, +@end ifnottex +@iftex +@tex +\sc{True} +@end tex +or +@tex +\sc{False}, +@end tex +@end iftex +and +@ifnottex +pri(@math{AP}) +@end ifnottex +@iftex +@tex +$\it{pri}(\it{AP})$ +@end tex +@end iftex +is the total priority of all atomic propositions. + +@cindex priorities for formula constants, atomic propositions and operators +@cindex atomic propositions, priorities for +@cindex probabilities for formula constants and atomic propositions +@cindex atomic propositions, computing probabilities for +The probability of choosing a particular atomic proposition into a formula +(line 3) is + +@ifnottex +@math{pri(AP) / (|AP| * (pri(AP) + pri(@samp{true}) + pri(@samp{false})))}, +@end ifnottex +@iftex +@tex +\smallskip +\center{$\it{pri}(\it{AP}) / \Big(|\it{AP}|\times\big(\it{pri}(\it{AP}) + \it{pri}($\sc{True}$) + \it{pri}($\sc{False}$)\big)\Big)$} +\smallskip +@end tex +@end iftex + +@noindent +where +@ifnottex +@math{|AP|} +@end ifnottex +@iftex +@tex +$|AP|$ +@end tex +@end iftex +and +@ifnottex +pri(@math{AP}) +@end ifnottex +@iftex +@tex +$\it{pri}(\it{AP})$ +@end tex +@end iftex +are as defined above. + +@cindex priorities for formula constants, atomic propositions and operators +@cindex operators, priorities for +@cindex probabilities for formula operators +@cindex operators, computing probabilities for +Line 7 of the algorithm concerns choosing a unary operator +@ifnottex +(@samp{!}, @samp{X}, @samp{<>} or @samp{[]}) +@end ifnottex +@iftex +@tex +($\neg$, $\bf{X}$, $\bf{F}$ or $\bf{G}$) +@end tex +@end iftex +into a formula. Here the probability of choosing the unary operator +@iftex +@tex +$\it{op}$ +@end tex +@end iftex +@ifnottex +@var{op} +@end ifnottex +is given by the equation + +@iftex +@tex +$$ +\it{pri}(\it{op}) / \sum_{\rm{op}' \in \{\neg, \bf{X}, \bf{F}, \bf{G}\}}\it{pri}\it(op') +$$ +@end tex +@end iftex +@ifnottex +@math{pri(@var{op}) / Sum (pri(@var{op'}))}, +@end ifnottex + +@noindent +where +@iftex +@tex +$\it{op}'$ +@end tex +@end iftex +@ifnottex +@var{op'} +@end ifnottex +ranges over all unary operators. + +Finally, at line 12 of the algorithm, the probability of choosing the (unary or +binary) operator +@iftex +@tex +$\it{op}$ +@end tex +@end iftex +@ifnottex +@var{op} +@end ifnottex +into the formula is + +@iftex +@tex +$$ +\it{pri}(\it{op}) / \sum_{\rm{op}' \in \{\neg, \vee, \wedge, \rightarrow, \leftrightarrow, \oplus, \bf{X}, \bf{U}, \bf{W}, \bf{F}, \bf{B}, \bf{V}, \bf{M}, \bf{G}\}}\it{pri}\it(op') +$$ +@end tex +@end iftex +@ifnottex +@math{pri(@var{op}) / Sum (pri(@var{op'}))}, +@end ifnottex + +@noindent +where +@iftex +@tex +$\it{op}'$ +@end tex +@end iftex +@ifnottex +@var{op'} +@end ifnottex +ranges over all unary and binary +@ifnottex +operators (@samp{!}, @samp{\/}, @samp{/\}, @samp{->}, @samp{<->}, @samp{xor}, +@samp{X}, @samp{U}, @samp{W}, @samp{<>}, @samp{B}, @samp{V}, @samp{M}, +@samp{[]}). +@end ifnottex +@iftex +operators. +@end iftex + +An analysis of this algorithm is included in an appendix of +@ifnottex +@ref{[Tau00]}. +@end ifnottex +@iftex +[Tau00]. +@end iftex +The analysis shows how to use the operator priorities to calculate the +expected number of occurrences of an operator in a randomly generated +formula. @command{lbtt} can optionally compute the expected operator +distribution for a given combination of operator priorities; see the +@samp{--showoperatordistribution} command line option +(@pxref{--showoperatordistribution,,@samp{--showoperatordistribution} command line option}) +for more information. + + + +@node Random state spaces, , Random LTL formulas, Random input generation +@subsection Random state spaces + +@cindex state space +@cindex state space, random +@cindex random state space +State spaces are needed as input for tests only in the +model checking result cross comparison test +(@pxref{Model checking result cross-comparison test}) and the +model checking result consistency check +(@pxref{Model checking result consistency check}). +The state spaces are directed labelled graphs, each node of which is labelled +with a randomly chosen set of atomic propositions (the propositions that +hold in the state corresponding to the graph node). In addition, each state of +the state space always has at least one successor. + +@command{lbtt} provides three different random state space generation +algorithms that differ in the structure of the generated state spaces. The +common parameters for each of these algorithms are: + +@cindex state space, generation parameters +@cindex random state space, generation parameters +@cindex parameters for random state space generation algorithm +@itemize @bullet + +@item +Number of states in the state space. + +@item +Maximum number of different atomic propositions allowed in the label of any +state of the state space. + +@item +The probability with which each atomic proposition should hold in a state of +the state space (which is, for simplicity, common to all atomic propositions). + +@end itemize + +@noindent +The different types of random state spaces that can be generated are: +@cindex state space, generation modes + +@enumerate + +@item +@cindex random connected graph +@cindex graph density +@cindex density (of a state space) +@cindex state space, density +@cindex random state space, density +Random connected graphs. These state spaces are guaranteed to have at least +one state such that every other state of the state space is reachable from +this state. In addition to the three above parameters, the behavior of the +algorithm generating these state spaces can be adjusted by specifying a +probability which approximates the @dfn{density} of the graph, i.e., the +probability that there is a directed edge from a state @var{x} to another +state @var{y}, where @var{x} and @var{y} are any two states in the state +space. For more details, see @ref{Algorithm for generating connected graphs}. + +@item +@cindex random graph +Random graphs. These state spaces are constructed simply by taking all +pairs (@var{x}, @var{y}) of states in the state space and connecting state +@var{x} to state @var{y} with a user-specified probability that approximates +the graph density. + +@item +@cindex random path +Random paths. A random path is simply a non-branching sequence of states, +where the last state of the sequence is connected to a randomly chosen state +earlier in the sequence. + +@end enumerate + +@cindex enumerated path +@command{lbtt} also includes a state space generation algorithm which +systematically enumerates all ``paths'' (see above) of a given size with a +given number of atomic propositions in each state. If +@iftex +@tex +$|S|$ +@end tex +@end iftex +@ifnottex +@math{|S|} +@end ifnottex +is the number of states in the path and +@iftex +@tex +$|\it{AP}|$ +@end tex +@end iftex +@ifnottex +@math{|AP|} +@end ifnottex +is the number of atomic propositions in each state of the path, it is easy to +see that the number of different paths having these parameters is + +@iftex +@tex +$$|S|\cdot 2^{|S|\cdot|AP|},$$ +@end tex +@end iftex +@ifnottex +@math{|S| * 2^(|S| * |AP|)}, +@end ifnottex + +@noindent +a number which grows exponentially in the product of the two parameters. +Obviously, this makes the exhaustive enumeration of all paths of a given size +practicable only for very small values of +@iftex +@tex +$|S|$ +@end tex +@end iftex +@ifnottex +@math{|S|} +@end ifnottex +and +@iftex +@tex +$|\it{AP}|$. +@end tex +@end iftex +@ifnottex +@math{|AP|}. +@end ifnottex + +In practice, you should always start testing using only very small state spaces +(say, with only 10--50 states and a small density) regardless of the particular +algorithm chosen for generating the state spaces. The size of the state spaces +can then be increased if @command{lbtt}'s memory consumption stays within +acceptable limits. + + + +@node Algorithm for generating connected graphs, , Random state spaces, Random state spaces + +@subsubsection Algorithm for generating connected graphs +@cindex random connected graph, generation algorithm +@cindex random state space, algorithm for generating random connected graphs +@cindex state space, algorithm for generating random connected graphs + +@command{lbtt} uses the following algorithm for generating random connected +graphs: + +@smallexample +@r{1} function RandomGraph(@var{n} : Integer; @var{d} : Real in [0.0,1.0]; + @var{t} : Real in [0.0,1.0]) : Graph; +@r{2} @var{S} := @{s1, s2, ..., sn@}; +@r{3} @var{NodesToProcess} := @{s1@}; +@r{4} @var{UnreachableNodes} := @{s2, s3, ..., sn@}; +@r{5} @var{Edges} := @{@}; +@r{6} while @var{NodesToProcess} @r{is not empty} do begin +@r{7} @var{state} := @r{a random node in} @var{NodesToProcess}; +@r{8} @r{remove} @var{state} @r{from} @var{NodesToProcess}; +@r{9} @var{Label}(@var{state}) := @{@}; +@r{10} for @r{all} @var{p} @r{in} @var{AP} do +@r{11} if RandomNumber(0.0, 1.0) < @var{t} then +@r{12} @r{insert} @var{p} @r{into} @var{Label}(@var{state}); +@r{13} if @var{UnreachableNodes} @r{is not empty} then begin +@r{14} @var{state'} := @r{a random node in} @var{UnreachableNodes}; +@r{15} @r{remove} @var{state'} @r{from} @var{UnreachableNodes}; +@r{16} @r{insert} @var{state'} @r{into} @var{NodesToProcess}; +@r{17} @r{insert} (@var{state},@var{state'}) @r{into} @var{Edges}; +@r{18} end; +@r{19} for @r{all} @var{state'} @r{in} @var{S} do +@r{20} if RandomNumber(0.0, 1.0) < @var{d} then begin +@r{21} @r{insert} (@var{state},@var{state'}) @r{into} @var{Edges}; +@r{22} if @var{state'} @r{is in} @var{UnreachableNodes} then begin +@r{23} @r{remove} @var{state'} @r{from} @var{UnreachableNodes}; +@r{24} @r{insert} @var{state'} @r{into} @var{NodesToProcess}; +@r{25} end; +@r{26} end; +@r{27} if @r{there is no edge} (@var{state},@var{state'}) @r{in} @var{Edges} + @r{for any} @var{state'} @r{in} @var{S} then +@r{28} @r{insert} (@var{state},@var{state}) @r{into} @var{Edges}; +@r{29} end; +@r{30} return <@var{S}, @var{Edges}, s1, @var{Label}>; +@r{31} end; +@end smallexample + +The algorithm receives the parameters @var{n} (number of states in the +state spaces), @var{d} (approximate density of the generated graph) and +@var{t} (the probability with which each of the propositions in @math{AP} +should hold in a state) and returns the generated state space as a quadruple +<@var{S}, @var{Edges}, s1, @var{Label}>. Here @var{S} is the set of +states, @var{Edges} is the set of directed edges between the states, @math{s1} +is a state from which every state of the state space can be reached, and +@var{Label} is a function which maps each state to its label (a subset of +@var{AP}). + + + +@node Testing procedure, Model checking result cross-comparison test, Random input generation, Test methods +@section Testing procedure + +@cindex testing procedure +The following figure illustrates the first two tests in @command{lbtt}'s +testing procedure: +@ifhtml +@* +@end ifhtml + +@image{testprocedure,6.5cm} + +@noindent +After obtaining an LTL formula +@ifnottex +@math{f} +@end ifnottex +@iftex +@tex +$f$ +@end tex +@end iftex +(either by reading it from a file or by calling the random formula generation +algorithm), +@command{lbtt} invokes each LTL-to-B@"uchi translator participating in +the tests in turn to construct a collection of B@"uchi automata for the formula +@ifnottex +@math{f} +@end ifnottex +@iftex +@tex +$f$ +@end tex +@end iftex +@emph{and} the negated formula +@ifnottex +@math{! f}. +@end ifnottex +@iftex +@tex +$\neg f$. +@end tex +@end iftex +Each of these automata is then composed with the +randomly generated state space, whereafter @command{lbtt} performs the +model checking result cross-comparison test +(@pxref{Model checking result cross-comparison test}) +and the model checking result consistency check +(@pxref{Model checking result consistency check}) +on the model checking results, and reports all detected failures. + +The B@"uchi automata intersection emptiness check +@ifnottex +(@pxref{B@"uchi automata intersection emptiness check}) +@end ifnottex +@iftex +(@pxref{Automata intersection emptiness check}) +@end iftex +operates as follows +(note that the LTL-to-B@"uchi translation phase is repeated in this figure +only for completeness; in reality, @command{lbtt} performs this phase only +once): +@ifhtml +@* +@end ifhtml + +@image{intersectioncheck,6.5cm} + +The test procedure can then be repeated using a different LTL formula +and/or a different state space. + + + + + +@node Model checking result cross-comparison test, Model checking result consistency check, Testing procedure, Test methods +@section Model checking result cross-comparison test + +@cindex model checking result cross-comparison test +@cindex tests, model checking result cross-comparison test + +LTL model checking can be used to test whether any of the infinite paths +starting from some state of a state space satisfies a given LTL +formula. For a fixed LTL formula, this question may have a different answer in +different states of the state space, but the answer should be independent +of the details of any (correct) implementation of the LTL model checking +procedure. + +Therefore, it is possible to test LTL-to-B@"uchi translators by comparing +the results obtained by model checking an LTL formula in a fixed state space +several times, using each time a different translator for constructing a +B@"uchi automaton from the LTL formula. Differences in the model checking +results then suggest that at least one of the translators failed to translate +the LTL formula correctly into an automaton. + +@cindex model checking modes +@cindex local model checking +@cindex global model checking +To extract as much test data as possible from a state space, +@command{lbtt} will by default make the model checking result comparison +``globally'' in the state space, which means using each LTL-to-B@"uchi +translator to find @emph{all} states in the state space with an infinite +path supposedly satisfying some LTL formula and then comparing the resulting +state sets for equality. Alternatively, the test can be performed only +``locally'' in a single state of each state space (i.e., by choosing some +state of the state space and checking that all B@"uchi automata constructed +using the different translators give the same model checking result in +that state), which may speed up testing, but +will reduce the number of comparison tests. In addition, @command{lbtt} repeats +the result +cross-comparison test for the negation of each LTL formula, since model +checking also the negated formula permits making an additional consistency +check (see below) on the results computed using each implementation. + +@cindex internal model checking algorithm +@cindex using the internal model checking algorithm +@cindex tests, against internal model checking algorithm +Note: If the generated state spaces are paths (either random or +systematically enumerated, @pxref{Random state spaces}), @command{lbtt} +will then include its internal LTL model checking algorithm (a restricted model +checking algorithm used normally in test failure analysis, +@pxref{Analyzing test results}) into the model +checking result cross-comparison test. This is especially useful if there is +only one translation algorithm implementation available for testing +(in which case normal model checking result cross-comparison is obviously +redundant) but may be of advantage also in other cases by +providing an additional implementation to include in the tests. + + + +@node Model checking result consistency check, B@"uchi automata intersection emptiness check, Model checking result cross-comparison test, Test methods +@section Model checking result consistency check + +@cindex model checking result consistency check +@cindex tests, model checking result consistency check + +LTL model checking tells whether any of the infinite paths starting from some +state of a state space satisfies a given LTL formula. If there +are no such paths beginning from the state, it follows that all infinite paths +beginning from the state must then satisfy the @emph{negation} of the same +formula. Since all state spaces used by @command{lbtt} always have at least +one path beginning from each state of the state space (guaranteed by the +state space generation algorithms), at least +one path beginning from any state must satisfy either the formula or its +negation, i.e., it cannot be the case that none of the paths is a model of +either formula. + +However, implementation errors in an LTL-to-B@"uchi translator used for model +checking may actually lead to this inconsistent model checking result if the +translation of either of the formulas results in an incorrect automaton, in +which case @command{lbtt} will report an error. + +@cindex model checking modes +@cindex local model checking +@cindex global model checking +Similarly to the model checking result cross-comparison test, the +model checking result consistency check can be performed either in all states +of the state space (``globally'') or only in a single state of the state +space (``locally''), with the same trade-offs between testing speed and +number of tests as described in the previous +@ifnottex +section (@pxref{Model checking result cross-comparison test}). +@end ifnottex +@iftex +section. +@end iftex + + +@node B@"uchi automata intersection emptiness check, , Model checking result consistency check, Test methods +@section B@"uchi automata intersection emptiness check +@anchor{Automata intersection emptiness check} + +@cindex B@"uchi automata intersection emptiness check +@cindex tests, B@"uchi automata intersection emptiness check + +The semantics of LTL guarantee that no model of an LTL formula +can be the model of the negation of the same formula. In terms of +B@"uchi automata, this implies that the languages accepted by automata +constructed from two complementary LTL formulas should be disjoint. +This can be confirmed by intersecting the automata (i.e., by composing the +automata to construct a third B@"uchi automaton that accepts precisely +those inputs accepted by both of the original automata) and checking the +result for emptiness. If the intersection is found to be nonempty, however, +at least one of the LTL-to-B@"uchi translator(s) used for constructing the +original automata must have failed to perform the translation of either +formula correctly. + + + +@node Invocation, Interpreting the output, Test methods, Top +@chapter Invocation + +@cindex invoking @command{lbtt} +@cindex starting @command{lbtt} +@cindex tests, starting +@cindex @command{lbtt} (executable file) +@command{lbtt} is started with the command @command{lbtt} with optional +command line parameters. Before starting the program, however, you need +to create a configuration file which lists the LTL-to-B@"uchi translators +to be tested and defines additional testing parameters. +@xref{Configuration file}. If no suitable configuration file is found or +if the configuration file cannot be processed successfully, @command{lbtt} +exits with an error message. + +After reading the configuration file @command{lbtt} starts tests on the +LTL-to-B@"uchi translators listed in the configuration file (for +details about the testing procedure, see @ref{Test methods}). The program +exits after a predetermined number of test rounds. + +@cindex exiting @command{lbtt} +@cindex quitting @command{lbtt} +@cindex tests, aborting +If the program is started in any of its interactive modes +(see @ref{Interactivity modes}), the program +may occasionally pause to wait for user input between test rounds. Type +@samp{quit @key{ENTER}} at the prompt to exit @command{lbtt} at this +point (or see @ref{Analyzing test results}, for more information on +how to use @command{lbtt}'s internal commands). + +@menu +* Configuration file:: Description of the configuration file + format. +* Command line options:: List of command line options. +@end menu + + + +@node Configuration file, Command line options, Invocation, Invocation +@section Configuration file + +@cindex configuration file, formatting +@cindex conventions for writing configuration files + +The configuration file of @command{lbtt} contains a list of the +LTL-to-B@"uchi translators to be tested along with other options +which affect the way the tests are performed. The configuration +file is processed before starting the tests. By default, @command{lbtt} +will try to read the configuration from the file @file{config} in the +current working directory; a different file name can be specified with the +@option{--configfile=@var{filename}} command line option. + +The configuration file consists of one or more sections, each of which +provides a collection of interrelated configuration options. The general +format of the configuration file is + +@smallexample +@var{section-name} +@{ + @var{option-name} = @var{value} + @var{option-name} = @var{value} + @dots{} +@} + +@dots{} +@end smallexample + +@cindex configuration file, option values +@cindex truth values in configuration file +@cindex string values in configuration file +@cindex numeric values in configuration file + +@noindent +Section and option names are case-insensitive. Values can be numbers, +strings or truth values (@samp{yes} and @samp{no}, or equivalently, +@samp{true} and @samp{false}). String values are case-sensitive and must be +always enclosed in quotes (@samp{"}). +The backslash (@samp{\}) is treated as an escape character (to be used +e.g.@: if the string itself contains quotes; use @samp{\\} to get a backslash). + +@cindex comments in configuration file +@cindex configuration file, comments + +Comments can be included by putting a @samp{#} symbol before them; the end of +any line containing the @samp{#} character will be ignored when processing the +configuration file. + +@cindex configuration file, minimal requirements +@cindex minimal requirements for configuration files + +The configuration file must contain at least one @samp{Algorithm} +section specifying an LTL-to-B@"uchi translator. The other sections +are optional and can be used to override the default testing parameters. + +@menu +* @samp{Algorithm} section:: Each LTL-to-B@"uchi translator to be + tested requires a separate + @samp{Algorithm} section in the + configuration file. +* @samp{GlobalOptions} section:: Options for changing the general + behavior of @command{lbtt}. +* @samp{FormulaOptions} section:: Options controlling the way random + LTL formulas are generated. +* @samp{StateSpaceOptions} section:: Options controlling the way random + state spaces are generated. +* Sample configuration file:: An example of a configuration file. +@end menu + + +@node @samp{Algorithm} section, @samp{GlobalOptions} section, Configuration file, Configuration file +@subsection The @samp{Algorithm} section + +@cindex configuration file, @samp{Algorithm} section +@cindex @samp{Algorithm} section (configuration file) +@cindex configuration file, minimal requirements + +Each LTL-to-B@"uchi translator to be tested requires a separate +@samp{Algorithm} section in the configuration file; there must be at least +one such section in the file. The translators are assumed to be accessible +through external executable files. Therefore, this section must at a minimum +specify the full file name of the executable to run in order to invoke +the translator; see @ref{Translator interface}, for information about the +conventions @command{lbtt} uses to communicate with the +LTL-to-B@"uchi translators. + +@cindex LTL-to-B@"uchi translators, identifiers +@cindex identifiers for LTL-to-B@"uchi translators +Translators specified in the configuration file are given unique +integer identifiers in the order they are listed in the configuration file, +starting from zero. These numbers must be used when referring to the +different translators when using @command{lbtt}'s internal commands +(@pxref{Analyzing test results}). + +The following options (in alphabetical order) are available within this +section: + +@table @samp +@item Enabled = @var{TRUTH-VALUE} +@cindex enabling LTL-to-B@"uchi translators +@cindex LTL-to-B@"uchi translators, enabling +@cindex disabling LTL-to-B@"uchi translators +@cindex LTL-to-B@"uchi translators, disabling +@findex Enabled @r{[}Algorithm@r{]} +This option determines whether the translator should be initially included in +or excluded from the tests. The default value is @samp{Yes}. The translator +can be enabled or disabled during testing with @command{lbtt}'s +internal commands (@pxref{Test control commands}). + +@item Name = @var{STRING} +@findex Name @r{[}Algorithm@r{]} +This option can be used to specify a textual identifier for the LTL-to-B@"uchi +translator. This identifier will then be used when displaying various messages +concerning the implementation. (If no name has been explicitly given for the +translator, @command{lbtt} assigns the translator a name of the form +@samp{Algorithm @var{n}}, where @var{n} is the running integer identifier for +the translators.) + +@item Parameters = @var{STRING} +@findex Parameters @r{[}Algorithm@r{]} +This option can be used to specify any additional parameters that should +be passed to the translator executable whenever running it. (The default +value of this option is the empty string.) + +@item Path = @var{STRING} +@findex Path @r{[}Algorithm@r{]} +This option must be given a value for each translator specified in the +configuration file. The value should be the complete file name of the program +which is used to run the translator. + +@end table + + + +@node @samp{GlobalOptions} section, @samp{FormulaOptions} section, @samp{Algorithm} section, Configuration file +@subsection The @samp{GlobalOptions} section + +@cindex configuration file, @samp{GlobalOption} section +@cindex @samp{GlobalOptions} section (configuration file) + +The @samp{GlobalOptions} section includes options that affect the general +behavior of @command{lbtt}. Options available within this section are (in +alphabetical order): + +@table @samp +@item ComparisonCheck = @var{TRUTH-VALUE} +@itemx ComparisonTest = @var{TRUTH-VALUE} +@cindex tests, enabling and disabling +@cindex enabling and disabling tests +@findex ComparisonCheck @r{[}GlobalOptions@r{]} +@findex ComparisonTest @r{[}GlobalOptions@r{]} +This option can be used to enable or disable the model checking result +cross-comparison test (@pxref{Model checking result cross-comparison test}). +The test is enabled by default. + +@item ConsistencyCheck = @var{TRUTH-VALUE} +@itemx ConsistencyTest = @var{TRUTH-VALUE} +@cindex tests, enabling and disabling +@cindex enabling and disabling tests +@findex ConsistencyCheck @r{[}GlobalOptions@r{]} +@findex ConsistencyTest @r{[}GlobalOptions@r{]} +This option can be used to enable or disable the model checking result +consistency check (@pxref{Model checking result consistency check}). The test +is enabled by default. + +@item @anchor{Interactivity modes}Interactive = Always @r{|} OnError @r{|} Never +@cindex interactivity modes +@findex Interactive @r{[}GlobalOptions@r{]} +The interactivity mode determines whether @command{lbtt} should pause to wait +for user input between test rounds. A value of @samp{Always} causes +@command{lbtt} to pause unconditionally after each test round, @samp{OnError} +will interrupt testing only after failed test rounds, and @samp{Never} will +simply run all tests without interruption. + +The default value for this option is @samp{Always}. + +@item IntersectionCheck = @var{TRUTH-VALUE} +@itemx IntersectionTest = @var{TRUTH-VALUE} +@cindex tests, enabling and disabling +@cindex enabling and disabling tests +@findex IntersectionCheck @r{[}GlobalOptions@r{]} +@findex IntersectionTest @r{[}GlobalOptions@r{]} +This option can be used to enable or disable the B@"uchi automata intersection +emptiness check +@ifnottex +(@pxref{B@"uchi automata intersection emptiness check}). +@end ifnottex +@iftex +(@pxref{Automata intersection emptiness check}). +@end iftex +The test is enabled by default. + +@item ModelCheck = Local @r{|} Global +@findex ModelCheck @r{[}GlobalOptions@r{]} +@cindex model checking modes +@cindex local model checking +@cindex global model checking +This option determines whether @command{lbtt} should perform model checking +with respect to all states of each state space or only with respect +to a single state of each state space. This affects the number of tests that +@command{lbtt} makes during the model checking result cross-comparison test +(@pxref{Model checking result cross-comparison test}) and the model checking +result consistency check (@pxref{Model checking result consistency check}). +Global model checking (the default) maximizes the number of tests, but may +require more time and memory. (Note: This option has no effect if none of the +model checking tests is enabled.) + +@item Rounds = @var{INTEGER} +@findex Rounds @r{[}GlobalOptions@r{]} +The @samp{Rounds} option can be used to specify the number of test rounds to +run; the default value is 10. + +@item Verbosity = @var{INTEGER} +@findex Verbosity @r{[}GlobalOptions@r{]} +@cindex verbosity, changing +@cindex changing verbosity of output +This option sets the verbosity level for output messages. The value +can be an integer between 0 and 5 (inclusive). A value of 0 will suppress all +messages (and is therefore useful only when storing test results into a log +file; @pxref{--logfile,,@samp{--logfile} command line option}); increasing +the value results in more output. The default value is 3. + +@end table + + + +@node @samp{FormulaOptions} section, @samp{StateSpaceOptions} section, @samp{GlobalOptions} section, Configuration file +@subsection The @samp{FormulaOptions} section + +@cindex configuration file, @samp{FormulaOptions} section +@cindex @samp{FormulaOptions} section (configuration file) + +The @samp{FormulaOptions} section defines the parameters that affect the +algorithm @command{lbtt} uses for generating random LTL formulas +(for more information about the algorithm, see @ref{Random LTL formulas}). +This section provides the following options: + +@cindex parameters for random LTL formula generation algorithm +@cindex random LTL formula, parameters for generation algorithm +@cindex LTL formula, parameters for generation algorithm +@cindex priorities for formula constants, atomic propositions and operators +@cindex atomic propositions, priorities for +@cindex constants, priorities for +@cindex operators, priorities for + +@table @samp +@item AbbreviatedOperators = @var{TRUTH-VALUE} +@cindex abbreviated LTL formula operators +@cindex LTL formula operators, abbreviated +@cindex operators, abbreviated +@findex AbbreviatedOperators @r{[}FormulaOptions@r{]} +This option determines whether the generated formulas should be allowed to +include any of the operators @samp{->}, @samp{<->}, @samp{xor}, @samp{W}, +@samp{<>}, @samp{B}, @samp{V}, @samp{M} or @samp{[]} (all of which can be +given definitions using only the operators @samp{!}, @samp{\/}, @samp{/\}, +@samp{U} and @samp{V}). Setting this option to @samp{No} assigns each of the +abbreviated operators a zero priority, overriding any explicit priorities +defined for these operators in the program configuration. The default value +for the option is @samp{Yes}, so abbreviations are allowed by default. + +@item AndPriority = @var{INTEGER} +@findex AndPriority @r{[}FormulaOptions@r{]} +Priority of the logical conjunction operator (@samp{/\}). + +@item BeforePriority = @var{INTEGER} +@findex BeforePriority @r{[}FormulaOptions@r{]} +Priority of the temporal operator ``before'' (@samp{B}). (Note: This option +has no effect if @samp{AbbreviatedOperators} is set to @samp{No}.) + +@item ChangeInterval = @var{INTEGER} +@findex ChangeInterval @r{[}FormulaOptions@r{]} +This option determines how often (in number of test rounds) +@command{lbtt} should generate a new random LTL formula (or read a new formula +from a user-specified file). A value of 0 forces +@command{lbtt} to use a fixed LTL formula for all tests. The default value +is 1, i.e.,@: a new formula will be generated at the beginning of each test +round. + +@item DefaultOperatorPriority = @var{INTEGER} +@cindex default operator priority +@findex DefaultOperatorPriority @r{[}FormulaOptions@r{]} +This option sets the priority for all formula operators for which +no priority has been given explicitly in the program configuration (i.e., +it can be used as a shorthand to initialize the priority of all operators). +The default value of this option is 0, so all operators with no explicitly +given priorities are disabled by default. + +@item EquivalencePriority = @var{INTEGER} +@findex EquivalencePriority @r{[}FormulaOptions@r{]} +Priority of the logical equivalence operator (@samp{<->}). (Note: This +option has no effect if @samp{AbbreviatedOperators} is set to @samp{No}.) + +@item FalsePriority = @var{INTEGER} +@findex FalsePriority @r{[}FormulaOptions@r{]} +Priority of the Boolean constant +@iftex +@tex +\sc{False} +@end tex +@end iftex +@ifnottex +@samp{false} +@end ifnottex +(with respect to priorities of the constant +@iftex +@tex +\sc{True} +@end tex +@end iftex +@ifnottex +@samp{true} +@end ifnottex +and the atomic propositions). + +@item FinallyPriority = @var{INTEGER} +@findex FinallyPriority @r{[}FormulaOptions@r{]} +Priority of the temporal operator ``finally'' (@samp{<>}). (Note: This +option has no effect if @samp{AbbreviatedOperators} is set to @samp{No}.) + +@item GenerateMode = Normal @r{|} NNF +@cindex random LTL formula, generation modes +@cindex negation normal form +@findex GenerateMode @r{[}FormulaOptions@r{]} +This option determines whether @command{lbtt} should generate random formulas +directly into (a weakened version of) negation normal form in which the +negation operator may only precede atomic propositions. Note that the +formulas may still contain ``abbreviated'' operators if they have nonzero +priorities---use @samp{AbbreviatedOperators=No} or @samp{OutputMode=NNF} if you +wish to prevent this. The default value for this option is @samp{Normal}. +(See the @samp{OutputMode} option below for an example about the differences +in the effects of the @samp{GenerateMode} and @samp{OutputMode} options.) + +@item GloballyPriority = @var{INTEGER} +@findex GloballyPriority @r{[}FormulaOptions@r{]} +Priority of the temporal operator ``globally'' (@samp{[]}). (Note: This +option has no effect if @samp{AbbreviatedOperators} is set to @samp{No}.) + +@item ImplicationPriority = @var{INTEGER} +@findex ImplicationPriority @r{[}FormulaOptions@r{]} +Priority of the logical implication operator (@samp{->}). (Note: This +option has no effect if @samp{AbbreviatedOperators} is set to @samp{No}.) + +@item NextPriority = @var{INTEGER} +@findex NextPriority @r{[}FormulaOptions@r{]} +Priority of the temporal operator ``next time'' (@samp{X}). + +@item NotPriority = @var{INTEGER} +@findex NotPriority @r{[}FormulaOptions@r{]} +Priority of the logical negation operator (@samp{!}). + +@item OrPriority = @var{INTEGER} +@findex OrPriority @r{[}FormulaOptions@r{]} +Priority of the logical disjunction operator (@samp{\/}). + +@item OutputMode = Normal @r{|} NNF +@findex OutputMode @r{[}FormulaOptions@r{]} +@cindex random LTL formula, output modes +@cindex LTL formula, output modes +@cindex negation normal form +This option determines whether @command{lbtt} should transform each generated +LTL formula into (strict) negation normal form before passing it to +LTL-to-B@"uchi translators. If the value is set to @samp{NNF}, @command{lbtt} +will rewrite each generated formula into a form consisting of the +operators @samp{!}, @samp{\/}, @samp{/\}, @samp{U} and @samp{V} such that all +negations in the formula (if any) precede atomic propositions. The default +value is @samp{Normal}. (See also the @samp{GenerateMode} option that can be +used to force formulas to be generated directly into negation normal +form.) + +The option is probably useful only if you have a translator which does not +support the ``abbreviated'' operators directly, but you still wish to test it +with formulas which describe properties expressed using these operators. +Note, however, that rewriting may change the size of the formula. + +The following table illustrates the effects of the +@samp{GenerateMode} and the @samp{OutputMode} options. +@smallformat +@t{ + @r{LTL formula} @r{Can} f @r{be} OutputMode@r{'s effect on the} + f @r{generated if} @r{formula passed to the} + GenerateMode @r{LTL-to-B@"uchi translators} + =NNF @r{?} + ------------------------------------------------------------- + (p1 V ! p0) @r{Yes} Normal@r{/}NNF@r{:} (p1 V ! p0) + + [] p0 -> <> p1 @r{Yes*} Nor@r{:} [] p0 -> <> p1 + NNF@r{:} (true U ! p0) \/ (true U p1) + + ! <> p0 @r{No} Nor@r{:} ! <> p0 + NNF@r{:} (false V ! p0) + + @r{* only if} AbbreviatedOperators=Yes +} +@end smallformat + +@item PropositionPriority = @var{INTEGER} +@cindex atomic propositions, priorities for +@findex PropositionPriority @r{[}FormulaOptions@r{]} +Priority for atomic propositions with respect to the priority of Boolean +constants. This priority is the common priority of @emph{all} atomic +propositions. + +@item Propositions = @var{INTEGER} +@findex Propositions @r{[}FormulaOptions@r{]} +This option sets the maximum number of different atomic propositions in +each generated LTL formula. No generated formula will have more than this +number of different atomic propositions. A value of 0 will generate random +formulas with only Boolean +constants (one of which must in this case have a nonzero priority). The +default value is 5. The names of the propositions are of the form +@ifhtml +@samp{p}@var{n}, +@end ifhtml +@ifnothtml +@samp{p@var{n}}, +@end ifnothtml +where @var{n} is a nonnegative integer less than the maximum number of +propositions. + +@item RandomSeed = @var{INTEGER} +@cindex random LTL formula, random seed for generation algorithm +@cindex random seed, LTL formula generation algorithm +@findex RandomSeed @r{[}FormulaOptions@r{]} +This option specifies a seed value for generating random numbers for the +random LTL formula generation algorithm. If this option is not present, the +seed defaults to 1. See the next section for information on how to change the +default seed for the random state space generation algorithm. + +(The reason for having two separate random seeds is to make the sequences of +random formulas and state spaces independent of each other. For example, +this makes it easy to repeat tests using the same batch of random LTL formulas, +but with state spaces of different size.) + +@item ReleasePriority = @var{INTEGER} +@findex ReleasePriority @r{[}FormulaOptions@r{]} +Priority of the temporal ``(weak) release'' operator (@samp{V}). + +@item Size = @var{INTEGER} +@itemx Size = @var{MINIMUM-SIZE}...@var{MAXIMUM-SIZE} +@findex Size @r{[}FormulaOptions@r{]} +This option defines how many nodes are allowed in the parse tree of each +randomly generated LTL formula. If the size is given as an interval (by +separating the bounds with three dots with no white space in between), +@command{lbtt} chooses the size of each formula randomly in the interval +using a uniform random distribution. The default size is 5. + +@item StrongReleasePriority = @var{INTEGER} +@findex StrongReleasePriority @r{[}FormulaOptions@r{]} +Priority of the temporal ``strong release'' operator (@samp{M}). (Note: This +option has no effect if @samp{AbbreviatedOperators} is set to @samp{No}.) + +@item UntilPriority = @var{INTEGER} +@findex UntilPriority @r{[}FormulaOptions@r{]} +Priority of the temporal ``(strong) until'' operator (@samp{U}). + +@item TruePriority = @var{INTEGER} +@findex TruePriority @r{[}FormulaOptions@r{]} +Priority of the Boolean constant +@iftex +@tex +\sc{True} +@end tex +@end iftex +@ifnottex +@samp{true} +@end ifnottex +(with respect to the priorities of the constant +@iftex +@tex +\sc{False} +@end tex +@end iftex +@ifnottex +@samp{false} +@end ifnottex +and the atomic propositions). + +@item WeakUntilPriority = @var{INTEGER} +@findex WeakUntilPriority @r{[}FormulaOptions@r{]} +Priority of the temporal ``weak until'' operator (@samp{W}). (Note: This +option has no effect if @samp{AbbreviatedOperators} is set to @samp{No}.) + +@item XorPriority = @var{INTEGER} +@findex XorPriority @r{[}FormulaOptions@r{]} +Priority of the logical ``exclusive or'' operator (@samp{xor}). (Note: This +option has no effect if @samp{AbbreviatedOperators} is set to @samp{No}.) + +@end table + + + +@node @samp{StateSpaceOptions} section, Sample configuration file, @samp{FormulaOptions} section, Configuration file +@subsection The @samp{StateSpaceOptions} section + +@cindex configuration file, @samp{StateSpaceOptions} section +@cindex @samp{StateSpaceOptions} section (configuration file) + +The @samp{StateSpaceOptions} section defines the parameters that affect the +way @command{lbtt} generates random state spaces for the +model checking result cross-comparison test +(@pxref{Model checking result cross-comparison test}) +and the model checking result consistency check +(@pxref{Model checking result consistency check}). +See also @ref{Random state spaces}, for more information about the different +types of available state spaces and the algorithms used for constructing them. +The options available within this section are: + +@cindex parameters for random state space generation algorithm +@cindex state space, generation parameters +@cindex random state space, generation parameters + +@table @samp +@item ChangeInterval = @var{INTEGER} +@findex ChangeInterval @r{[}StateSpaceOptions@r{]} +This option determines how often (in number of test rounds) +@command{lbtt} should generate a new random state space. A value of 0 forces +@command{lbtt} to use a fixed state space for all tests. The default behavior +is to generate a new state space at the beginning of each test round. + +@item EdgeProbability = @var{PROBABILITY} +@cindex graph density +@cindex density (of a state space) +@cindex state space, density +@cindex random state space, density +@findex EdgeProbability @r{[}StateSpaceOptions@r{]} +This option sets the approximate probability (between 0.0 and 1.0) of adding a +transition from any state @var{x} to some other state @var{y} when generating +random graphs as state spaces. The default value is 0.2. The probability is +approximate because +@command{lbtt} still has +to ensure that all states of each generated state spaces have at least one +successor, which might require adding extra transitions to the graph. +Note: This option has no effect if @samp{GenerateMode} is set to +@samp{RandomPath} or @samp{EnumeratedPath}. + +@item GenerateMode = RandomConnectedGraph @r{|} RandomGraph @r{|} RandomPath @r{|} EnumeratedPath +@cindex state space, generation modes +@cindex random connected graph +@cindex random graph +@cindex random path +@cindex enumerated path +@findex GenerateMode @r{[}StateSpaceOptions@r{]} +This option selects the type of generated state spaces from the four available +types. The default value is @samp{RandomConnectedGraph}. See +@ref{Random state spaces}, for more information on the different state space +types. + +@cindex internal model checking algorithm +@cindex using the internal model checking algorithm +@cindex tests, against internal model checking algorithm +Note: Using the @samp{RandomPath} or the @samp{EnumeratedPath} setting includes +@command{lbtt}'s internal model checking algorithm into the various model +checking tests if they are enabled. For more information, see +@ref{Model checking result cross-comparison test}. + +@item Propositions = @var{INTEGER} +@findex Propositions @r{[}StateSpaceOptions@r{]} +This option sets the number of atomic propositions attached to each state of +the generated state spaces. The default value is 5. + +Usually this should probably be the same as the maximum number of +different atomic propositions in the generated formulas +@ifnottex +(@pxref{@samp{FormulaOptions} section}). +@end ifnottex +@iftex +(see the previous section). +@end iftex +If the number of propositions attached to each state of +the state spaces is less than the maximum number of different propositions that +may occur in the generated formulas, all ``extra'' propositions in the formulas +are considered to be false in every state of the state space. + +@item RandomSeed = @var{INTEGER} +@cindex random state space, random seed for generation algorithm +@cindex random seed, state space generation algorithm +@findex RandomSeed @r{[}StateSpaceOptions@r{]} +This option specifies a seed value for generating random numbers required by +the random state space generation algorithm. If this option is not present, the +seed defaults to 1. See the previous section for how to change the random seed +used to initialize the random number generator for the random LTL formula +generation algorithm. + +@item Size = @var{INTEGER} +@itemx Size = @var{MINIMUM-SIZE}...@var{MAXIMUM-SIZE} +@findex Size @r{[}StateSpaceOptions@r{]} +This option sets the number of states in the generated state spaces. If the +size is given as an interval, @command{lbtt} either chooses a random size in +the interval (including its endpoints) each time a new state space is +generated, or, if @samp{GenerateMode} is set to @samp{EnumeratedPath}, +enumerates all state spaces in the specified range systematically, starting +from the minimum size. The default size is 20. + +@item TruthProbability = @var{PROBABILITY} +@findex TruthProbability @r{[}StateSpaceOptions@r{]} +Probability (between 0.0 and 1.0) with which each individual atomic proposition +has the value +@ifnottex +@samp{true} +@end ifnottex +@iftex +@tex +\sc{True} +@end tex +@end iftex +in any state of the state space. Note: This option has no effect if +@samp{GenerateMode} is set to @samp{EnumeratedPath}. The default value is 0.5. + +@end table + + +@node Sample configuration file, , @samp{StateSpaceOptions} section, Configuration file +@subsection Sample configuration file + +@cindex configuration file, example + +The following configuration file sets @command{lbtt} up for testing two +imaginary LTL-to-B@"uchi translators. + +@smallexample +# Sample configuration file for lbtt + +Algorithm +@{ + Name = "Translator 1" + Path = "~/lbtt/bin/t-1" # location of the translator + # executable + Enabled = Yes +@} + +Algorithm +@{ + Name = "Translator 2" + Path = "~/lbtt/bin/t-2" + Parameters = "-x -y 3 -v 0" # parameters to be passed to the + # `~/lbtt/bin/t-2' executable + Enabled = Yes +@} + +GlobalOptions +@{ + Rounds = 100 # 100 test rounds + + Interactive = OnError # pause only in case of an error + + Verbosity = 1 # suppress most of the output + + ComparisonTest = Yes # enable all tests except the + ConsistencyTest = Yes # B@"uchi automata intersection + IntersectionTest = No # emptiness test + + ModelCheck = Local # perform the tests only in a + # single state of each state + # space +@} + +FormulaOptions +@{ + AbbreviatedOperators = Yes # formula generation mode + GenerateMode = Normal + OutputMode = Normal + + ChangeInterval = 1 # new formula after each test + # round + + RandomSeed = 4632912 # random seed + + Size = 5...15 # 5 to 15 nodes in the parse + # tree of each formula + + Propositions = 3 # max. 3 different propositions + # in each LTL formula + + PropositionPriority = 50 # priorities for propositional + TruePriority = 1 # symbols + FalsePriority = 1 + + AndPriority = 10 # priorities for some logical + OrPriority = 10 # operators + NotPriority = 10 + + NextPriority = 5 # priorities for some temporal + UntilPriority = 5 # operators + ReleasePriority = 5 + + DefaultOperatorPriority = 0 # disable all the remaining + # operators +@} + +StatespaceOptions +@{ + GenerateMode = RandomGraph # generate random (not + # necessarily connected) graphs + # as state spaces + + ChangeInterval = 10 # new state space after every + # 10th test round + + RandomSeed = 37620 # random seed + + Size = 50 # 50 states in each state space + + Propositions = 3 # 3 propositions in each state + # of each state space + + EdgeProbability = 0.1 # approximate probability of + # having a transition between + # any two states + + TruthProbability = 0.5 # probability with which any + # atomic proposition is true in + # a state +@} + +@end smallexample + + +@node Command line options, , Configuration file, Invocation +@section Command line options + +This section lists the command line options that may be used when +invoking @command{lbtt}. The command line options are processed only +after reading the configuration file, so they can be used to override the +settings given in the file. There are also a few options for which +there is no direct equivalent in the configuration file options. + +@menu +* Special options:: Options available only as command line + parameters. +* Global options:: Options corresponding to the + @samp{GlobalOptions} section of the + configuration file. +* LTL formula options:: Options corresponding to the + @samp{FormulaOptions} section of the + configuration file. +* State space options:: Options corresponding to the + @samp{StateSpaceOptions} section of the + configuration file. +@end menu + + + +@node Special options, Global options, Command line options, Command line options +@subsection Special options + +The following list presents all command line options for which there is +no (directly) corresponding option that may be set in the program +configuration file. + +@table @samp +@item --configfile=@var{FILE-NAME} +@cindex configuration file, changing the name of +@vindex --configfile +This option can be used to instruct @command{lbtt} to read program +configuration from another file instead of the default configuration +file @samp{config} in the current working directory. + +@item @anchor{--formulafile}--formulafile=@var{FILE-NAME} +@vindex --formulafile +@cindex LTL formula, reading from a file +@cindex file formats, formula input file for @command{lbtt} +This option instructs @command{lbtt} to read the LTL formulas used in the tests +from a file instead of generating them randomly. The file should contain +LTL formulas separated from each other with white space. Currently the +only supported format for the formulas is the same prefix notation that +@command{lbtt} uses for its output files (@pxref{Format for LTL formulas}, +for details). + +If this option is used, all LTL formula generation parameters in the command +line or in the configuration file are ignored. + +@item --h +@itemx --help +@vindex -h +@vindex --help +These options list all the available command line parameters. + +@item @anchor{--logfile}--logfile=@var{FILE-NAME} +@vindex --logfile +@cindex log file for test failures +@cindex using a test failure log file +This option instructs @command{lbtt} to create a log of all errors +encountered during testing. By default no log will be created. + +@item --profile +@vindex --profile +@cindex tests, enabling and disabling +@cindex enabling and disabling tests +@cindex tests, profiling LTL-to-B@"uchi translators +This option can be used as a shorthand for disabling all B@"uchi automata +correctness tests. The test report generated at the end of testing then shows +only the running times of each tested LTL-to-B@"uchi translator and the sizes +of the generated automata. + +@item --quiet +@itemx --silent +@vindex --quiet +@vindex --silent +@cindex suppressing output +These options suppress any messages that are normally displayed during +testing. These options also imply the @samp{--nopause} option, i.e., all +tests will be run without interruption. Use the @samp{--logfile} option (see +above) with these options to save a test failure report into a log file. + +@item @anchor{--showconfig}--showconfig +@vindex --showconfig +If this option is present on the command line, @command{lbtt} will +write the current configuration to standard output +(@pxref{Configuration information}) and then exit. +This option can be used together with the @samp{--configfile} option to +test the settings defined in a configuration file without actually performing +any tests. + +@item @anchor{--showoperatordistribution}--showoperatordistribution +@vindex --showoperatordistribution +@cindex operators, computing distribution for +@cindex random LTL formula, computing operator distribution +With this option @command{lbtt} uses the priorities defined for the +LTL formula operators available for random LTL formula generation to compute +the expected number of occurrences of each operator in a single randomly +generated formula. The distribution is then displayed along with other +configuration information when the program starts. + +@item @anchor{--skip}--skip=@var{NUMBER-OF-ROUNDS} +@vindex --skip +@cindex tests, skipping test rounds +@cindex skipping test rounds +This option can be used to skip the first @var{NUMBER-OF-ROUNDS} test rounds, +i.e., begin testing from round @var{NUMBER-OF-ROUNDS}+1. + +@item --version +@vindex --version +This option displays the version of the @command{lbtt} executable. + +@end table + + + +@node Global options, LTL formula options, Special options, Command line options +@subsection Global options + +The following list presents the options that can be used to override the +values specified in the @samp{GlobalOptions} section of the configuration +file. + +@table @samp +@item --comparisontest +@itemx --nocomparisontest +@cindex tests, enabling and disabling +@cindex enabling and disabling tests +@vindex --comparisontest +@vindex --nocomparisontest +These options enable or disable the model checking result cross-comparison +test (@pxref{Model checking result cross-comparison test}). + +@item --consistencytest +@itemx --noconsistencytest +@cindex tests, enabling and disabling +@cindex enabling and disabling tests +@vindex --consistencytest +@vindex --noconsistencytest +These options enable or disable the model checking result consistency check +(@pxref{Model checking result consistency check}). + +@item --disable=@var{IMPLEMENTATION-ID}@r{[},@var{IMPLEMENTATION-ID}@r{...]} +@cindex disabling LTL-to-B@"uchi translators +@cindex LTL-to-B@"uchi translators, disabling +@vindex --disable +This option can be used to exclude some implementations from the tests by +specifying a comma-separated list of integers (the implementation identifiers). +The implementations are numbered in the order in which they appear in the +configuration file, starting from zero. (The @samp{--showconfig} +option, see @ref{Special options}, can be used to obtain a list of the +implementations specified in the configuration file, together with their +identifiers.) + +@item --enable=@var{IMPLEMENTATION-ID}@r{[},@var{IMPLEMENTATION-ID}@r{...]} +@cindex enabling LTL-to-B@"uchi translators +@cindex LTL-to-B@"uchi translators, enabling +@vindex --enable +This option can be used to include implementations into the tests (in the +case they are initially disabled in the configuration file). + +@item --globalmodelcheck +@vindex --globalmodelcheck +@cindex model checking modes +@cindex global model checking +This option instructs @command{lbtt} to perform model checking globally +(with respect to all states of each random state space) in the model +checking result cross-comparison test and the model checking result +consistency check. Using a global check increases the number of possible +tests. + +@item --interactive=always | onerror | never +@vindex --interactive +@cindex interactivity modes +This option can be used to override whether @command{lbtt} should pause +between test rounds to wait for user input. + +@item --intersectiontest +@item --nointersectiontest +@cindex tests, enabling and disabling +@cindex enabling and disabling tests +@vindex --intersectiontest +@vindex --nointersectiontest +These options enable or disable the B@"uchi automata intersection emptiness +check +@ifnottex +(@pxref{B@"uchi automata intersection emptiness check}). +@end ifnottex +@iftex +(@pxref{Automata intersection emptiness check}). +@end iftex + +@item --localmodelcheck +@vindex --localmodelcheck +@cindex model checking modes +@cindex local model checking +This option instructs @command{lbtt} to perform model checking only with +respect to a single state of each random state space in the model checking +result cross-comparison test and the model checking result consistency +check. + +@item --modelcheck=global | local +@vindex --modelcheck +@cindex model checking modes +@cindex local model checking +@cindex global model checking +This option can be used to select the model checking mode. + +@item --nopause +@vindex --nopause +@cindex interactivity modes +This option forces @command{lbtt} to run all tests without interruption. + +@item --pause +@vindex --pause +@cindex interactivity modes +This option forces @command{lbtt} to pause between each test round to +wait for user commands. + +@item --pauseonerror +@vindex --pauseonerror +@cindex interactivity modes +Using this option will cause testing to be paused each time an error occurs +during testing, +giving the user an opportunity to analyze the error situation using +@command{lbtt}'s internal commands before proceeding to the next test round. + +@item --rounds=@var{NUMBER-OF-ROUNDS} +@vindex --rounds +This option can be used to override the number of test rounds to run. + +@item --verbosity=@var{INTEGER} +@vindex --verbosity +@cindex verbosity, changing +@cindex changing verbosity of output +This option sets the verbosity of output messages. The value must be +between 0 and 5 (inclusive). + +@end table + + + +@node LTL formula options, State space options, Global options, Command line options +@subsection LTL formula options + +The following command line options can be used to control the behavior of +@command{lbtt}'s random LTL formula generation algorithm. They correspond +to the options available in the @samp{FormulaOptions} section of the +configuration file. + +@cindex parameters for random LTL formula generation algorithm +@cindex random LTL formula, parameters for generation algorithm +@cindex LTL formula, parameters for generation algorithm +@cindex priorities for formula constants, atomic propositions and operators +@cindex atomic propositions, priorities for +@cindex constants, priorities for +@cindex operators, priorities for + +@table @samp +@item --abbreviatedoperators +@itemx --noabbreviatedoperators +@vindex --abbreviatedoperators +@vindex --noabbreviatedoperators +@cindex abbreviated LTL formula operators +@cindex LTL formula operators, abbreviated +@cindex operators, abbreviated +These options can be used to allow or prevent @command{lbtt} from using any of +the ``abbreviated'' operators (@samp{->}, @samp{<->}, @samp{xor}, @samp{W}, +@samp{<>}, @samp{B}, @samp{M} and @samp{[]}) when generating random LTL +formulas. + +@item --andpriority +@vindex --andpriority +This option sets the priority for logical conjunction (the @samp{/\} operator). + +@item --beforepriority +@vindex --beforepriority +This option sets the priority for the temporal ``before'' operator +(@samp{B}). + +@item --defaultoperatorpriority +@vindex --defaultoperatorpriority +@cindex default operator priority +This option sets the default priority for all logical and temporal operators. + +@item --equivalencepriority +@vindex --equivalencepriority +This option sets the priority for logical equivalence (the @samp{<->} +operator). + +@item --falsepriority +@vindex --falsepriority +This option sets the priority for the Boolean constant @samp{false}. + +@item --finallypriority +@vindex --finallypriority +This option sets the priority for the temporal ``finally'' operator +(@samp{<>}). + +@item --formulachangeinterval=@var{NUMBER-OF-ROUNDS} +@vindex --formulachangeinterval +This option determines how often (in number of test rounds) +@command{lbtt} should generate a new random LTL formula. A value of 0 forces +@command{lbtt} to use a fixed LTL formula for all tests. + +@item --formulageneratemode=normal | nnf +@vindex --formulageneratemode +@cindex random LTL formula, generation modes +@cindex negation normal form +This option can be used to choose how @command{lbtt} should generate random +LTL formulas. With the option @samp{--formulageneratemode=nnf}, all generated +formulas will be in (a weakened) negation normal form in which all negations in +the formula (if any) precede atomic propositions. (Note that the formulas may +still contain some of the ``abbreviated'' operators if their priorities are +not explicitly set to zero.) + +@item --formulaoutputmode=normal | nnf +@vindex --formulaoutputmode +@cindex random LTL formula, output modes +@cindex LTL formula, output modes +@cindex negation normal form +This option can be used to force or prevent @command{lbtt} from converting each +LTL formula into (strict) negation normal form (i.e., rewriting it with the +operators @samp{!}, @samp{/\}, @samp{\/}, @samp{U} and @samp{V}) before passing +it to the LTL-to-B@"uchi translators. + +@item --formulapropositions +@vindex --formulapropositions +This option sets the maximum number of different atomic propositions that +@command{lbtt} may use for generating random LTL formulas. + +@item --formularandomseed=@var{INTEGER} +@cindex random seed, LTL formula generation algorithm +@vindex --formularandomseed +This option gives a seed value for generating random numbers used by the +random LTL formula generation algorithm. + +@item --formulasize=@var{INTEGER} +@itemx --formulasize=@var{MINIMUM-SIZE}...@var{MAXIMUM-SIZE} +@vindex --formulasize +This option sets the size of the random LTL formulas generated for the tests. +The size can be given either as a fixed integer or as an interval, in which +case the size of each generated formula will be chosen randomly in the +interval using a uniform random distribution. + +@item --generatennf +@itemx --nogeneratennf +@vindex --generatennf +@vindex --nogeneratennf +@cindex random LTL formula, generation modes +@cindex negation normal form +These options can be used instead of the @samp{--formulageneratemode} option +to select the random formula generation mode. + +@item --globallypriority +@vindex --globallypriority +This option sets the priority for the temporal ``globally'' operator +(@samp{[]}). + +@item --implicationpriority +@vindex --implicationpriority +This option sets the priority for logical implication (the @samp{->} operator). + +@item --nextpriority +@vindex --nextpriority +This option sets the priority for the temporal ``next time'' operator +(@samp{X}). + +@item --notpriority +@vindex --notpriority +This option sets the priority for logical negation (the @samp{!} operator). + +@item --orpriority +@vindex --orpriority +This option sets the priority for logical disjunction (the @samp{\/} operator). + +@item --outputnnf +@itemx --nooutputnnf +@vindex --outputnnf +@vindex --nooutputnnf +@cindex random LTL formula, output modes +@cindex LTL formula, output modes +@cindex negation normal form +These options can be used instead of the @samp{--formulaoutputmode} option to +choose the format in which @command{lbtt} passes LTL formulas to LTL-to-B@"uchi +translators. + +@item --propositionpriority +@vindex --propositionpriority +This option sets the priority for atomic propositions. + +@item --releasepriority +@vindex --releasepriority +This option sets the priority for the temporal ``(weak) release'' operator +(@samp{V}). + +@item --strongreleasepriority +@vindex --strongreleasepriority +This option sets the priority for the temporal ``strong release'' operator +(@samp{M}). + +@item --truepriority +@vindex --truepriority +This option sets the priority for the Boolean constant +@iftex +@tex +\sc{True}. +@end tex +@end iftex +@ifnottex +@samp{true}. +@end ifnottex + +@item --untilpriority +@vindex --untilpriority +This option sets the priority for the temporal ``(strong) until'' operator +(@samp{U}). + +@item --weakuntilpriority +@vindex --weakuntilpriority +This option sets the priority for the temporal ``weak until'' operator +(@samp{W}). + +@item --xorpriority +@vindex --xorpriority +This option sets the priority for the logical ``exclusive or'' operator. + +@end table + +Note also the @samp{--formulafile=@var{FILE-NAME}} option +(@pxref{--formulafile,,@samp{--formulafile} option}), which can be used to +instruct @command{lbtt} to read LTL formulas from a file instead of generating +them randomly. + + + +@node State space options, , LTL formula options, Command line options +@subsection State space options + +The following command line options affect the way in which @command{lbtt} +generates state spaces that are then used in the model checking tests. They +correspond to options in the @samp{StateSpaceOptions} section of the +configuration file. See also @ref{Random state spaces}, for more information +about the graph generation modes. + +@cindex parameters for random state space generation algorithm +@cindex state space, generation parameters +@cindex random state space, generation parameters + +@table @samp +@item --edgeprobability=@var{PROBABILITY} +@vindex --edgeprobability +This option sets the approximate random edge probability for state spaces. (The +option has no effect if the generated state spaces are random or enumerated +paths.) + +@item --enumeratedpath +@vindex --enumeratedpath +@cindex state space, generation modes +@cindex enumerated path +This option instructs @command{lbtt} to enumerate all paths of a given size +as state spaces instead of generating random state spaces for model checking +tests. The option also enables @command{lbtt}'s internal model checking +algorithm. + +@item --randomconnectedgraph +@vindex --randomconnectedgraph +@cindex state space, generation modes +@cindex random connected graph +This option makes @command{lbtt} generate random connected graphs as state +spaces for model checking tests. + +@item --randomgraph +@vindex --randomgraph +@cindex state space, generation modes +@cindex random graph +This option makes @command{lbtt} generate random graphs as state spaces for +model checking tests. + +@item --randompath +@vindex --randompath +@cindex state space, generation modes +@cindex random path +This option forces @command{lbtt} to generate random paths as state spaces. +The option also enables @command{lbtt}'s internal model checking algorithm +in the model checking tests. + +@item --statespacechangeinterval=@var{NUMBER-OF-ROUNDS} +@vindex --statespacechangeinterval +This option sets the frequency (in test rounds) in which new state spaces +are generated. A value of 0 forces @command{lbtt} to use a fixed state space +for all tests. + +@item --statespacegeneratemode=randomconnectedgraph | randomgraph | randompath | enumeratedpath +@vindex --statespacegeneratemode +@cindex state space, generation modes +@cindex enumerated path +@cindex random connected graph +@cindex random graph +@cindex random path +This option can be used instead of one of the four options above to select +the state space generation mode. + +@item --statespacerandomseed=@var{INTEGER} +@vindex --statespacerandomseed +@cindex random state space, random seed for generation algorithm +This option gives a seed value for generating random numbers required by the +random state space generation algorithm. + +@item --statespacesize=@var{INTEGER} +@itemx --statespacesize=@var{MINIMUM-SIZE}...@var{MAXIMUM-SIZE} +@vindex --statespacesize +This option can be used to change the size of the generated state spaces. + +@item --truthprobability=@var{PROBABILITY} +@vindex --truthprobability +This option sets the probability that @command{lbtt} uses for choosing the +valuation for each atomic proposition in each state of the randomly generated +state spaces. (This option has no effect is using enumerated paths as state +spaces.) + +@end table + + + +@node Interpreting the output, Analyzing test results, Invocation, Top +@chapter Interpreting the output + +This chapter briefly intoduces the most typical messages that +@command{lbtt} outputs during testing when running in its default output +verbosity mode (3). In lower verbosity modes some (or all) of these +messages will be suppressed; higher verbosity modes show information about +@command{lbtt}'s internal behavior. + +@menu +* Configuration information:: The current configuration is shown + before starting tests. +* Test round messages:: Conventions for reporting test + results and test failures. +* Test statistics:: Shown at the end of testing. +@end menu + + +@node Configuration information, Test round messages, Interpreting the output, Interpreting the output +@section Configuration information + +@cindex configuration information + +Before starting tests, @command{lbtt} shows a short summary of the current +program configuration as obtained by reading the program configuration file +and interpreting the command line parameters. The same summary can be +obtained without running any tests by using the +@samp{--showconfig} command line option +(@pxref{--showconfig,,@samp{--showconfig} option}). The information will be +written also to the error log file if one was specified in the command line +with the @samp{--logfile} option +(@pxref{--logfile,,@samp{--logfile} option}). +The summary consists of the following information: + +@itemize @bullet +@item +LTL-to-B@"uchi translator implementations enabled for testing. + +@item +List of enabled tests. + +@item +Random state space generation parameters. + +@item +Random LTL formula generation parameters (unless reading LTL formulas from +a file; @pxref{--formulafile,,@samp{--formulafile} command line option}). +This includes information about all enabled formula operators and their +priorities. When using the command line option +@samp{--showoperatordistribution} +(@pxref{--showoperatordistribution,,@samp{--showoperatordistribution} option}), +@command{lbtt} shows also the expected number of occurrence of each +operator in each randomly generated formula. + +@end itemize + +@noindent +Example: + +@smallexample +Program configuration: +---------------------- + + 1000 test rounds. + Testing will be interrupted in case of an error. + Using global model checking for tests. + Writing error log to `error.log'. + + Implementations: + 0: `Implementation 0' + 1: `Implementation 1' + + Enabled tests: + Model checking result cross-comparison test + Model checking result consistency check + B@"uchi automata intersection emptiness check + + Random state spaces: + Random connected graphs (50 states, 5 atomic propositions) + New state space will be generated after every 5th round. + Random seed: 98 + Random edge probability: 0.10 + Propositional truth probability: 0.50 + + Random LTL formulas: + 5 parse tree nodes, 5 atomic propositions + New LTL formula will be generated after every round. + Random seed: 17991 + Atomic symbols in use (priority): + false (5); propositions (90); true (5) + Operators used for random LTL formula generation: + operator ! /\ U V X \/ + priority 10 20 20 20 10 20 +@end smallexample + +@node Test round messages, Test statistics, Configuration information, Interpreting the output +@section Test round messages + +The following example shows a fragment of the output that @command{lbtt} might +produce during a test round: + +@cindex tests, output example + +@smallformat +1. @t{Round 6 of 10} + +2. @t{ Generating random state space} + +3. @t{ Random LTL formula:} +@r{ }@t{ formula: ((p1 <-> p0) U (p0 \/ ! p3))} +@r{ }@t{ negated formula: ! ((p1 <-> p0) U (p0 \/ ! p3))} + +@r{ }@t{ 0: `Implementation 0'} +@r{ }@t{ Positive formula:} +4. @t{ B@"uchi automaton:} +@r{ }@t{ number of states: 6} +@r{ }@t{ number of transitions: 15} +@r{ }@t{ acceptance sets: 1} +@r{ }@t{ computation time: 0.03 seconds (user time)} +5. @t{ Product automaton:} +@r{ }@t{ number of states: 582 [97.00% of worst case (600)]} +@r{ }@t{ number of transitions: 7188} +6. @t{ Accepting cycles:} +@r{ }@t{ cycle reachable from 0 states} +@r{ }@t{ not reachable from 100 states} +7. @t{ Negated formula:} +@r{ }@t{ B@"uchi automaton:} +@r{ }@t{ number of states: 4} +@r{ }@t{ number of transitions: 6} +@r{ }@t{ acceptance sets: 0} +@r{ }@t{ computation time: 0.04 seconds (user time)} +@r{ }@t{ Product automaton:} +@r{ }@t{ number of states: 363 [90.75% of worst case (400)]} +@r{ }@t{ number of transitions: 2581} +@r{ }@t{ Accepting cycles:} +@r{ }@t{ cycle reachable from 25 states} +@r{ }@t{ not reachable from 75 states} +8. @t{ Result consistency check:} +@r{ }@t{ result: failed [75 (75.00%) of 100 test cases]} +@end smallformat + +@noindent +The numbered parts of the output are: + +@enumerate +@item +Number of the test round. + +@item +@command{lbtt} generates a new random state space for model +checking tests. (In this case the size of the state spaces was fixed in the +configuration; if the state space size is allowed to vary in an interval, +@command{lbtt} would also show here the actual size of the generated state +space.) + +@item +@cindex operators, precedence +Information about a random LTL formula and its negation. Note that in the +printed output, it is assumed (to remove some parentheses) that all unary +formula operators have strictly higher precedence than binary operators. + +@item +Information about the B@"uchi automaton that `Implementation 0' generated +from the positive LTL formula (number of states, transitions and acceptance +conditions). + +@item +Information about the synchronous product of the state space and the +B@"uchi automaton constructed from the positive formula. + +@item +Model checking result information. In this case, the automaton cannot reach +an ``accepting cycle'' regardless of the state of the state space in which +the automaton could begin its execution. In other words, the random state +space contains no states with an infinite path beginning from the state such +that the B@"uchi automaton accepts the temporal interpretation of the path +(the infinite sequence of state labels on the path). + +(If the implementation used for translating the positive formula +@samp{((p1 <-> p0) U (p0 \/ ! p3))} into a B@"uchi automaton is known to be +correct, the result would then also indicate that no infinite path in the state +space satisfies the formula.) + +@item +The model checking process is repeated using the negated formula as +input for the LTL-to-B@"uchi translator `Implementation 0'. + +@item +@command{lbtt} performs the model checking result consistency check +(@pxref{Model checking result consistency check}) using the model checking +results computed for the positive and the negative formula. In this case, +the result consistency check fails in 75 states of the state space. This +implies that `Implementation 0' failed to translate one (or both) +of the formulas into a B@"uchi automaton correctly. + +@end enumerate + +The output of phases 4---8 will be repeated for each implementation included in +the tests. After this @command{lbtt} proceeds to the model checking result +cross-comparison test (@pxref{Model checking result cross-comparison test}) and +the B@"uchi automata intersection emptiness test +@ifnottex +(@pxref{B@"uchi automata intersection emptiness check}). +@end ifnottex +@iftex +(@pxref{Automata intersection emptiness check}). +@end iftex + +The model checking result cross-comparison test might result in the following +output: + +@smallformat +@t{ Model checking result cross-comparison:} +@t{ result:} +@t{ failed (+) 0: `Implementation 0', 1: `Implementation 1'} +@end smallformat + +@cindex tests, failure report format +Throughout all test failure reports, @command{lbtt} refers to the positive and +negated formulas with the symbols @samp{+} and @samp{-}, respectively. +Therefore, the above message indicates that the model checking results obtained +using `Implementation 0' and `Implementation 1' for the positive formula do not +agree. A similar line will be shown for all pairs of implementations for which +the test failed. + +@command{lbtt} also reports if the model checking result cross-comparison could +not be performed between a pair of implementations (for example, if one of the +implementations failed to generate an automaton); in this case, the result of +the test is @samp{N/A}. + +@cindex internal model checking algorithm +@cindex tests, against internal model checking algorithm +If using enumerated or randomly generated paths as state spaces, the model +checking results are also compared against those given by +@command{lbtt}'s internal model checking algorithm. In the output, the +internal implementation has the name @samp{lbtt} and no numeric identifier. + +A similar convention is used to report failures in the B@"uchi automata +intersection emptiness check. However, because this test is always performed +on B@"uchi automata constructed from two complementary LTL formulas, a test +failure report shows LTL formula information beside the name of the +implementation used for generating the B@"uchi automaton from that formula. +Note that the B@"uchi automata intersection emptiness check may fail on the +automata constructed by the same implementation; in the following example, +the check failed between the automata constructed by `Implementation 0', and +the automata constructed by `Implementation 0' and `Implementation 1' from +the positive and negative formulas, respectively. + +@smallformat +@t{ B@"uchi automata intersection emptiness check:} +@t{ result:} +@t{ failed 0: `Implementation 0'} +@t{ failed (+) 0: `Implementation 0', (-) 1: `Implementation 1'} +@end smallformat + +If using a log file (@pxref{--logfile,,@samp{--logfile} command line option}), +a summary of all testing errors will be written to the file using the output +format specified above. + + + + + +@node Test statistics, , Test round messages, Interpreting the output +@section Test statistics + +@cindex tests, statistics + +At the end of testing, @command{lbtt} outputs some simple statistics computed +over all tests. If using an error log file +(@pxref{--logfile,,@samp{--logfile} command line option}), the statistics +will be stored also in the log file. These statistics can be also accessed +during interactive testing by using the internal command @samp{statistics} +(@pxref{statistics,,@samp{statistics} command}). +In brief, the statistics include: + +@itemize @bullet +@item +Number of generated state spaces and the total number of states and +transitions in them. + +@item +@cindex operators, computing distribution for +Number of processed LTL formulas (not counting the negations of each +formula). If using random formulas, @command{lbtt} also shows the overall +distribution of each individual proposition, Boolean constant and logical +or temporal operator in the sample of randomly generated formulas. +Theoretically, in a large sample of random formulas, this distribution +should correspond to the one that can be computed before testing by using the +@samp{--showoperatordistribution} command line option +(@pxref{--showoperatordistribution,,@samp{--showoperatordistribution} command line option}). + +@item +Automata statistics for each implementation: + +@itemize @minus +@item +number of generated B@"uchi automata and product automata + +@item +total and average numbers of states, transitions and acceptance sets in the +generated B@"uchi/product automata, and + +@item +total and average time consumed in generating the B@"uchi automata. +@end itemize + +@item +Number of times that each implementation failed to generate an acceptable +automaton from an input formula. + +@item +Number of failures in the model checking result consistency check +(@pxref{Model checking result consistency check}) for each implementation. + +@item +Number of result inconsistencies detected in pairwise comparison of the +B@"uchi automata generated by different implementations. Depending on +the model checking mode and which correctness tests are enabled, the +output may include none, some or all of the following information: + +@itemize @minus +@item +Overall number of failures in the model checking result cross-comparison test +(@pxref{Model checking result cross-comparison test}) for each pair of +implementations. + +@item +Number of failures in the model checking result cross-comparison test in +a single fixed state of each generated state space (called the ``initial'' +state of the state space). + +@item +Number of failures in the B@"uchi automata intersection emptiness check +@iftex +(@pxref{Automata intersection emptiness check}) +@end iftex +@ifnottex +(@pxref{B@"uchi automata intersection emptiness check}) +@end ifnottex +for each pair of implementations. +@end itemize + +Note that the pairwise inconsistency results form a symmetric matrix (although +@command{lbtt} might display the matrix in several parts), which means that +the same information is repeated on both sides of the matrix diagonal. + +@end itemize + +@noindent +Where applicable, the statistics are shown separately for positive, negative +and all LTL formulas used in the tests. + + + + +@node Analyzing test results, Interfacing with @command{lbtt}, Interpreting the output, Top +@chapter Analyzing test results + +This chapter documents how to use @command{lbtt}'s internal commands to +analyze test results. + +To use the internal commands, @command{lbtt} must be started in one of its +interactive modes (@pxref{Interactivity modes}). Depending on the mode, +@command{lbtt} may occasionally pause (either after each test round or only in +case a test failure is detected) between test rounds to wait for user input by +showing a prompt of the form + +@smallexample + ** [Round 22 of 1000] >> +@end smallexample + +@menu +* Command conventions:: Conventions for entering commands. +* Getting help:: Use the @samp{help} command to access + on-line help. +* Test control commands:: Commands for continuing testing, + skipping tests or enabling or disabling + implementations. +* Data display commands:: Commands for displaying information + about B@"uchi automata, state spaces, + and LTL formulas. +* Failure analysis commands:: Commands for analyzing test failures. +@end menu + + + +@node Command conventions, Getting help, Analyzing test results, Analyzing test results +@section Command conventions + +@cindex commands, conventions for entering +@cindex conventions for entering commands + +Commands are entered by typing a command name followed by any parameters for +the command and then pressing @key{ENTER}. +The command names are case-sensitive. Each parameter should be separated from +the command name and other parameters with white space. + +@cindex commands, abbreviating +Command names can be abbreviated to the shortest prefix that identifies the +command unambiguously (for example, @samp{h} could be used in place of the +@samp{help} command). + +@cindex commands, entering lists of numbers +Some of the commands accept or require lists of implementation or state +identifiers as parameters. The lists can be specified as comma-separated +numbers (for example, @samp{8}) or intervals (for example, @samp{3-11}) +with no white space between the commas and the numbers or intervals that +belong to the same list. For example, assuming that the state space used in +the current test round has at least 23 states, the command +@samp{statespace -5,8,14-18,22-} would +display information about all state space states with an identifier less than +or equal to 5, together with information about state 8, states 14 to 18 +(inclusive) and all states with an identifier greater than or equal to 22. The +@samp{*} symbol can be used as a shorthand for all identifiers in the +available range. + +@cindex LTL formula, identifiers in commands +@cindex commands, LTL formula identifiers +Some of the commands require a formula identifier as a parameter for choosing +between a positive and a negative LTL formula. The formula identifier +(@samp{+} for positive formula, @samp{-} for negative formula) must follow +the command name as the first parameter for the command. If the formula +identifier is omitted, the positive formula is assumed. + +@cindex redirecting command output +@cindex commands, redirecting output +The output of most commands (excluding the test control commands, +@pxref{Test control commands}) can be redirected or appended to a file +by ending the command line with @samp{>filename} or @samp{>>filename}, +respectively. + +@cindex commands, invoking external programs +@cindex commands, writing output to a pipe +Optionally, the output can be written to a pipe (to be processed by an external +program) instead of a file. This is done by ending the command line with +@samp{| @var{command}}, where @var{command} is the command line for the +external program intended to process the output produced by @command{lbtt}. +For example, the output of the (@command{lbtt}'s internal) command can be piped +to a pager application if the entire output does not fit on the screen by +itself. Using the pipe construct @emph{without} specifying any internal command +will simply invoke the external program. + + + +@node Getting help, Test control commands, Command conventions, Analyzing test results +@section Getting help + +@cindex commands, getting help +@kindex help +Use the @samp{help} command to access on-line help. Typing @samp{help} with +no parameters shows a list of all available commands, together with +general conventions for using the commands. The @samp{help} command can be +optionally given a command name as a parameter to access command-specific +help. + +In command-specific help, arguments in angle brackets (@samp{<}, @samp{>}) +denote obligatory command parameters, while arguments in square brackets +(@samp{[}, @samp{]}) are optional. A vertical bar (@samp{|}) denotes +selection between several alternatives. Arguments in double quotes should +be entered literally (without the quotes themselves). + + + +@node Test control commands, Data display commands, Getting help, Analyzing test results +@section Test control commands + +@cindex commands, test control +@cindex tests, controlling with user commands +The following commands can be used to continue or abort testing, skip a number +of test rounds, enable or disable implementations for testing, and change the +verbosity of @command{lbtt}'s output messages. + +@table @samp +@item continue @r{[}@var{number-of-rounds}@r{]} +@kindex continue +Continue testing. If no argument is given, testing will be interrupted again +when the current interactivity mode requires it +(@pxref{Interactivity modes}). The optional argument @var{number-of-rounds} +can be used to specify a number of rounds to run; testing is then interrupted +again after the given number of test rounds (or in case of a new +test failure if this is required by the current interactivity mode). + +@item disable @r{[}@var{implementation-id-list}@r{]} +@kindex disable +@cindex disabling LTL-to-B@"uchi translators +@cindex LTL-to-B@"uchi translators, disabling +Disable testing of a list of implementations or all implementations if +no list of implementations is specified. @command{lbtt} will not include these +implementations in the tests in subsequent test rounds. (See +@ref{Command conventions}, for the syntax used for the list of +implementations.) + +@item enable @r{[}@var{implementation-id-list}@r{]} +@kindex enable +@cindex enabling LTL-to-B@"uchi translators +@cindex LTL-to-B@"uchi translators, enabling +Enable testing of a list of implementations. + +@item quit +@kindex quit +@cindex quitting @command{lbtt} +Display test statistics (@pxref{Test statistics}) over the test rounds +performed and then abort testing. + +@item skip @r{[}@var{number-of-rounds}@r{]} +@kindex skip +@cindex tests, skipping test rounds +@cindex skipping test rounds +Skip a number of test rounds and then return to wait for further user input. +If not explicitly specified, the number of rounds to skip defaults to 1. +Use the @samp{--skip} command line option +(@pxref{--skip,,The @samp{--skip} command line option}) to begin +testing from another test round than 1. + +@item verbosity @r{[}@var{verbosity-level}@r{]} +@kindex verbosity +@cindex verbosity, changing +@cindex changing verbosity of output +Display or change the verbosity of @command{lbtt}'s output messages. If no +argument is given, show the current verbosity level, otherwise change the +verbosity setting to the given value. The argument must be an integer between +0 and 5 (inclusive). (The new value will take effect when testing is resumed.) + +@end table + + + +@node Data display commands, Failure analysis commands, Test control commands, Analyzing test results +@section Data display commands + +The following commands can be used to access test result information and to +inspect the LTL formulas, B@"uchi automata and the state space used in the +current test round. + +@table @samp +@item algorithms +@kindex algorithms +Show a list of implementations declared in the program configuration file and +whether they are currently enabled for testing. The list also shows the +numeric identifiers of the implementations. These identifiers +should be used with commands that accept or require implementation identifier +lists as arguments. + +@item buchi @r{[}+ @r{|} -@r{]} @var{implementation-id} @r{[}@var{state-id-list} | dot@r{]} +@kindex buchi +Display information about the structure of the B@"uchi automaton generated by +the implementation @var{implementation-id} from the positive (@samp{+}) or +negative (@samp{-}) LTL formula used in the current test round. The +implementation identifier may be optionally followed by a list of state +identifiers to display only certain states of the automaton (see +@ref{Command conventions}, for details on how the list should be formatted), +or the keyword @samp{dot} to display the automaton in a format that can be +given as input for the @samp{dot} tool of the +@cindex GraphViz +GraphViz graph visualization package +@ifnottex +@ref{[GViz]} +@end ifnottex +@iftex +[GViz] +@end iftex +to obtain a graphical representation of the automaton. + +@item evaluate @r{[}+ @r{|} -@r{]} @r{[}@var{implementation-id-list}@r{]} @r{[}@var{state-id-list}@r{]} +@kindex evaluate +Display the model checking results for the positive (@samp{+}) or the negative +(@samp{-}) formula computed using a given set of implementations for +constructing a B@"uchi automaton from the formula. If no implementation list +is specified, show the results for all implementations. The implementation +identifier list may optionally be followed by a list of (state space) state +identifiers to restrict the output to only a subset of all states. (See +@ref{Command conventions}, for more information about the format used for the +lists.) + +This command can be used to look for states in which the model checking result +cross-comparison test (@pxref{Model checking result cross-comparison test}) +failed for a pair of implementations. These state identifiers can then be used +as input for the @samp{resultanalysis} command +(@pxref{Failure analysis commands}). + +Note 1: Observe that the model checking results shown do not follow the +``traditional'' semantics of LTL, by which a formula is usually considered to +hold in a set of infinite paths beginning from a state only if @emph{all} +paths in the set are accepted by the B@"uchi automaton constructed from the +formula to be model checked. Instead, @command{lbtt} will mark the result as +true if even @emph{one} of these paths is accepted by the automaton. + +Note 2: If using random or enumerated paths as state spaces, @command{lbtt} +accepts also the symbol @samp{p} in the implementation identifier list. This +symbol can be used for accessing the model checking results computed using +@command{lbtt}'s internal ``path'' model checking algorithm. + +@item formula @r{[}+ @r{|} -@r{]} +@kindex formula +@cindex LTL formula, displaying with user command +Display the positive (@samp{+}) or the negative (@samp{-}) LTL formula used +for tests in the current test round. + +@item inconsistencies @r{[}@var{implementation-id-list}@r{]} +@kindex inconsistencies +List the state space states in which the model checking result consistency +check (@pxref{Model checking result consistency check}) failed for each +implementation in the list (or all implementations if the list is omitted). +See @ref{Command conventions}, for information on formatting the list. +The state identifiers can then be used as input for the +@samp{consistencyanalysis} command (@pxref{Failure analysis commands}). + +@item results @r{[}@var{implementation-id-list}@r{]} +@kindex results +Display test results for each implementation in the list (or all +implementations if the list is omitted) in a format very similar to test +messages shown when running @command{lbtt} in its default verbosity mode 3. +For more information about the output, see @ref{Test round messages}; see +@ref{Command conventions}, for information on how to specify the +implementations. + +@item statespace @r{[}@var{state-id-list} @r{|} dot@r{]} +@kindex statespace +@cindex state space, displaying with an user command +Display information about the structure of the state space used for model +checking tests in the current test round. The optional @var{state-id-list} +can be used to display only a part of the whole state space (see +@ref{Command conventions}, for information on formatting the state list). +Alternatively, the @samp{dot} keyword can be used to output the state space +description in a format recognized by the @samp{dot} tool of +@cindex GraphViz +the GraphViz graph visualization package +@ifnottex +@ref{[GViz]} +@end ifnottex +@iftex +[GViz] +@end iftex +to obtain a graphical representation of the state space. + +@item @anchor{statistics}statistics +@kindex statistics +Display statistics computed over all test rounds performed since the program +was started. This is the same information that @command{lbtt} normally outputs +at the end of testing; see @ref{Test statistics}, for more information about +the output that is displayed. + +@end table + + + +@node Failure analysis commands, , Data display commands, Analyzing test results +@section Failure analysis commands + +The first part of this section introduces the commands available for +identifying an LTL-to-B@"uchi translator that caused a failure in one of the +automata correctness tests. The second part describes the conventions that +@command{lbtt} uses for justifying the result of the analysis. + +@subsection Alphabetical list of failure analysis commands + +@table @samp +@item buchianalysis @var{implementation-id} @var{implementation-id} +@kindex buchianalysis +@cindex B@"uchi automata intersection emptiness check, failure analysis +@cindex tests, failure analysis +@cindex failure analysis, B@"uchi automata intersection check +@cindex analyzing test failures, B@"uchi automata intersection emptiness check +Analyze a failure in the B@"uchi automata intersection emptiness check +@ifnottex +(@pxref{B@"uchi automata intersection emptiness check}). +@end ifnottex +@iftex +(@pxref{Automata intersection emptiness check}). +@end iftex +The two implementation identifiers select the B@"uchi automata for which +to perform the analysis. The B@"uchi automata intersection emptiness +check always involves automata constructed from the positive and the negative +formulas used in the current test round. The first implementation identifier +chooses an implementation that constructed an automaton from the positive +formula, and the second identifier selects an implementation used for +translating the negative formula into an automaton. (The identifiers can also +be equal if one of the tested implementations failed the check against itself.) + +@cindex witness +A failure in the B@"uchi automata intersection emptiness check implies that +there exists an input sequence over subsets of atomic propositions that is +accepted by both automata included in the analysis. @command{lbtt} examines the +intersection of the automata to find a witness of such an input, model +checks the positive formula in the witness, and tells which one of the automata +is likely to be incorrect according to the following rules: + +@itemize @bullet +@item +If the positive formula is found to hold in the witness, the automaton +constructed from the negative formula is likely to contain an error. + +@item +If the witness is not a model for the positive formula, then the automaton +constructed from the positive formula probably accepts the witness incorrectly. +@end itemize + +@item consistencyanalysis @var{implementation-id} @r{[}@var{state-id}@r{]} +@kindex consistencyanalysis +@cindex model checking result consistency check, failure analysis +@cindex tests, failure analysis +@cindex failure analysis, model checking result consistency check +@cindex analyzing test failures, model checking result consistency check +Analyze a failure in the model checking result consistency check +(@pxref{Model checking result consistency check}). The +@var{implementation-id} parameter chooses the implementation to analyze. +In addition, the optional @var{state-id} parameter can be used to specify a +state space state in which to perform the analysis (use the +@samp{inconsistencies} command, @ref{Data display commands}, to see a list +of all state space states in which the check failed). If the state identifier +is not present, @command{lbtt} will try to find a state where +the check failed. + +@cindex witness +A failure in the model checking result consistency check implies the existence +of a witness (i.e., a path in the state space used for the tests in the +current test round) whose temporal interpretation is not accepted by either of +two automata constructed from two complementary LTL formulas. In the analysis, +@command{lbtt} finds such a witness, model checks the positive formula in the +witness separately, and tells which one of the automata seems to reject the +witness incorrectly. + +@item resultanalysis @r{[}+ @r{|} -@r{]} @var{implementation-id} @var{implementation-id} @r{[}@var{state-id}@r{]} +@kindex resultanalysis +@cindex model checking result cross-comparison test, failure analysis +@cindex tests, failure analysis +@cindex failure analysis, model checking result cross-comparison test +@cindex analyzing test failures, model checking result cross-comparison test +Analyze a failure in the model checking result cross-comparison test +(@pxref{Model checking result cross-comparison test}) between two +implementations on either the positive (@samp{+}) or the negative +(@samp{-}) LTL formula used in the current test round. The implementation +identifiers can be optionally followed by a state space state identifier +to specify a state in which the analysis should be performed. (Suitable +state identifiers can be found by looking for inconsistencies in the model +checking results accessible with the @samp{evaluate} command, +@ref{Data display commands}; by omitting the state identifier, +@command{lbtt} will try to find a state in which the model +checking result comparison failed between the implementations.) + +If using randomly generated or enumerated paths as state spaces, @command{lbtt} +also accepts the symbol @samp{p} in place of either of the implementation +identifiers. This instructs @command{lbtt} to perform the analysis against +@command{lbtt}'s internal model checking algorithm. + +@cindex witness +A failure in the model checking result cross-comparison test suggests that +the state space used in the current test round contains a path which is +accepted by one, but rejected by another automaton constructed from the same +LTL formula. To determine which one of these automata accepts or rejects the +input incorrectly, @command{lbtt} finds a witness path giving contradictory +model checking results, model checks the formula separately in the witness, +and tells which one of the automata seems to accept or reject the witness +incorrectly. + +@end table + +@subsection Witnesses, proofs and refutations + +@cindex witness +All of the above analysis commands use @command{lbtt}'s internal model +checking algorithm to determine which one of the two automata involved in +each test is incorrect by checking whether an LTL formula holds in a witness +path extracted from the state space used in the current test round or from the +intersection of two B@"uchi automata. The witness path is a linear sequence of +states that ends in a loop, and is represented in two parts as an initial +``prefix'' (which may be empty) and a ``cycle'' that is considered to repeat +itself indefinitely. The witness might look as follows: + +@smallexample + Execution M: + prefix: < s0 @{p0, p4@}, + s2 @{p1, p2, p3, p4@} > + cycle: < s34 @{p0, p1, p2, p3, p4@}, + s42 @{p4@}, + s44 @{p1, p4@} > +@end smallexample + +@noindent +In this case, the witness (or ``execution'' as displayed in the output) +@math{M} consists of a prefix of two states followed by a cycle of three +states. Each state is associated with a set of atomic propositions that hold +in the state. + +(The witness can be considered a small state space +@iftex +@tex +$M = \langle S, \rho, {\cal L} \rangle$ +@end tex +@end iftex +@ifnottex +@math{M = } +@end ifnottex +following the definition in @ref{State spaces}; in the example above, +@iftex +@tex +$S = \{s_0, s_2, s_{34}, s_{42}, s_{44}\}$, +@end tex +@end iftex +@ifnottex +@math{S = @{s0, s2, s34, s42, s44@}}, +@end ifnottex +@iftex +@tex +$\rho = \{(s_0, s_2), (s_2, s_{34}), (s_{34}, s_{42}), (s_{42}, s_{44}), + (s_{44}, s_{34})\}$, +@end tex +@end iftex +@ifnottex +@math{R = @{(s0, s2), (s2, s34), (s34, s42), (s42, s44), (s44, s34)@}}, +@end ifnottex +@iftex +@tex +${\cal L}(s_0) = \{p_0, p_4\}, {\cal L}(s_2) = \{p_1, p_2, p_3, p_4\}, + {\cal L}(s_{34}) = \{p_0, p_1, p_2, p_3, p_4\}, {\cal L}(s_{42}) = \{p_4\},$ + and ${\cal L}(s_{44}) = \{p_1, p_4\}$.) +@end tex +@end iftex +@ifnottex +@math{L(s0) = @{p0, p4@}, L(s2) = @{p1, p2, p3, p4@}, L(s34) = @{p0, p1, p2, p3, p4@}, L(s42) = @{p4@},} and @math{L(s44) = @{p1, p4@}}.) +@end ifnottex + + +In the model checking result cross-comparison test and the model checking +result consistency check, the witness is an actual path extracted from the +state space used for the tests in the current test round. In this case, the +state identifiers correspond to the states of the state space, and can be +accessed with the @samp{statespace @r{[}@var{state-id}@r{]}} command +(@pxref{Data display commands}). + +@cindex proof for an LTL formula +@cindex refutation for an LTL formula +To justify the result of the analysis, @command{lbtt} also displays a proof +or a refutation for the LTL formula in the witness. The proof or refutation is +constructed by a recursive examination of the subformulas of the (positive or +negative) formula used in the current test round according to the semantics of +LTL and might look as follows: + +@smallexample + Analysis of the formula in the execution: + M, |== ((p1 U p4) <-> (! p1 -> [] p4)) : + +-> M, |== (p1 U p4) : + | +-> M, |== p4 + +-> M, |== (! p1 -> [] p4) : + +-> M, |== [] p4 : + +-> M, |== p4 + +-> s0 --> s2 + +-> M, |== p4 + +-> s2 --> s34 + +-> M, |== p4 + +-> s34 --> s42 + +-> M, |== p4 + +-> s42 --> s44 + +-> M, |== p4 + +-> s44 --> s34 +@end smallexample + +@noindent +The proof (or refutation) can be considered a tree of statements of the form +@samp{M, |== @var{subformula}} or +@samp{M, |/= @var{subformula}}. Here, the symbol @samp{|==} is used +to denote that the formula @var{subformula} holds in the (infinite) +subsequence beginning at state @samp{s} of the witness, and the relational +symbol @samp{|/=} denotes the opposite. The children of each proof tree node +give justification for the claim in their parent node; the children might be +further expanded if the claims in them do not directly follow from the +definition of @math{M}. In the presence of temporal operators, the proofs may +need to be based also on the structural properties of @math{M}. These are +shown as statements of the form @samp{sn --> sm} to indicate that @math{M} +contains a transition from the state @samp{sn} to the state @samp{sm} (and, +since the states in @math{M} are connected into a linear sequence, that this +is the @emph{only} transition originating from @samp{sn}). + +In the above example, @command{lbtt} claims that the formula +@samp{((p1 U p4) <-> (! p1 -> [] p4))} holds in the witness presented earlier +in this section, and that this follows (by the semantics of logical +equivalence) from the claims that the subformulas @samp{(p1 U p4)} and +@samp{(! p1 -> [] p4)} both hold in the same witness. +@samp{(p1 U p4)} holds in the witness because of the fact that +@iftex +@tex +$p_4 \in {\cal L}(s_0)$, +@end tex +@end iftex +@ifnottex +@math{p4} is included in @math{L(s0)}, +@end ifnottex +and the truth of the implication @samp{(! p1 -> [] p4)} is justified by the +property that +@iftex +@tex +$p_4$ +@end tex +@end iftex +@ifnottex +@math{p4} +@end ifnottex +holds in all states of the (only) infinite sequence beginning at the state +@iftex +@tex +$s_0$, +@end tex +@end iftex +@ifnottex +s0, +@end ifnottex +which can be seen from the proof. + + + +@node Interfacing with @command{lbtt}, The @command{lbtt-translate} utility, Analyzing test results, Top +@chapter Interfacing LTL-to-B@"uchi translators with @command{lbtt} + +@cindex LTL-to-B@"uchi translators, interfacing with +@cindex interfacing LTL-to-B@"uchi translators with @command{lbtt} + +This chapter gives the details on how to use @command{lbtt} for +testing LTL-to-B@"uchi translation algorithm implementations that are not +supported by the basic distribution. (See +@ifnottex +@ref{The @command{lbtt-translate} utility} +@end ifnottex +@iftex +the next chapter +@end iftex +for information on how to connect several publicly available +LTL-to-B@"uchi translator implementations to @command{lbtt}.) + +@menu +* Translator interface:: @command{lbtt}'s requirements for an + LTL-to-B@"uchi translator. +* Format for LTL formulas:: How @command{lbtt} passes LTL formulas + to the translators. +* B@"uchi automata:: How @command{lbtt} expects the translators + to present their output. +@end menu + + +@node Translator interface, Format for LTL formulas, Interfacing with @command{lbtt}, Interfacing with @command{lbtt} +@section Translator interface requirements + +@cindex LTL-to-B@"uchi translators, interface requirements +@command{lbtt} assumes each tested LTL-to-B@"uchi translator to be +accessible by running an executable file which should read in an LTL +formula from a file, convert it into a B@"uchi automaton and then +write the automaton into another file. For this purpose, the executable +should support the following command line interface: + +@example +@var{path-to-program} @var{parameters} @var{input-file} @var{output-file} +@end example + +@noindent +where @var{path-to-program} is the full name (and location) of the +executable, @var{parameters} are any optional parameters that might be needed +for running the executable, and @var{input-file} and +@var{output-file} are two file names. +The translator executable should read its input (an LTL formula) from +@var{input-file} and write its output (a B@"uchi automaton) into +@var{output-file} (without removing the input file); see +@ifnottex +@ref{Format for LTL formulas} and @ref{B@"uchi automata} +@end ifnottex +@iftex +the following two sections +@end iftex +for a description on how these files should be formatted. + +The translator executable should always create an output file and then return +with a zero exit status in case no errors occur during the translation. +@command{lbtt} interprets a missing output file or any non-zero exit status as +an error and will not in this case try to run any tests, even if +an automaton were successfully saved in an output file. + +To start testing the translator, add a new @samp{Algorithm} section for it into +@command{lbtt}'s configuration file (@pxref{Configuration file}), for example + +@smallexample +Algorithm +@{ + Name = "LTL-to-B@"uchi translator" + Path = "~/bin/ltl-to-buchi-translator" + Parameters = "-x -y -z" + Enabled = Yes +@} +@end smallexample + + + +@node Format for LTL formulas, B@"uchi automata, Translator interface, Interfacing with @command{lbtt} +@section Input file format for LTL formulas + +@cindex LTL-to-B@"uchi translators, LTL formula input file format +@cindex LTL formula, LTL-to-B@"uchi translator input file format +@cindex file formats, LTL-to-B@"uchi translator input file +@command{lbtt} passes each LTL formula to each LTL-to-B@"uchi translator +in a file containing an LTL formula in a prefix notation followed by a +single newline. The precise grammar for the LTL formulas (in a BNF-style +notation) is as follows: + +@smallexample +@var{formula} @r{::=} `t' + @r{// ``true''} + @r{|} `f' + @r{// ``false''} + @r{|} `p'@r{[}0@r{---}9@r{]+} + @r{// atomic proposition with} + @r{// a nonnegative integer} + @r{// identifier} + @r{|} `!' @var{sp} @var{formula} + @r{// negation} + @r{|} `X' @var{sp} @var{formula} + @r{// ``next time''} + @r{|} `F' @var{sp} @var{formula} + @r{// ``finally''} + @r{|} `G' @var{sp} @var{formula} + @r{// ``globally''} + @r{|} `&' @var{sp} @var{formula} @var{sp} @var{formula} + @r{// conjunction} + @r{|} `|' @var{sp} @var{formula} @var{sp} @var{formula} + @r{// disjunction} + @r{|} `i' @var{sp} @var{formula} @var{sp} @var{formula} + @r{// implication} + @r{|} `e' @var{sp} @var{formula} @var{sp} @var{formula} + @r{// equivalence} + @r{|} `^' @var{sp} @var{formula} @var{sp} @var{formula} + @r{// exclusive or} + @r{|} `U' @var{sp} @var{formula} @var{sp} @var{formula} + @r{// ``(strong) until''} + @r{|} `V' @var{sp} @var{formula} @var{sp} @var{formula} + @r{// ``(weak) release''} + @r{|} `W' @var{sp} @var{formula} @var{sp} @var{formula} + @r{// ``weak until''} + @r{|} `M' @var{sp} @var{formula} @var{sp} @var{formula} + @r{// ``strong release''} + @r{|} `B' @var{sp} @var{formula} @var{sp} @var{formula} + @r{// ``before''} + +@end smallexample +@noindent +(The quoted characters denote the characters themselves; @var{sp} denotes +any nonempty string of white space. Lines containing a // are comments and +are not part of the grammar. All atomic propositions in the formula have a +nonnegative numeric identifier.) + +@noindent +For example, the LTL formula +@iftex +@tex +$(p_0\;{\bf U}\;p_1)\rightarrow({\bf F}\;{\bf G}(\neg p_2\leftrightarrow p_3))$ +@end tex +@end iftex +@ifnottex +(in @command{lbtt}'s infix syntax) +@example +(p0 U p1) -> (<> [] (! p2 <-> p3)) +@end example +@end ifnottex +@noindent +would be expressed in the form +@example +i U p0 p1 F G e ! p2 p3 +@end example +@noindent +in an output file. + +If your translator does not support all of the above operators, edit +the configuration file (@pxref{Configuration file}) or use the command +line options (@pxref{Command line options}) to prevent +@command{lbtt} from generating random LTL formulas with these operators. + + + +@node B@"uchi automata, , Format for LTL formulas, Interfacing with @command{lbtt} +@section Output file format for B@"uchi automata + +@cindex file formats, LTL-to-B@"uchi translator output file +@cindex LTL-to-B@"uchi translators, automaton output file format +@cindex B@"uchi automata, LTL-to-B@"uchi translator output file format +@cindex generalized B@"uchi automata, LTL-to-B@"uchi translator output file format +@command{lbtt} expects the B@"uchi automata generated by each LTL-to-B@"uchi +translator implementation to be in the format specified below. The format +encodes a generalized B@"uchi automaton (a B@"uchi automaton with zero or +more acceptance conditions) with a single initial state and labels on +transitions. For the full formal definition and some general guidelines on how +to convert your automata to support the definition, see @ref{Definitions}. + +The output file generated by the translator should contain an @var{automaton} +described using the following grammar: + +@smallexample +@var{automaton} @r{::=} @var{number-of-states} @var{sp} @var{number-of-acceptance-conditions} @var{states} + +@var{number-of-states} @r{::=} @r{[}0@r{---}9@r{]+} + +@var{number-of-acceptance-conditions} @r{::=} @r{[}0@r{---}9@r{]+} + +@var{states} @r{::=} @var{states} @var{sp} @var{state} + @r{|} @r{// empty} + +@var{state} @r{::=} @var{state-id} @var{sp} @var{initial?} @var{acceptance-conditions} @var{sp} `-1' @var{transitions} @var{sp} `-1' + +@var{state-id} @r{::=} @r{[}0@r{---}9@r{]+} + +@var{initial?} @r{::=} `0' @r{|} `1' + +@var{acceptance-conditions} @r{::=} @var{acceptance-conditions} @var{sp} @var{acceptance-set-id} + @r{|} @r{// empty} + +@var{acceptance-set-id} @r{::=} @r{[}0@r{---}9@r{]+} + +@var{transitions} @r{::=} @var{transitions} @var{sp} @var{transition} + @r{|} @r{// empty} + +@var{transition} @r{::=} @var{state-id} @var{sp} @var{guard-formula} + +@var{guard-formula} @r{::=} `t' + @r{// ``true''} + @r{|} `f' + @r{// ``false''} + @r{|} `p'@r{[}0@r{---}9@r{]+} + @r{// atomic proposition} + @r{|} `!' @var{sp} @var{guard-formula} + @r{// negation} + @r{|} `&' @var{sp} @var{guard-formula} @var{sp} @var{guard-formula} + @r{// conjunction} + @r{|} `|' @var{sp} @var{guard-formula} @var{sp} @var{guard-formula} + @r{// disjunction} + @r{|} `i' @var{sp} @var{guard-formula} @var{sp} @var{guard-formula} + @r{// implication} + @r{|} `e' @var{sp} @var{guard-formula} @var{sp} @var{guard-formula} + @r{// equivalence} + @r{|} `^' @var{sp} @var{guard-formula} @var{sp} @var{guard-formula} + @r{// exclusive or} +@end smallexample +@noindent +(The quoted characters denote the characters themselves; @var{sp} denotes any +non-empty string of white space. Lines containing a // are comments and are not +part of the grammar.) + +The automaton description begins with two nonnegative numbers, the first one +of which gives the number of states in the automaton, and the second one +tells the number of acceptance conditions in the automaton. If the number of +states is 0, the automaton will not accept any input. If the number of +acceptance conditions is 0, the automaton accepts an input word if and only if +it has a run on that word according to the definition given in the +Appendix (@pxref{Definitions}). + +If the number of states is greater than zero, the acceptance condition count +should be followed by a list of states. The description of each state begins +with a numeric state identifier, which can be any nonnegative integer. The +state identifier should be followed by a number telling whether the state is +initial (@samp{1} if yes). The automaton should have exactly one initial state. +This number is then followed by a list of the identifiers of the acceptance +conditions to which the state belongs. This list should be terminated with +@samp{-1}. + +The state and acceptance condition identifiers need not be successive, and the +states or acceptance conditions can be listed in any order. The only +restrictions are that the identifiers of different states and acceptance +conditions should be unique and that the total number of different identifiers +should equal @var{number-of-states} or @var{number-of-acceptance-conditions}, +respectively. (The same identifiers can be shared between states and acceptance +conditions, however.) + +Finally, the acceptance condition list of each state should be followed by a +list of transitions (terminated again by @samp{-1}). Each transition consists +of a state identifier (the target state of the transition) and a propositional +formula that encodes the symbols of the alphabet +@iftex +@tex +$2^{AP}$ +@end tex +@end iftex +@ifnottex +@math{2^AP} +@end ifnottex +(where @var{AP} is a finite set of atomic propositions) on which the +automaton is allowed to take the transition. + +Note that the output file should always contain a valid automaton description +if the LTL-to-B@"uchi translation was successful, even in the case that the +resulting automaton is empty (@command{lbtt} interprets a missing automaton +description file as an error). + +The following example illustrates the file format. + +@smallexample +6 2 @r{// an automaton with six states and two acceptance conditions} +0 1 -1 @r{// state 0: initial state, no acceptance conditions} +2 p1 @r{// transition to state 2, guard} @samp{p1} +5 p2 @r{// transition to state 5, guard} @samp{p2} +15 p3 @r{// transition to state 15, guard} @samp{p3} +-1 @r{// end of state 0} +2 0 1 -1 @r{// state 2: non-initial state, acceptance condition 1} +2 p1 @r{// transition to state 2, guard} @samp{p1} +5 p2 @r{// transition to state 5, guard} @samp{p2} +15 p3 @r{// transition to state 15, guard} @samp{p3} +-1 @r{// end of state 2} +5 0 0 -1 @r{// state 5: non-initial state, acceptance condition 0} +5 p2 @r{// transition to state 5, guard} @samp{p2} +8 & p1 p2 @r{// transition to state 8, guard} @samp{p1 /\ p2} +12 & p1 p3 @r{// transition to state 12, guard} @samp{p1 /\ p3} +15 p3 @r{// transition to state 15, guard} @samp{p3} +-1 @r{// end of state 5} +8 0 0 -1 @r{// state 8: non-initial state, acceptance condition 0} +5 p2 @r{// transition to state 5, guard} @samp{p2} +8 & p1 p2 @r{// transition to state 8, guard} @samp{p1 /\ p2} +12 & p1 p3 @r{// transition to state 12, guard} @samp{p1 /\ p3} +15 p3 @r{// transition to state 15, guard} @samp{p3} +-1 @r{// end of state 8} +15 0 1 0 -1 @r{// state 15: non-initial state, acceptance conditions 1 and 0} +2 p1 @r{// transition to state 2, guard} @samp{p1} +5 p2 @r{// transition to state 5, guard} @samp{p2} +15 p3 @r{// transition to state 15, guard} @samp{p3} +-1 @r{// end of state 15} +12 0 1 0 -1 @r{// state 12: non-initial state, acceptance conditions 1 and 0} +2 p1 @r{// transition to state 2, guard} @samp{p1} +5 p2 @r{// transition to state 5, guard} @samp{p2} +15 p3 @r{// transition to state 15, guard} @samp{p3} +-1 @r{// end of state 12} +@end smallexample + + + +@node The @command{lbtt-translate} utility, References, Interfacing with @command{lbtt}, Top +@section The @command{lbtt-translate} utility + +@cindex @command{lbtt-translate} (executable file) +@cindex LTL-to-B@"uchi translators, interfacing with +@cindex interfacing LTL-to-B@"uchi translators with @command{lbtt} +The @command{lbtt} source distribution includes a small utility which can be +used as a common interface for the following publicly available LTL-to-B@"uchi +translator algorithm implementations: + +@itemize @bullet +@item +@cindex @command{lbt} +@command{lbt} --- a free LTL-to-B@"uchi translation algorithm implementation +based on the algorithm described in +@ifnottex +@ref{[GPVW95]}. +@end ifnottex +@iftex +[GPVW95]. +@end iftex +See +@ifinfo +@url{http://www.tcs.hut.fi/Software/maria/tools/lbt/} +@end ifinfo +@ifnotinfo +<@uref{http://www.tcs.hut.fi/Software/maria/tools/lbt/}> +@end ifnotinfo +for more information, including the source code of the implementation. + +@item +@cindex SPIN +@ifnottex +SPIN @ref{[Hol97]} +@end ifnottex +@iftex +@tex +\sc{Spin} +@end tex +[Hol97] +@end iftex +--- a model checking tool +that includes a module for translating LTL formulas into B@"uchi automata +originally based on the algorithm presented in +@ifnottex +@ref{[GPVW95]}. +@end ifnottex +@iftex +[GPVW95]. +@end iftex +See +@ifinfo +@url{http://netlib.bell-labs.com/netlib/spin/whatispin.html} +@end ifinfo +@ifnotinfo +<@uref{http://netlib.bell-labs.com/netlib/spin/whatispin.html}> +@end ifnotinfo +for more information. + +@end itemize + +To use @command{lbtt} for testing the LTL-to-B@"uchi translators included in +these tools, you should first install the tool normally by following its +installation instructions. Then add the following @samp{Algorithm} section in +@command{lbtt}'s configuration file: + +@smallexample +Algorithm +@{ + Name = "@var{name for the implementation}" + Path = "@var{path-to-@command{lbtt-translate}} @var{implementation-selector} @var{path-to-executable}" + Enabled = Yes +@} +@end smallexample + +@noindent +where @var{path-to-@command{lbtt-translate}} contains the complete path and +file name of the @command{lbtt-translate} tool executable, +@var{implementation-selector} is either of the options @samp{--lbt} or +@samp{--spin}, and @var{path-to-executable} is the full path +of the tool executable. The names of these executables are usually (assuming +a normal installation) @command{lbt} and @command{spin}, respectively. + +Note: These implementations may not have built-in support for all of the +LTL formula operators available for generating random LTL formulas with +@command{lbtt}. See the documentation of each translator for information +about which operators are supported, and then change the parameters in +@command{lbtt}'s configuration file accordingly to disable the unsupported +operators. + +The @command{lbtt-translate} utility can also be invoked directly from the +shell to translate an LTL formula into a B@"uchi automaton using either of the +above translators. Use the command @command{lbtt-translate --help} to +see a short summary of available options. + + + +@node References, Definitions, The @command{lbtt-translate} utility, Top +@unnumbered References + +@table @asis +@item @anchor{[CGP99]} [CGP99] +E.@: Clarke Jr., O.@: Grumberg and D.@: Peled. Model checking. The MIT Press, +1999. + +@item @anchor{[Cou99]} [Cou99] +J-M. Couvreur. On-the-fly verification of linear temporal logic. In +@i{Proceedings of the World Congress on Formal Methods in the Development of +Computing Systems (FM'99), volume I}, volume 1708 of +@i{Lecture Notes in Computer Science}, pages 253---271. Springer-Verlag, 1999. + +@item @anchor{[DGV99]} [DGV99] +M.@: Daniele, F.@: Giunchiglia and M.@: Y.@: Vardi. Improved automata +generation for linear temporal logic. In @i{Proceedings of the 11th +International Conference on Computer Aided Verification (CAV'99)}, volume 1633 +of @i{Lecture Notes in Computer Science}, pages 249---260. Springer-Verlag, +1999. + +@item @anchor{[EH00]} [EH00] +K.@: Etessami and G.@: Holzmann. Optimizing B@"uchi automata. In +@i{Proceedings of the 11th International Conference on Concurrency Theory +(CONCUR'2000)}, volume 1877 of @i{Lecture Notes in Computer Science}, +pages 153---167. Springer-Verlag, 2000. + +@item @anchor{[Ete99]} [Ete99] +K.@: Etessami. Stutter-invariant languages, omega-automata, and temporal +logic. In @i{Proceedings of the 11th Conference on Computer Aided Verification +(CAV'99)}, volume 1633 of @i{Lecture Notes in Computer Science}, pages +236---248. Springer-Verlag, 1999. + +@item @anchor{[EWS01]} [EWS01] +K.@: Etessami, Th.@: Wilke and R.@: Schuller. Fair simulation relations, +parity games, and state space reduction for B@"uchi automata. In +@i{Proceedings of the 28th International Colloquium on Automata, Languages and +Programming (ICALP'2001)}, volume 2076 of +@i{Lecture Notes in Computer Science}, pages 694---707. Springer-Verlag, 2001. + +@item @anchor{[GO01]} [GO01] +P.@: Gastin and D.@: Oddoux. Fast LTL to B@"uchi automata translation. +In @i{Proceedings of the 13th International Conference on Computer +Aided Verification (CAV'01)}, volume 2102 of @i{Lecture Notes in Computer +Science}, pages 53---65. Springer-Verlag, 2001. + +@item @anchor{[GPVW95]} [GPVW95] +R.@: Gerth, D.@: Peled, M.@: Y.@: Vardi and P.@: Wolper. Simple on-the-fly +automatic verification of linear temporal logic. In +@i{Proceedings of 15th Workshop Protocol Specification, Testing, and +Verification (PSTV'95)}, pages 3---18. Chapman & Hall, 1995. + +@item @anchor{[GViz]} [GViz] +GraphViz - open source graph drawing software. See +@ifinfo +@url{http://www.research.att.com/sw/tools/graphviz/}. +@end ifinfo +@ifnotinfo +<@uref{http://www.research.att.com/sw/tools/graphviz/}>. +@end ifnotinfo + +@item @anchor{[Hol97]} [Hol97] +G. Holzmann. The model checker +@ifnottex +SPIN. +@end ifnottex +@iftex +@tex +\sc{Spin}. +@end tex +@end iftex +@i{IEEE Transactions on Software Engineering}, 23(5):279---295, 1997. + +@item @anchor{[SB00]} [SB00] +F.@: Somenzi and R.@: Bloem. Efficient B@"uchi automata from LTL formulae. +In @i{Proceedings of the 12th International Conference on Computer Aided +Verification (CAV'00)}, volume 1855 of @i{Lecture Notes in Computer Science}, +pages 247---263. Springer-Verlag, 2000. + +@item @anchor{[Tau00]} [Tau00] +H. Tauriainen. Automated testing of B@"uchi automata translators +for linear temporal logic. Technical report A66, Laboratory for Theoretical +Computer Science, Helsinki University of Technology, Espoo, Finland, +2000. Available on the WWW at +@ifinfo +@url{http://www.tcs.hut.fi/Publications/reports/A66abstract.html} +@end ifinfo +@ifhtml +<@uref{http://www.tcs.hut.fi/Publications/reports/A66abstract.html}>. +@end ifhtml +@iftex +<@url{http://www.tcs.hut.fi/Publications/reports/ A66abstract.html}>. +@end iftex + +@item @anchor{[Var96]} [Var96] +M.@: Y.@: Vardi. An automata-theoretic approach to linear temporal logic. +In @i{Logics for Concurrency: Structure versus Automata}, volume 1043 of +@i{Lecture Notes in Computer Science}, pages 238---265. Springer-Verlag, +1996. + +@item @anchor{[VW86]} [VW86] +M.@: Y.@: Vardi and P.@: Wolper. An automata-theoretic approach to +automatic program verification. In @i{Proceedings of the 1st IEEE +Symposium on Logic in Computer Science (LICS'86)}, pages 332---344. IEEE +Computer Society Press, 1986. + +@end table + + + +@node Definitions, Configuration file option index, References, Top +@appendix Definitions + +This appendix reviews the formal definitions of the objects that @command{lbtt} +manipulates. + +@menu +* LTL formulas:: @command{lbtt} uses traditional semantics + for propositional linear temporal + logic. +* Generalized B@"uchi automata:: The B@"uchi automata used by @command{lbtt} + have one initial state, labels on + transitions and zero or more + acceptance conditions. +* State spaces:: State spaces are Kripke structures + with a total transition relation. +@end menu + + +@node LTL formulas, Generalized B@"uchi automata, Definitions, Definitions +@appendixsec LTL formulas + +@command{lbtt} uses the traditional definition for propositional linear +temporal logic. Let @math{AP} be a finite set of atomic propositions. +The set of propositional linear temporal logic formulas is defined inductively +as follows: + +@itemize @bullet +@item +@cindex @samp{t} (Boolean constant semantics in LTL) +@cindex true (Boolean constant semantics in LTL) +All atomic propositions in @math{AP} and the Boolean constant +@iftex +@tex +\sc{True} +@end tex +@end iftex +@ifnottex +@samp{true} +@end ifnottex +are LTL formulas. + +@item +If +@iftex +@tex +$\varphi$ and $\psi$ +@end tex +@end iftex +@ifnottex +@samp{f1} and @samp{f2} +@end ifnottex +are LTL formulas, then +@iftex +@tex +$\neg\varphi, {\bf X}\varphi, (\varphi\vee\psi)$ and $(\varphi\;{\bf U}\;\psi)$ +@end tex +@end iftex +@ifnottex +@samp{! f1}, @samp{X f1}, @samp{(f1 \/ f2)} and @samp{(f1 U f2)} +@end ifnottex +are LTL formulas. + +@end itemize + +The semantics of linear temporal logic +(i.e., a satisfiability +@iftex +relation, denoted by +@tex +$\models$) +@end tex +@end iftex +@ifnottex +relation) +@end ifnottex +is defined over infinite sequences +@iftex +@tex +$\xi = \langle y_0, y_1, y_2, \ldots \rangle \in (2^{AP})^\omega$ +@end tex +@end iftex +@ifnottex +@math{x = } +@end ifnottex +over subsets of @math{AP} as follows: + +@itemize @bullet +@item +@iftex +@tex +$\xi \models$ \sc{True} for all sequences $\xi$. +@end tex +@end iftex +@ifnottex +@math{x} satisfies @samp{true} for all sequences @math{x}. +@end ifnottex + +@item +@iftex +@tex +$\xi \models p \in {\it AP}$ +@end tex +@end iftex +@ifnottex +@math{x} satisfies an atomic proposition @samp{p} +@end ifnottex +if and only if +@iftex +@tex +$p \in y_0$, +@end tex +@end iftex +@ifnottex +@samp{p} belongs to @math{y(0)}, +@end ifnottex +the first element of the sequence +@iftex +@tex +$\xi$. +@end tex +@end iftex +@ifnottex +@math{x}. +@end ifnottex + +@item +@cindex @samp{!} (operator semantics in LTL) +@cindex negation (operator semantics in LTL) +@iftex +@tex +$\xi \models \neg\varphi$ +@end tex +@end iftex +@ifnottex +@math{x} satisfies @samp{! f} +@end ifnottex +if and only if it is not the case that +@iftex +@tex +$\xi \models \varphi$. +@end tex +@end iftex +@ifnottex +@math{x} satisfies @samp{f}. +@end ifnottex + +@item +@cindex @samp{\/} (operator semantics in LTL) +@cindex @emph{or} (operator semantics in LTL) +@cindex disjunction (operator semantics in LTL) +@iftex +@tex +$\xi \models \varphi\vee\psi$ +@end tex +@end iftex +@ifnottex +@math{x} satisfies @samp{f1 \/ f2} +@end ifnottex +if and only if +@iftex +@tex +$\xi \models \varphi$ +@end tex +@end iftex +@ifnottex +@math{x} satisfies @samp{f1} +@end ifnottex +or +@iftex +@tex +$\xi \models \psi$. +@end tex +@end iftex +@ifnottex +@math{x} satisfies @samp{f2}. +@end ifnottex + +@item +@cindex @samp{X} (operator semantics in LTL) +@cindex next time (operator semantics in LTL) +@iftex +@tex +$\xi \models {\bf X}\varphi$ +@end tex +@end iftex +@ifnottex +@math{x} satisfies @samp{X f} +@end ifnottex +if and only if +@iftex +@tex +$\langle y_1, y_2, y_3, \ldots\rangle \models \varphi$. +@end tex +@end iftex +@ifnottex +@math{} satisfies @samp{f}. +@end ifnottex + +@item +@cindex @samp{U} (operator semantics in LTL) +@cindex strong until (operator semantics in LTL) +@cindex until (operator semantics in LTL) +@iftex +@tex +$\xi \models \varphi\;{\bf U}\;\psi$ +@end tex +@end iftex +@ifnottex +@math{x} satisfies @samp{f1 U f2} +@end ifnottex +if and only if there exists an +@iftex +@tex +$i \geq 0$ +@end tex +@end iftex +@ifnottex +@math{i >= 0} +@end ifnottex +such that +@iftex +@tex +$\langle y_i, y_{i+1},$ $y_{i+2}, \ldots\rangle \models \psi$ +@end tex +@end iftex +@ifnottex +@math{} satisfies @samp{f2} +@end ifnottex +and for all +@iftex +@tex +$0 \leq j < i, \langle y_j, y_{j+1}, y_{j+2}, \ldots \rangle \models \varphi$. +@end tex +@end iftex +@ifnottex +@math{0 <= j < i}, @math{} satisfies @samp{f1}. +@end ifnottex + +@end itemize + +@command{lbtt} also supports the following operators and Boolean constants, +the definitions of which can be given in terms of the previously defined +operators: + +@itemize @bullet +@item +@cindex @samp{f} (Boolean constant semantics in LTL) +@cindex false (Boolean constant semantics in LTL) +``false'': +@iftex +@tex +\sc{False} $\equiv_{\rm def} \neg\!$ \sc{True} +@end tex +@end iftex +@ifnottex +@samp{false} := @samp{! true} +@end ifnottex + +@item +@cindex @samp{/\} (operator semantics in LTL) +@cindex @emph{and} (operator semantics in LTL) +@cindex conjunction (operator semantics in LTL) +logical conjunction: +@iftex +@tex +$(\varphi \wedge \psi) \equiv_{\rm def} \neg(\neg\varphi \vee \neg\psi)$ +@end tex +@end iftex +@ifnottex +@samp{(f1 /\ f2)} := @samp{! (! f1 \/ ! f2)} +@end ifnottex + +@item +@cindex @samp{->} (operator semantics in LTL) +@cindex implication (operator semantics in LTL) +logical implication: +@iftex +@tex +$(\varphi \rightarrow \psi) \equiv_{\rm def} (\neg\varphi \vee \psi)$ +@end tex +@end iftex +@ifnottex +@samp{(f1 -> f2)} := @samp{(! f1 \/ f2)} +@end ifnottex + +@item +@cindex @samp{<->} (operator semantics in LTL) +@cindex equivalence (operator semantics in LTL) +logical equivalence: +@iftex +@tex +$(\varphi \leftrightarrow \psi) \equiv_{\rm def} ((\varphi \rightarrow \psi) \wedge (\psi \rightarrow \varphi))$ +@end tex +@end iftex +@ifnottex +@samp{(f1 <-> f2)} := @samp{((f1 -> f2) /\ (f2 -> f1))} +@end ifnottex + +@item +@cindex @samp{xor} (operator semantics in LTL) +@cindex exclusive or (operator semantics in LTL) +logical ``exclusive or'': +@iftex +@tex +$(\varphi \oplus \psi) \equiv_{\rm def} \neg(\varphi \leftrightarrow \psi)$ +@end tex +@end iftex +@ifnottex +@samp{(f1 xor f2)} := @samp{! (f1 <-> f2)} +@end ifnottex + +@item +@cindex @samp{F} (operator semantics in LTL) +@cindex finally (operator semantics in LTL) +@cindex eventually (operator semantics in LTL) +temporal ``finally'': +@iftex +@tex +${\bf F} \varphi \equiv_{\rm def} ($\sc{True}$\;{\bf U}\;\varphi)$ +@end tex +@end iftex +@ifnottex +@samp{<> f} := @samp{(true U f)} +@end ifnottex + +@item +@cindex @samp{G} (operator semantics in LTL) +@cindex globally (operator semantics in LTL) +@cindex always (operator semantics in LTL) +@cindex henceforth (operator semantics in LTL) +temporal ``globally'': +@iftex +@tex +${\bf G} \varphi \equiv_{\rm def} \neg{\bf F}\neg\varphi$ +@end tex +@end iftex +@ifnottex +@samp{[] f} := @samp{! <> ! f} +@end ifnottex + +@item +@cindex @samp{V} (operator semantics in LTL) +@cindex weak release (operator semantics in LTL) +@cindex release (operator semantics in LTL) +temporal ``(weak) release'': +@iftex +@tex +$(\varphi\;{\bf V}\;\psi) \equiv_{\rm def} \neg(\neg\varphi\;{\bf U}\;\neg\psi)$ +@end tex +@end iftex +@ifnottex +@samp{(f1 V f2)} := @samp{! (! f1 U ! f2)} +@end ifnottex + +@item +@cindex @samp{W} (operator semantics in LTL) +@cindex weak until (operator semantics in LTL) +@cindex unless (operator semantics in LTL) +temporal ``weak until'': +@iftex +@tex +$(\varphi\;{\bf W}\;\psi) \equiv_{\rm def} \big((\varphi\;{\bf U}\;\psi) \vee {\bf G}\varphi\big)$ +@end tex +@end iftex +@ifnottex +@samp{(f1 W f2)} := @samp{((f1 U f2) \/ [] f1)} +@end ifnottex + +@item +@cindex @samp{M} (operator semantics in LTL) +@cindex strong release (operator semantics in LTL) +temporal ``strong release'': +@iftex +@tex +$(\varphi\;{\bf M}\;\psi) \equiv_{\rm def} \big((\varphi\;{\bf V}\;\psi) \wedge {\bf F}\varphi\big)$ +@end tex +@end iftex +@ifnottex +@samp{(f1 M f2)} := @samp{((f1 V f2) /\ <> f1)} +@end ifnottex + +@item +@cindex @samp{B} (operator semantics in LTL) +@cindex before (operator semantics in LTL) +temporal ``before'': +@iftex +@tex +$(\varphi\;{\bf B}\;\psi) \equiv_{\rm def} \neg(\neg\varphi\;\bf{U}\;\psi)$ +@end tex +@end iftex +@ifnottex +@samp{(f1 B f2)} := @samp{! (! f1 U f2)} +@end ifnottex + +@end itemize + + + +@node Generalized B@"uchi automata, State spaces, LTL formulas, Definitions +@appendixsec Generalized B@"uchi automata + +@cindex B@"uchi automata, formal definition +@cindex generalized B@"uchi automata, formal definition + +@command{lbtt} uses internally finite-state automata on infinite words +(B@"uchi automata) over the alphabet +@iftex +@tex +$2^{AP}$ +@end tex +@end iftex +@ifnottex +@math{2^AP} +@end ifnottex +(where @math{AP} is a finite set of atomic propositions) +with one initial state, labels on transitions and zero or more acceptance +conditions. + +@appendixsubsec Formal definition of generalized B@"uchi automata + +Formally, a generalized B@"uchi automaton can be represented as a tuple +@iftex +@tex +$\langle \Sigma, Q, \Delta, q^0, \cal{F}\rangle$, +@end tex +@end iftex +@ifnottex +@math{}, +@end ifnottex +where + +@itemize @bullet +@item +@iftex +@tex +$\Sigma$ +@end tex +@end iftex +@ifnottex +@math{S} +@end ifnottex +is the @emph{alphabet} +@iftex +@tex +($\Sigma = 2^{AP}$ in this case), +@end tex +@end iftex +@ifnottex +(@math{S = 2^AP} in this case), +@end ifnottex + +@item +@math{Q} is the set of @emph{states}, + +@item +@iftex +@tex +$\Delta \subseteq Q \times 2^\Sigma \times Q$ +@end tex +@end iftex +@ifnottex +@math{R} (a subset of @math{Q x 2^S x Q}) +@end ifnottex +is the +@emph{transition relation}, + +@item +@iftex +@tex +$q^0$ +@end tex +@end iftex +@ifnottex +@math{q} +@end ifnottex +is the @emph{initial state}, and + +@item +@iftex +@tex +${\cal F} \subseteq 2^Q$ +@end tex +@end iftex +@ifnottex +@math{F}, a collection of subsets of @math{Q}, +@end ifnottex +is the set of @emph{acceptance conditions}. (A ``nongeneralized'' +B@"uchi automaton has exactly one acceptance condition.) + +@end itemize + +A @emph{run} of a B@"uchi automaton on an infinite sequence +@iftex +@tex +$\langle x_0, x_1, x_2, \ldots \rangle \in (2^{AP})^\omega$ +@end tex +@end iftex +@ifnottex +@math{} +@end ifnottex +over the alphabet +@iftex +@tex +$2^{AP}$ +@end tex +@end iftex +@ifnottex +@math{2^AP} +@end ifnottex +is an infinite sequence of states +@iftex +@tex +$\langle q_0, q_1, q_2, \ldots \rangle \in Q^\omega$ +@end tex +@end iftex +@ifnottex +@math{} (where each @math{q(i)} is a state in @math{Q}) +@end ifnottex +such that +@iftex +@tex +$q_0 = q^0$ +@end tex +@end iftex +@ifnottex +@math{q(0) = q} +@end ifnottex +and for all +@iftex +@tex +$i \geq 0$, +@end tex +@end iftex +@ifnottex +@math{i >= 0}, +@end ifnottex +there is a triple +@iftex +@tex +$\langle q_i, X, q_{i+1}\rangle \in \Delta$ +@end tex +@end iftex +@ifnottex +@math{} in @math{R} +@end ifnottex +such that +@iftex +@tex +$x_i \in X$. +@end tex +@end iftex +@ifnottex +@math{x(i)} belongs to @math{X}. +@end ifnottex +Because +@iftex +@tex +$\Delta$ +@end tex +@end iftex +@ifnottex +@math{R} +@end ifnottex +is not necessarily a function from +@iftex +@tex +$Q \times 2^\Sigma$ +@end tex +@end iftex +@ifnottex +@math{Q x 2^S} +@end ifnottex +to +@iftex +@tex +$Q$, +@end tex +@end iftex +@ifnottex +@math{Q}, +@end ifnottex +the automaton may have many runs on the same input. + +A run +@iftex +@tex +$\langle q_0, q_1, q_2, \ldots\rangle$ +@end tex +@end iftex +@ifnottex +@math{} +@end ifnottex +is @emph{accepting} if and only if additionally +for each acceptance condition +@iftex +@tex +$F_j \in {@cal F}$, +@end tex +@end iftex +@ifnottex +@math{C(j)} in @math{F}, +@end ifnottex +there is a state +@iftex +@tex +$q_j \in F_j$ +@end tex +@end iftex +@ifnottex +@math{q(j)} in @math{C(j)} +@end ifnottex +that occurs infinitely often in the run. The automaton @emph{accepts} an +infinite sequence +@iftex +@tex +$\langle x_0, x_1, x_2, \ldots\rangle \in 2^{AP}$ +@end tex +@end iftex +@ifnottex +@math{} over the alphabet @math{2^AP} +@end ifnottex +if and only if the automaton has at least one accepting run on this sequence. + +@appendixsubsec Transition label encoding + +In practice, a transition label of a B@"uchi automaton can be expressed as a +propositional formula, since these formulas readily encode sets of subsets of +the alphabet +@iftex +@tex +$2^{AP}$, +@end tex +@end iftex +@ifnottex +@math{2^AP}, +@end ifnottex +namely, the models of the formula. A transition can then be seen as a rule +``if in state +@iftex +@tex +$q_i$ +@end tex +@end iftex +@ifnottex +@math{q(i)} +@end ifnottex +and the next input symbol +@iftex +@tex +$x_i$ +@end tex +@end iftex +@ifnottex +@math{x(i)} +@end ifnottex +is a model of the propositional formula guarding the transition, the automaton +can move to state +@iftex +@tex +$q_{i+1}$''. +@end tex +@end iftex +@ifnottex +@math{q(i+1)}''. +@end ifnottex +In the context of B@"uchi automata constructed from LTL formulas, this +often allows for a compact representation for the transitions. + +@appendixsubsec Converting between equivalent definitions + +Many LTL-to-B@"uchi translation algorithms presented in the literature +(for example, +@ifnottex +@ref{[GPVW95]}) +@end ifnottex +@iftex +[GPVW95]) +@end iftex +use a slightly different definition for generalized B@"uchi automata, +which permits a B@"uchi automaton to have several initial states and +places the labels on states instead of transitions. However, these B@"uchi +automata can be easily converted into an equivalent B@"uchi automaton in the +above format with the following steps (we assume here that +each state of the automaton is labelled with a set of LTL formulas that +should hold in that state): + +@enumerate +@item +Add a new state (with an empty label) into the automaton and add transitions +from it to each initial state of the original automaton. Make the new state +the (only) initial state of the automaton. + +@item +For each state of the (modified) automaton, construct a conjunction of all +propositional +constraints (i.e., all formulas with no temporal operators) in the label of +the state and copy the conjunction onto each transition coming into the +state. Then remove all labels from the states. +@end enumerate + +Conversions from other definitions can be handled in a similar way. In +some cases (e.g., if the acceptance conditions are subsets of transitions +instead of subsets of states), the conversion may also require making copies +of some states and then adjusting the transition labels appropriately. + + + +@node State spaces, , Generalized B@"uchi automata, Definitions +@appendixsec State spaces + +@cindex state space, formal definition +@command{lbtt} uses randomly generated state spaces in the +model checking result cross-comparison test +(@pxref{Model checking result cross-comparison test}) and +the model checking result consistency check +(@pxref{Model checking result consistency check}). Formally, +the state spaces are Kripke structures with a total transition +relation, i.e., directed graphs with a set of atomic propositions +attached to each state, with each state having at least one +immediate successor (which may be the state itself). The precise +definition is as follows (as before, let @math{AP} be a +finite set of atomic propositions). + +A state space can be represented as a tuple +@iftex +@tex +$\langle S, \rho, {\cal L}\rangle$, +@end tex +@end iftex +@ifnottex +@math{}, +@end ifnottex +where + +@itemize @bullet + +@item +@math{S} is the set of @emph{states}, + +@item +@iftex +@tex +$\rho \subseteq S \times S$ +@end tex +@end iftex +@ifnottex +@math{R} (a subset of @math{S x S}) +@end ifnottex +is the @emph{transition relation}, and + +@item +@iftex +@tex +${\cal L}: S \rightarrow 2^{AP}$ +@end tex +@end iftex +@ifnottex +@math{L} (a function from @math{S} to @math{2^AP}) +@end ifnottex +is the @emph{labeling function} which maps each state to a set of atomic +propositions that hold in the state. + +@end itemize + + + +@node Configuration file option index, Command line option index, Definitions, Top +@unnumbered Configuration file option index + +@printindex fn + + + +@node Command line option index, User command index, Configuration file option index, Top +@unnumbered Command line option index + +@printindex vr + + + +@node User command index, Concept index, Command line option index, Top +@unnumbered User command index + +@printindex ky + + +@node Concept index, , User command index, Top +@unnumbered Concept index + +@printindex cp + +@bye diff --git a/lbtt/doc/testprocedure.eps b/lbtt/doc/testprocedure.eps new file mode 100644 index 000000000..0d86fb4fc --- /dev/null +++ b/lbtt/doc/testprocedure.eps @@ -0,0 +1,769 @@ +%!PS-Adobe-2.0 EPSF-2.0 +%%Title: testprocedure.eps +%%Creator: fig2dev Version 3.2 Patchlevel 3a +%%CreationDate: Mon Aug 6 16:10:19 2001 +%%For: htauriai@lattice (Heikki Tauriainen,TB349,451 3263,) +%%BoundingBox: 0 0 354 441 +%%Magnification: 1.0000 +%%EndComments +/$F2psDict 200 dict def +$F2psDict begin +$F2psDict /mtrx matrix put +/col-1 {0 setgray} bind def +/col0 {0.000 0.000 0.000 srgb} bind def +/col1 {0.000 0.000 1.000 srgb} bind def +/col2 {0.000 1.000 0.000 srgb} bind def +/col3 {0.000 1.000 1.000 srgb} bind def +/col4 {1.000 0.000 0.000 srgb} bind def +/col5 {1.000 0.000 1.000 srgb} bind def +/col6 {1.000 1.000 0.000 srgb} bind def +/col7 {1.000 1.000 1.000 srgb} bind def +/col8 {0.000 0.000 0.560 srgb} bind def +/col9 {0.000 0.000 0.690 srgb} bind def +/col10 {0.000 0.000 0.820 srgb} bind def +/col11 {0.530 0.810 1.000 srgb} bind def +/col12 {0.000 0.560 0.000 srgb} bind def +/col13 {0.000 0.690 0.000 srgb} bind def +/col14 {0.000 0.820 0.000 srgb} bind def +/col15 {0.000 0.560 0.560 srgb} bind def +/col16 {0.000 0.690 0.690 srgb} bind def +/col17 {0.000 0.820 0.820 srgb} bind def +/col18 {0.560 0.000 0.000 srgb} bind def +/col19 {0.690 0.000 0.000 srgb} bind def +/col20 {0.820 0.000 0.000 srgb} bind def +/col21 {0.560 0.000 0.560 srgb} bind def +/col22 {0.690 0.000 0.690 srgb} bind def +/col23 {0.820 0.000 0.820 srgb} bind def +/col24 {0.500 0.190 0.000 srgb} bind def +/col25 {0.630 0.250 0.000 srgb} bind def +/col26 {0.750 0.380 0.000 srgb} bind def +/col27 {1.000 0.500 0.500 srgb} bind def +/col28 {1.000 0.630 0.630 srgb} bind def +/col29 {1.000 0.750 0.750 srgb} bind def +/col30 {1.000 0.880 0.880 srgb} bind def +/col31 {1.000 0.840 0.000 srgb} bind def + +end +save +newpath 0 441 moveto 0 0 lineto 354 0 lineto 354 441 lineto closepath clip newpath +-11.0 521.0 translate +1 -1 scale + +/cp {closepath} bind def +/ef {eofill} bind def +/gr {grestore} bind def +/gs {gsave} bind def +/sa {save} bind def +/rs {restore} bind def +/l {lineto} bind def +/m {moveto} bind def +/rm {rmoveto} bind def +/n {newpath} bind def +/s {stroke} bind def +/sh {show} bind def +/slc {setlinecap} bind def +/slj {setlinejoin} bind def +/slw {setlinewidth} bind def +/srgb {setrgbcolor} bind def +/rot {rotate} bind def +/sc {scale} bind def +/sd {setdash} bind def +/ff {findfont} bind def +/sf {setfont} bind def +/scf {scalefont} bind def +/sw {stringwidth} bind def +/tr {translate} bind def +/tnt {dup dup currentrgbcolor + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} + bind def +/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul + 4 -2 roll mul srgb} bind def +/reencdict 12 dict def /ReEncode { reencdict begin +/newcodesandnames exch def /newfontname exch def /basefontname exch def +/basefontdict basefontname findfont def /newfont basefontdict maxlength dict def +basefontdict { exch dup /FID ne { dup /Encoding eq +{ exch dup length array copy newfont 3 1 roll put } +{ exch newfont 3 1 roll put } ifelse } { pop pop } ifelse } forall +newfont /FontName newfontname put newcodesandnames aload pop +128 1 255 { newfont /Encoding get exch /.notdef put } for +newcodesandnames length 2 idiv { newfont /Encoding get 3 1 roll put } repeat +newfontname newfont definefont pop end } def +/isovec [ +8#055 /minus 8#200 /grave 8#201 /acute 8#202 /circumflex 8#203 /tilde +8#204 /macron 8#205 /breve 8#206 /dotaccent 8#207 /dieresis +8#210 /ring 8#211 /cedilla 8#212 /hungarumlaut 8#213 /ogonek 8#214 /caron +8#220 /dotlessi 8#230 /oe 8#231 /OE +8#240 /space 8#241 /exclamdown 8#242 /cent 8#243 /sterling +8#244 /currency 8#245 /yen 8#246 /brokenbar 8#247 /section 8#250 /dieresis +8#251 /copyright 8#252 /ordfeminine 8#253 /guillemotleft 8#254 /logicalnot +8#255 /hyphen 8#256 /registered 8#257 /macron 8#260 /degree 8#261 /plusminus +8#262 /twosuperior 8#263 /threesuperior 8#264 /acute 8#265 /mu 8#266 /paragraph +8#267 /periodcentered 8#270 /cedilla 8#271 /onesuperior 8#272 /ordmasculine +8#273 /guillemotright 8#274 /onequarter 8#275 /onehalf +8#276 /threequarters 8#277 /questiondown 8#300 /Agrave 8#301 /Aacute +8#302 /Acircumflex 8#303 /Atilde 8#304 /Adieresis 8#305 /Aring +8#306 /AE 8#307 /Ccedilla 8#310 /Egrave 8#311 /Eacute +8#312 /Ecircumflex 8#313 /Edieresis 8#314 /Igrave 8#315 /Iacute +8#316 /Icircumflex 8#317 /Idieresis 8#320 /Eth 8#321 /Ntilde 8#322 /Ograve +8#323 /Oacute 8#324 /Ocircumflex 8#325 /Otilde 8#326 /Odieresis 8#327 /multiply +8#330 /Oslash 8#331 /Ugrave 8#332 /Uacute 8#333 /Ucircumflex +8#334 /Udieresis 8#335 /Yacute 8#336 /Thorn 8#337 /germandbls 8#340 /agrave +8#341 /aacute 8#342 /acircumflex 8#343 /atilde 8#344 /adieresis 8#345 /aring +8#346 /ae 8#347 /ccedilla 8#350 /egrave 8#351 /eacute +8#352 /ecircumflex 8#353 /edieresis 8#354 /igrave 8#355 /iacute +8#356 /icircumflex 8#357 /idieresis 8#360 /eth 8#361 /ntilde 8#362 /ograve +8#363 /oacute 8#364 /ocircumflex 8#365 /otilde 8#366 /odieresis 8#367 /divide +8#370 /oslash 8#371 /ugrave 8#372 /uacute 8#373 /ucircumflex +8#374 /udieresis 8#375 /yacute 8#376 /thorn 8#377 /ydieresis] def +/Times-Roman /Times-Roman-iso isovec ReEncode +/Times-Italic /Times-Italic-iso isovec ReEncode + /DrawEllipse { + /endangle exch def + /startangle exch def + /yrad exch def + /xrad exch def + /y exch def + /x exch def + /savematrix mtrx currentmatrix def + x y tr xrad yrad sc 0 0 1 startangle endangle arc + closepath + savematrix setmatrix + } def + +/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def +/$F2psEnd {$F2psEnteredState restore end} def + +$F2psBegin +%%Page: 1 1 +10 setmiterlimit + 0.06299 0.06299 sc +/Times-Roman-iso ff 180.00 scf sf +2925 3150 m +gs 1 -1 sc (LTL-to-B\374chi) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +2925 3375 m +gs 1 -1 sc (translator 2) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +15.000 slw +n 2295 2970 m 3555 2970 l 3555 3465 l 2295 3465 l + cp gs col0 s gr +7.500 slw +% Ellipse +n 3847 3195 23 23 0 360 DrawEllipse gs 0.00 setgray ef gr gs col0 s gr + +% Ellipse +n 4027 3195 23 23 0 360 DrawEllipse gs 0.00 setgray ef gr gs col0 s gr + +% Ellipse +n 4207 3195 23 23 0 360 DrawEllipse gs 0.00 setgray ef gr gs col0 s gr + +/Times-Italic-iso ff 180.00 scf sf +2340 2227 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1305 2227 m +gs 1 -1 sc (LTL formula) col0 sh gr +% Polyline +n 4830 2154 m 4867 2154 l + 4867 2184 l gs col0 s gr +/Times-Italic-iso ff 180.00 scf sf +4905 2227 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3150 2227 m +gs 1 -1 sc (Negated LTL formula) col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +2700 5265 m +gs 1 -1 sc (f) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +2385 5265 m +gs 1 -1 sc (2 for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +2475 5040 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +900 4500 m +gs 1 -1 sc (1 for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +1215 4500 m +gs 1 -1 sc (f) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +990 4275 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 2287 4423 m 2324 4423 l + 2324 4453 l gs col0 s gr +/Times-Italic-iso ff 180.00 scf sf +2362 4496 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +2025 4499 m +gs 1 -1 sc (1 for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +2160 4274 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +4334 4497 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4117 4499 m +gs 1 -1 sc (for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +3892 4499 m +gs 1 -1 sc (n) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4140 4274 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 5452 4424 m 5489 4424 l + 5489 4454 l gs col0 s gr +/Times-Italic-iso ff 180.00 scf sf +5527 4497 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +5265 4499 m +gs 1 -1 sc (for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +5040 4499 m +gs 1 -1 sc (n) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +5310 4274 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Italic-iso ff 180.00 scf sf +5512 3375 m +gs 1 -1 sc (n) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4702 3375 m +gs 1 -1 sc (translator) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +5130 3150 m +gs 1 -1 sc (LTL-to-B\374chi) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +15.000 slw +n 4500 2970 m 5760 2970 l 5760 3465 l 4500 3465 l + cp gs col0 s gr +% Polyline +7.500 slw +n 3950 5189 m 3987 5189 l + 3987 5219 l gs col0 s gr +/Times-Italic-iso ff 180.00 scf sf +4025 5262 m +gs 1 -1 sc (f) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3690 5264 m +gs 1 -1 sc (2 for) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3825 5039 m +gs 1 -1 sc (Automaton) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1665 6142 m +gs 1 -1 sc (Model) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1665 6367 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +15.000 slw +n 1395 5940 m 1935 5940 l 1935 6435 l 1395 6435 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +810 6142 m +gs 1 -1 sc (Model) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +810 6367 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 540 5940 m 1080 5940 l 1080 6435 l 540 6435 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +5490 6142 m +gs 1 -1 sc (Model) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +5490 6367 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 5220 5940 m 5760 5940 l 5760 6435 l 5220 6435 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +4635 6142 m +gs 1 -1 sc (Model) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4635 6367 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 4365 5940 m 4905 5940 l 4905 6435 l 4365 6435 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +2745 6142 m +gs 1 -1 sc (Model) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +2745 6367 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 2475 5940 m 3015 5940 l 3015 6435 l 2475 6435 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +3555 6142 m +gs 1 -1 sc (Model) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3555 6367 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 3285 5940 m 3825 5940 l 3825 6435 l 3285 6435 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +1395 7065 m +gs 1 -1 sc (Consistency) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1395 7290 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 855 6840 m 1935 6840 l 1935 7425 l 855 7425 l + cp gs col0 s gr +% Polyline +n 900 6885 m 1890 6885 l 1890 7380 l 900 7380 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +4905 7065 m +gs 1 -1 sc (Consistency) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +4905 7290 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 4410 6885 m 5400 6885 l 5400 7380 l 4410 7380 l + cp gs col0 s gr +% Polyline +n 4365 6840 m 5445 6840 l 5445 7425 l 4365 7425 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +3150 7065 m +gs 1 -1 sc (Consistency) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +3150 7290 m +gs 1 -1 sc (check) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 2655 6885 m 3645 6885 l 3645 7380 l 2655 7380 l + cp gs col0 s gr +% Polyline +n 2610 6840 m 3690 6840 l 3690 7425 l 2610 7425 l + cp gs col0 s gr +% Polyline +n 540 7875 m 2520 7875 l 2520 8235 l 540 8235 l + cp gs col0 s gr +% Polyline +n 585 7920 m 2475 7920 l 2475 8190 l 585 8190 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +1530 8100 m +gs 1 -1 sc (Cross-comparison test) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 3825 7920 m 5715 7920 l 5715 8190 l 3825 8190 l + cp gs col0 s gr +% Polyline +n 3780 7875 m 5760 7875 l 5760 8235 l 3780 8235 l + cp gs col0 s gr +/Times-Roman-iso ff 180.00 scf sf +4770 8100 m +gs 1 -1 sc (Cross-comparison test) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1170 3375 m +gs 1 -1 sc (translator 1) dup sw pop 2 div neg 0 rm col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +1170 3150 m +gs 1 -1 sc (LTL-to-B\374chi) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 540 2970 m 1800 2970 l 1800 3465 l 540 3465 l + cp gs col0 s gr +% Polyline +7.500 slw +gs clippath +1001 2966 m 1052 2998 l 1134 2871 l 1044 2956 l 1083 2838 l cp +eoclip +n 1440 2340 m + 1035 2970 l gs col0 s gr gr + +% arrowhead +n 1083 2838 m 1044 2956 l 1134 2871 l 1083 2838 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4505 3003 m 4523 2945 l 4379 2900 l 4485 2965 l 4361 2957 l cp +eoclip +n 2475 2340 m + 4500 2970 l gs col0 s gr gr + +% arrowhead +n 4361 2957 m 4485 2965 l 4379 2900 l 4361 2957 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2689 3001 m 2731 2959 l 2625 2852 l 2689 2959 l 2582 2895 l cp +eoclip +n 2070 2340 m + 2700 2970 l gs col0 s gr gr + +% arrowhead +n 2582 2895 m 2689 2959 l 2625 2852 l 2582 2895 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1773 2949 m 1799 3003 l 1936 2937 l 1815 2963 l 1910 2883 l cp +eoclip +n 3105 2340 m + 1800 2970 l gs col0 s gr gr + +% arrowhead +n 1910 2883 m 1815 2963 l 1936 2937 l 1910 2883 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3117 2963 m 3164 3000 l 3257 2881 l 3160 2957 l 3210 2844 l cp +eoclip +n 3645 2340 m + 3150 2970 l gs col0 s gr gr + +% arrowhead +n 3210 2844 m 3160 2957 l 3257 2881 l 3210 2844 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4841 2997 m 4893 2968 l 4818 2836 l 4852 2956 l 4766 2866 l cp +eoclip +n 4500 2340 m + 4860 2970 l gs col0 s gr gr + +% arrowhead +n 4766 2866 m 4852 2956 l 4818 2836 l 4766 2866 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +960 4110 m 1020 4110 l 1020 3959 l 990 4079 l 960 3959 l cp +eoclip +n 990 3465 m + 990 4095 l gs col0 s gr gr + +% arrowhead +n 960 3959 m 990 4079 l 1020 3959 l 960 3959 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2146 4125 m 2192 4086 l 2093 3972 l 2149 4083 l 2048 4011 l cp +eoclip +n 1620 3465 m + 2160 4095 l gs col0 s gr gr + +% arrowhead +n 2048 4011 m 2149 4083 l 2093 3972 l 2048 4011 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +5280 4110 m 5340 4110 l 5340 3959 l 5310 4079 l 5280 3959 l cp +eoclip +n 5310 3465 m + 5310 4095 l gs col0 s gr gr + +% arrowhead +n 5280 3959 m 5310 4079 l 5340 3959 l 5280 3959 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4107 4086 m 4153 4125 l 4251 4011 l 4151 4083 l 4206 3972 l cp +eoclip +n 4680 3465 m + 4140 4095 l gs col0 s gr gr + +% arrowhead +n 4206 3972 m 4151 4083 l 4251 4011 l 4206 3972 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2760 4875 m 2820 4875 l 2820 4724 l 2790 4844 l 2760 4724 l cp +eoclip +n 2790 3465 m + 2790 4860 l gs col0 s gr gr + +% arrowhead +n 2760 4724 m 2790 4844 l 2820 4724 l 2760 4724 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3482 4879 m 3542 4870 l 3517 4720 l 3507 4844 l 3458 4730 l cp +eoclip +n 3285 3465 m + 3510 4860 l gs col0 s gr gr + +% arrowhead +n 3458 4730 m 3507 4844 l 3517 4720 l 3458 4730 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +870 5955 m 930 5955 l 930 5804 l 900 5924 l 870 5804 l cp +eoclip +n 900 4590 m + 900 5940 l gs col0 s gr gr + +% arrowhead +n 870 5804 m 900 5924 l 930 5804 l 870 5804 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1722 5949 m 1782 5959 l 1807 5810 l 1758 5924 l 1748 5800 l cp +eoclip +n 1980 4590 m + 1755 5940 l gs col0 s gr gr + +% arrowhead +n 1748 5800 m 1758 5924 l 1807 5810 l 1748 5800 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +5550 5955 m 5610 5955 l 5610 5804 l 5580 5924 l 5550 5804 l cp +eoclip +n 5580 4590 m + 5580 5940 l gs col0 s gr gr + +% arrowhead +n 5550 5804 m 5580 5924 l 5610 5804 l 5550 5804 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4744 5961 m 4802 5947 l 4767 5800 l 4766 5924 l 4709 5813 l cp +eoclip +n 4455 4590 m + 4770 5940 l gs col0 s gr gr + +% arrowhead +n 4709 5813 m 4766 5924 l 4767 5800 l 4709 5813 l cp gs 0.00 setgray ef gr col0 s +% Polyline +n 315 1620 m + 315 5580 l gs col0 s gr +% Polyline +gs clippath +2854 5961 m 2912 5947 l 2878 5800 l 2876 5924 l 2819 5813 l cp +eoclip +n 2745 5355 m + 2880 5940 l gs col0 s gr gr + +% arrowhead +n 2819 5813 m 2876 5924 l 2878 5800 l 2819 5813 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3611 5945 m 3669 5963 l 3713 5819 l 3650 5925 l 3656 5801 l cp +eoclip +n 3825 5355 m + 3645 5940 l gs col0 s gr gr + +% arrowhead +n 3656 5801 m 3650 5925 l 3713 5819 l 3656 5801 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2943 6861 m 3002 6848 l 2969 6700 l 2966 6824 l 2910 6713 l cp +eoclip +n 2880 6435 m + 2970 6840 l gs col0 s gr gr + +% arrowhead +n 2910 6713 m 2966 6824 l 2969 6700 l 2910 6713 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3297 6848 m 3356 6861 l 3389 6713 l 3334 6824 l 3330 6700 l cp +eoclip +n 3420 6435 m + 3330 6840 l gs col0 s gr gr + +% arrowhead +n 3330 6700 m 3334 6824 l 3389 6713 l 3330 6700 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1498 6851 m 1558 6858 l 1575 6708 l 1532 6824 l 1515 6701 l cp +eoclip +n 1575 6435 m + 1530 6840 l gs col0 s gr gr + +% arrowhead +n 1515 6701 m 1532 6824 l 1575 6708 l 1515 6701 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1151 6867 m 1203 6838 l 1129 6706 l 1162 6826 l 1077 6735 l cp +eoclip +n 945 6435 m + 1170 6840 l gs col0 s gr gr + +% arrowhead +n 1077 6735 m 1162 6826 l 1129 6706 l 1077 6735 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4741 6858 m 4801 6851 l 4784 6701 l 4768 6824 l 4724 6708 l cp +eoclip +n 4725 6435 m + 4770 6840 l gs col0 s gr gr + +% arrowhead +n 4724 6708 m 4768 6824 l 4784 6701 l 4724 6708 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +5096 6838 m 5148 6867 l 5222 6735 l 5138 6826 l 5170 6706 l cp +eoclip +n 5355 6435 m + 5130 6840 l gs col0 s gr gr + +% arrowhead +n 5170 6706 m 5138 6826 l 5222 6735 l 5170 6706 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +645 7890 m 705 7890 l 705 7739 l 675 7859 l 645 7739 l cp +eoclip +n 675 6435 m + 675 7875 l gs col0 s gr gr + +% arrowhead +n 645 7739 m 675 7859 l 705 7739 l 645 7739 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +5595 7890 m 5655 7890 l 5655 7739 l 5625 7859 l 5595 7739 l cp +eoclip +n 5625 6435 m + 5625 7875 l gs col0 s gr gr + +% arrowhead +n 5595 7739 m 5625 7859 l 5655 7739 l 5595 7739 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1946 7879 m 2003 7898 l 2052 7756 l 1985 7860 l 1995 7736 l cp +eoclip +n 2475 6435 m + 1980 7875 l gs col0 s gr gr + +% arrowhead +n 1995 7736 m 1985 7860 l 2052 7756 l 1995 7736 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4296 7898 m 4353 7879 l 4304 7736 l 4315 7860 l 4247 7756 l cp +eoclip +n 3825 6435 m + 4320 7875 l gs col0 s gr gr + +% arrowhead +n 4247 7756 m 4315 7860 l 4304 7736 l 4247 7756 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2498 7849 m 2512 7907 l 2659 7872 l 2536 7871 l 2645 7813 l cp +eoclip +n 4365 6435 m 3825 7560 l + 2520 7875 l gs col0 s gr gr + +% arrowhead +n 2645 7813 m 2536 7871 l 2659 7872 l 2645 7813 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3787 7907 m 3801 7849 l 3654 7813 l 3764 7871 l 3640 7872 l cp +eoclip +n 1935 6435 m 2475 7560 l + 3780 7875 l gs col0 s gr gr + +% arrowhead +n 3640 7872 m 3764 7871 l 3654 7813 l 3640 7872 l cp gs 0.00 setgray ef gr col0 s +% Polyline +15.000 slw + [15 45] 45 sd +n 202 1305 m 1237 1305 l 1237 1620 l 202 1620 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 1260 2025 m 2475 2025 l 2475 2340 l 1260 2340 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 3105 2025 m 5040 2025 l 5040 2340 l 3105 2340 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 540 4095 m 1440 4095 l 1440 4590 l 540 4590 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 1710 4095 m 2610 4095 l 2610 4590 l 1710 4590 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 3690 4095 m 4590 4095 l 4590 4590 l 3690 4590 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 4860 4095 m 5760 4095 l 5760 4590 l 4860 4590 l + cp gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 2025 4860 m 2925 4860 l 2925 5355 l 2025 5355 l + cp gs col0 s gr [] 0 sd +% Polyline +7.500 slw + [15 45] 45 sd +n 3375 4860 m 4275 4860 l 4275 5355 l 3375 5355 l + cp gs col0 s gr [] 0 sd +/Times-Roman-iso ff 180.00 scf sf +720 1507 m +gs 1 -1 sc (State space) dup sw pop 2 div neg 0 rm col0 sh gr +% Polyline +n 744 5435 m 4997 5435 l 4997 5427 l 744 5427 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 744 5480 m 4967 5480 l 4967 5465 l 744 5465 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 744 5601 m 4974 5601 l 4974 5646 l 744 5646 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 744 5713 m 4997 5713 l 4997 5721 l 744 5721 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 744 5668 m 4967 5668 l 4967 5683 l 744 5683 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 751 5599 m 4974 5599 l 4974 5502 l 751 5502 l + cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 315 5580 m + 5175 5580 l gs col0 s gr +% Polyline +gs clippath +684 5968 m 735 5936 l 654 5808 l 693 5926 l 603 5840 l cp +eoclip +n 477 5580 m + 702 5940 l gs col0 s gr gr + +% arrowhead +n 603 5840 m 693 5926 l 654 5808 l 603 5840 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +1552 5968 m 1603 5936 l 1522 5808 l 1561 5926 l 1471 5840 l cp +eoclip +n 1345 5580 m + 1570 5940 l gs col0 s gr gr + +% arrowhead +n 1471 5840 m 1561 5926 l 1522 5808 l 1471 5840 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +2633 5968 m 2684 5936 l 2603 5808 l 2642 5926 l 2552 5840 l cp +eoclip +n 2426 5580 m + 2651 5940 l gs col0 s gr gr + +% arrowhead +n 2552 5840 m 2642 5926 l 2603 5808 l 2552 5840 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +3447 5968 m 3498 5936 l 3417 5808 l 3456 5926 l 3366 5840 l cp +eoclip +n 3240 5580 m + 3465 5940 l gs col0 s gr gr + +% arrowhead +n 3366 5840 m 3456 5926 l 3417 5808 l 3366 5840 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +4498 5968 m 4549 5936 l 4468 5808 l 4507 5926 l 4417 5840 l cp +eoclip +n 4291 5580 m + 4516 5940 l gs col0 s gr gr + +% arrowhead +n 4417 5840 m 4507 5926 l 4468 5808 l 4417 5840 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +5382 5968 m 5433 5936 l 5352 5808 l 5391 5926 l 5301 5840 l cp +eoclip +n 5175 5580 m + 5400 5940 l gs col0 s gr gr + +% arrowhead +n 5301 5840 m 5391 5926 l 5352 5808 l 5301 5840 l cp gs 0.00 setgray ef gr col0 s +$F2psEnd +rs diff --git a/lbtt/doc/testprocedure.png b/lbtt/doc/testprocedure.png new file mode 100644 index 0000000000000000000000000000000000000000..b141929d167625519bdee8fbb159341b07d287fd GIT binary patch literal 14430 zcmeAS@N?(olHy`uVBq!ia0y~yVDe#LV7kM>#K6EXE1}{#0|NtRfk$L90|SpQ2s2(f zx7Lq=fkC#!HKHUqKdq!Zu_%?n-7i$ZJtM!kq&T@Kv!F!5RL{_$ak}3G1_lKNPZ!6K ziaBrZ-oExoLC7I-&eiw-Pn~PMEIEroQRelA{j2lpdzSEbVaC^YvtqtQcW(CIF|WA($y;Oj`$~WQracMz ztQGyPf|J3a`?~qOEAIbOY>ofrKlT5bK5bk5Uu(^f^|wBJQ|+I)P>HEQX8m++@68(v zr|!JcHT}4K_h*Z#Pd+I!WPDlv*tF1d#s8+L$6KD=3UN2y)Nik6cJIFS%wwe?rq2o( z6wc1Su=`xu)k#%zv+q|tE?&oPv&DQzGW*I1hJ*tb?oREWns79A)%N-49|uV??D#rA z;Kz>WYm2;tzk4Tr{k}`e=El4?*86gK7#!AIH{MyXV?F<0+17_uo|&9mQfE(%D&6VN z{{H=s8#8|Y4&r8Xm~!s?$)j5HZM^dsPTpH7FO`?4W4a*Zq?Y^%?WqSooMVIe<5!j#tRBBz2(0fr@skU+O&Xo$Q6QqcX> zi=iP>C!C4lOp=;8@9KRF3TN%@XI1Uz-1l_iJ8`x9v6V}Bi>3J)1fCxC>C5C|aF{NB z{fSh4AsYjO;tn--27_);E%jf7aOL%isL(#F;ze-j9Ss&)5HMhVmDD{K`Ie z!HI(++ifK?p09dVyYGJf>tK8Rc|0;7U++@U3V66`_8-f;3<+xLf5jvouA03e#&YKB zQU)C-F~-dSPZ$`|HX;caOm5cH`NG&AQ!3mg!*D=nn)dYF^9}EcFSa_jN-BKIBVG9z zmff=qSMx6Ajf#D-TE<!g><%G4c=&Z%j#%vP*)>UDe@&h?akb%0 z?Yj$Wl{pn{ll;HUdau?Sv2Mzudx6J|Lr-)3(6F;K-5jhN_3EI~Z}wAH?>_vfyQTQH z@4Fc%eYYvipY#01+@B3L?-o2?FJH9#alYUR`K~z}moFRIT|K+*TgrW_xceSr-jk31 z4gX)8^YF;s>st%1KeQ5?Y8Bp}`|`zJA2<&z~&Pc=K5E%I^y`X~G3de(nwiJsqo{POFcC+iRYK7Fgu_+5Tf<<+Zl z=fkDEo@;+>zIo$bf2_=WFbNY=IU)uNG)3>dEB(SBVeZp%& zp7wTN(((Nm{-)-P?%`>{R#WjBVH)DY>uFFz4pA6B7 z{Psm4x{7FEoC`&seDp^JMS z&ysQJ)>kNeW$>=fv~Pu!gZavYokEK)ma4Ccf7BFLGMTGO#wT&7M8y+OM&e{J=$7_R z=$bq^IH6^A-XWhnqwsju2G%8y3 zi{s|jOa6;xmv7v|y4#bxTB9h!BV_LHiNSBDOf$C;n(=L3;1fRM`;te>e!Mwvo}cmj z<0C7-XN;_yG>Rhpa_0W7-1D(kOgh;7WP1?bL(BJEuJ2Ur?>czTkKZyQbMYQle@Sj* zjiTcf()wGj?3^EY^_)-T;pkmL_YAsZzFoZWcK7PJdp|9EcSCl$;lsBJPt1L2_VsJo z?{CtfZ>Da4V3E8~R-Vni!DCjZgVA2&$a_r-AD6ORwCd{q+r@5tLsH? zb8n}gxqh)CBPD3obF+fSC+%1^xCExKNqWLP1j{S*FMt4NJ?i4-wqMh!}s2Cqvlw~+Mrqxzt=T-?*E18wWPN$-qyvx5C z%)H&B#NJuP$6|CkI>~u!@{_x5R@;SL^AtZ-9BFHGaIN&#*%jCt(;8YMQ6qZD^yngk zES-GO$NS%8sflz6e6yW#(s9en&z8wg-=?P+PF^UsE%3>-gaRfLRcpTC@g~pDO_Qx7U0*Q~zivpk5 zxv(~#pRwPG&;LlTx-G|1m+pN9mwp-sZTuaw+e(x@&@QcL{lv=6I)Cj_&p$sDRW`@F z{6>RPJM*_cF%8eHmUCa#>t>nPr1YK7(md(Ez_WrG0ekn~S<&=Tqv*!N2l{(=t}0IZ zHbpwLE++N8lE_Z+6WjQN>P}oa>5*6}A9>~oM^}vYYULAFLZ@yY^_;u;Z2DH^Que?n z2J=^ZD%)rqA#rc3b^g_JWv|w`P1Npk&oWvw@yw^PjaCse?yMBMmlskPbmjS?6Se|& zS*I7O2AI8yvAH36V{V~bUPvdm<*mRcvkdw|GBoq2uQrg7;5~W9y1IMjloFKVj$-00 z;VGxWjNU%g74hqpG_?t+dg=G|(BVRnojuidC*QMOj&LsOVH#*J?cx{lot>qSjrwcH;FmUS zzpsA(9a^~K@nwBcUb6*4ud3=JyB7cFa@9MW$QQcn_4oC2-`;%x@kuPlXLhIQ!Asxm z<7gD>x??Z0Hq3X*-o*2sx!tk9wF}-nxE6Mj@0z8}t=jUX2Ocsz##$B-RqzRR3IwBxoh{P zr?0PCuWRi7<#hGNn)Asa`z9uy+U?;y?@R8T=y@KWXKe5Hoc*`s&_U+yCYjEr#__6a z7bso-SFLw)dhn6|Q@<@XxcTkRzu7hK4F0V@e}0L1(LD9Wkc%q~pK5vD-Z#;9i^|%4 zPft~qq}_eD=gYR2zs<8_W*UB6ySFypGs%D6nY$rMzj<{o2Oq7R*Wq+&U+r|UrnI8R zU%p-4@wDo@6Zcg?wa}WN+BN6Z)^1YTuI}(r(*0$1wfZ)dpM~=;<^L`DvuN>z)ZV|_ z-hQ$To%ZiZwD+nB>Ry$8w@y7?YQI$-WOME#$&aEJ-lXhtu4$R{IAqs_sH)eO=KomK zl{@K&{;A0qCTKn3lsf5o>w{f>{MHFd-`A9!`d}t%H}MUdw!V*%THWhQWfq3_du$r5 zj!!l#dHQzsTkYs|B9Cr=Eq#14X7@+2#lNKuwK5&&In15wDZTB<+T%*$_AUA69e$;I{0FXtZgxDmFgrc5l^AHS?#Rew=VS7dL-@ ze2?u_p@%0XpQ=~9c5{#IJazBSVeS*HXEQEcy-9hwuf4)Go8EIhV#GK zRGW*h6YkCPy|qC#KRze#RQ3K_|6Pi5H-EXVx%$#|H?P^XqT8oUOPN2d_WSSVle(Ta z|4)-rE0(!3NqwQrw|8%Cwk?^Yb?=kQEX~U3{o=_-f0UVJ9{#+Isgrhx(3>EC(cipB61WrkM%vP8S4AX&(}TG3rO0!Ew)75 za-(f!(&2T9+;?rBcy_pHznW8K)Iax3nC+$5Ck!mpHZ>X@z5b+bx#-9I=s&xvK4pt9 zLxX(|{ znVlG&nzC9YR5e+8{jF>Bw<*OwzReeQr)fcj+VrolrX(Kz>H5+|EiV3%$)ApI8@NwS z{x;3SqV`zKBDZZjUsn14Sa&w)Nt}fJjitIJ`IWyEk62V!I<*?-#O^A~uQ|0LY<^N% z@cx*eE0RA|AKmru(96qNGAU~Qc6VAH9oSX!_V$mCtM60yoG4v?TjhGbTAn1+%#*_B zcG*NsU#BcBzvZ25ulP>E{_u?3hF>Jq)cq|!9#Xb^8D(hRt}DLtUGfE^x%qcOzTL|C zRyZd~(|^+Q;M+B9_)G ziW~GocJOKxEem+gpu3`=I^YRIg1OcmGZD+|8D}irnNGO3-tJUN7dR}#@F(C&&Z2XS zMuxq&dz8|}O8F+Vac!4(J!kYvMuA=Q9Y@!k!Yh^zOj>s|MJx|Uoy%FW@Q|#_#LfMR z{1zQtLsJ?AdvyV1<0&f*qbI7F^>mS$OEA`^3Zl1D-S7 zaGrShf8g_mGM~pG3r;+gzhb$7Rn(8WOD5=wrLo17?tq6P3r{FE9~9JBc<80mMCOLD z5Q|ccq7%tR>?$h?qynD=xXfignBoj|DJs^m^QN{MeyYVDZ&r z@?_y!?-|+-%35n<6^q!M`0}&nkad|LR1~iZN=J9eT-z0N|L2FUV3xYsixQcd-|RZU zX(Oj5m_Fy|qNeopy4DBfd7{f>zvZm{vgMcZ?|6&tyxKW8{{FgL$DMjoT+`E{EFv-N zmWf?ZeblCD8+4z0zu$3aqTu>359XO|vCl1^n0z+Nn5l2>iS2VOGP6HE()L-J0Kh$JuXIZF%2+{_fvP%R*RcoZ};2dHfE29(}rA zd#!C!(gEwT{i(s#OnN0djiOK9zRI8VM{Mc;yb57WV?C=j-O1{}A)emGFD- zKS8*_QC?BP^OyTw!T)vpYuAfT`na?~zqhPFrr`OUC$lG{1e?!k6J``I_tmpB6S~&= zO~+0q@{xeV+XSlz=O*TteAr<7VAH+@mOo#=+Pq)vR{J5Z)2wdRFZg$G-A|3b?6dFf zk~|Aubtb*1vtpK>=G9YuZ}O?{?&%WQFqi5NTYuj#Og}Jp^Thn-{N;O_U%$Gm$hBjN z()Y01|MKsZXMNJsz89#&)Hm1V>FoBD)h$Vn-pOGDERo-6QXD|1EYx4KzsSVsaw;3kg)|+v1`>D0Ha#j_m z-?2|*d_MhPukr)!fG1kBd7s?YbJ^W`?WtF_RylJrD>(U`UDt4W*KVQ9f(0qMyADpO zx!swuUEsT=<@OWT&vIClDcv{Q&2+9vmEnL6c&x(ler%^GSiDT>mhKGDD7eta!~zz( zPn+&uDA$O1$(>f_VrTUuE&RYtkmMs}mY{;J>xoZ(Pv)?Qll-$u@6%fU@*C@)YwH#D zYU`d1U%EEu;=}iAq8F;3uvVV4C;yXc<%hKBcN25mcm15Q;k{nbWA4lA%PfAso!@=p z4O=Z^{{8N`g8TO88k6N5mR0zD z|CeEPO?RL8sp~>dzHzkQi?}QN{zr%MDt(?w8}vVB)&D;A-4(RPvr@fQEHD4O zwD!)$=TE!>z28aw%Dn&P>yz#JTb8RHTBv5&mEOG*IC1yn0)`yr zLlsZWbzV| z_QtFw+>HNcS`v@B+woVsobm*q;!xPQkv`wI>^9LUwUJKOwUEX$%x{!aURIk$eWh+o&R`VWWW zW8GCJV?(E2@2lZ)c*ZDqU1;(*4)eSX8xxN&)?LUeJooP0GZJl>mdVd5Ew zeVSfo%oq2q;oiMCyGU5`727m}hX>45ZVPH3I#-n1FDlx%b>dk!ro8p~H*PCuzIZS@ zmeKq->sjxKD;X9rx%zQ&Jx^niv7VSeJC^bJEYKMByLBdg4aw@Ca@7~zee|Iy^;=TT zX>~F0{*wI#UeeFZ>R4y)zW?{o&fEhfzpHzvU5PzXbH~bT>+TbVYjUzOdCr{Uz>TMavOP37Y9Y#144xK3n75Dj(lhW2aw zbh>2jJv7=K%5%-26J@r+IpXEi9wd==@f}+)nN7Q!!0*cE&=tgP{3Eqsfv3^C{NJTZ zY#;6Y{daccZ|%)>#~3dgPG+8!a$KLy)WHIrGrMFMmMt;pR%#E6DJp19{wwx<*(sNa z%u%Y3_iZ#v$SH4-hbn9MpJ>Ehtx?2az&`yc%c2vE`Z2|_YPJnrB9rOJR4hY%uz(evFs9h=M%6EEWiSm)hjSoBy z%5}-OZM_qB@Wo12_be{G(RkIc1@DlDxpvMV(U zrEP+yZr9$M{PgFBmYFhQRewY(pYH$o_@l$OzuGR&w@qIit@rvTz5Ug`o4-NzTy%(v zlzosgTw8QVTS25BN0&^QgnW!wyxcU~%jf@zhRe$i3G1L+{KC&3&63GFwqMp%(xJ7c{Ovsbsn@<Sdt>X>hM|#!1H22E~_}lv5>)Eg2#@P8X#I zJ~3GJ*(}w*@H6S+-l(Fd)@ae8K3w1F^cmfcgQ-V%FSYN=MojMoTKuG zrRIydoyq-J%|91Zj`d$zGi^#i1J@!Oq)|*G^-#%`C@&W3l>7Jbg--Dx@H z8#r9~I3C^)I{U=Is81wr;fcg4OV`)U|J`ViTpSa)OnznL?5ZPs)n^y_@^KtIKIgzO zyIVK7bDSqKhb3BE10{mfQyCaGYZU#k-?!+3nI0?W!PK2|Bi0>R@oRlYYevpq`ybyX z{?(szyMkeM;1h=BQChzede`T?3jUG$;h>pMrj1VJMw_K2y55YW$h~*#t$9xQ+A`oOh!?vWe z4AwXI?-pWMAW|OA#1N3O|3qPMj55>L4uM|fHp4|58~-Y`i>+~GHt5VQVqjPZnl*M1 zbme16nD$rc_|m&#E-M8ar5wM!>o}POn%3t4*~-ub9$>*w{M%DyCV!5dAwQ&^_e8hS z_3iU({JokQeB3=c59qiUd6z~+tZS*d|9-kDn~$U3&CPpGOf=zSYI^CRrL)xP;Ea>9 z3#^h;l$N+8h$+onYOZ75HRs+72f<4`n(%kukPzhsm{_wXS~2CDW7?v@QBqt+*Rrd;h24BEI>OyEjkR<1aAbC5NWqj8>1d zw>;eYdH9xC$R5*qGKq0__nm)B1m`Vgub%kf#0mxDHlem>k9h;UT<@r;L|CU4J%4#h zLKLb~lk0S&(sB)}3*9D%b*zg!_so5F#wV}%(woY~g6>Aqt|={(We#sXy!6Qlj^(N) zQLi@sed;CJ{*dGI#G98n&$e%D-aF%s%mqAR1&vXWWJjD zQKE`dQ(W89BWgG1U*?Ta<@^3M|GDySTeWpepI*0gms$3$UA=;B`rqtKz1SA&6jxV^MOyJ6ViUV7X6x+HFL{U4wbi&u25Y60JmwNlS zpZT6{8PAa8)5A)Z&Rn#NtGr+3`p)JnEW(D9lO6n1n3aqhMGEB(%=KKlVm;>)ld2?l zT|rm9>Ht-j=TlrG=jDWD#=qL8zA7mpHsX-C;U1 z^Wv^FZQc_i^S;~exc_(Oi-qNvqV!guV69}d>T6gd;9h!a=ahAapZxjsB<6DQ$%gme ztyLx0_4efW#!Tdn+wIl*S+G*@x#NRZ2VCZuq{P0pkN&MHw$ni0>el8bMz7CsY}~0B zTHeso<7l?R`d76FNB0FK@nvrAP79XL*v-`N!CWfO{pZWmUh~$TP-Skk-N#+j9U~wh z+t_ikWR9O^?luP(qwEjejcki|{a&3>GS?{o_q0OMkdlO0Mc&PeU)6jMS6E`$dr`o4 zGBZQMk(v4#&%WronJk+ue`$l!s&nP_tWHvwMR~zh8O~gNuBPRI`o4!-OZ>A=7t7s$ z^s)8&Zkau%Wu+z}tw~DH155Y)))u&wW2~iXDc3vMc;}Dm&vNz;H-3rmdQt7PNKsKl z*s_1s=ezNZ6OM(rIDaWxx~{)_otsHmfn4bG-TRpodp$ea7AQ^UZJ!`K?Qh31u5&PV z&%9_+>HF@}H|tfC)$cb)+`9EZbf4`rx5b+@MK8{H8XEFebq2`TI;qJrypF~@KfLF= zVtwiR`roVV=KFsO-}Fd4Y@7b^1QV@=99)YHPbY`uszcQaOYmyC>h1QP+Bc>4&&Q{4 zpL3`C_Pd>CJL;A^?VG|39|MrP{+g(nNhwX!et(NU#m0Bto%~#uammA#%}$MXG$)~tycp@<$|Cl8A}CC(@yp} zA0wtH1uBw2?w)YW#nm}!BDeq3U2+FceQkc?zo^f}wcNz3K1jAVQY-N5gal9w{mS(5 zGf5HMd39 z>z(HQ&3+mBP;Be#^&d??|BiI{vnt2Z%l1ajfkecFV}0(VpGcyrXIN83V*nmQxa?b*I>>C5o6FS))& zyHxrsKu&VLwZqX8R7-cidA~f#S0UcL!vxd)n{}l ziT}I1`pv(T)m1M;YojMGzV|Qa@mja!uAQ9Ub~t82-1}zvA!jYigJt*EO@1>wqWYGn z$+F^mA+bnl7V5_;uDMvXBMFnR-d=VU3yn<+l}N2SV_`Sx3;w6 z=gdmQ=zGsj=&k6u{n1S>-$9x4?UV~`?q9arb(bwn-hAds!_0m6*RMM&lVBC-dMkNi zvcxN8`@BWF#CQ}I^L&VSvSsn(gKR&S`@T@fZ77O*@*?(w&n*KL;lND?GbeIi*{L_F z`Pc*&8TAd)zjTWZC{6NbT_PyGdUNyE8yhm7>^d>gQ#OUo_rXu)2fb(VUhF!-XyNxD zbMl#kQ5G8=KgT^`DKq%VF)J&HT{(gEy(=FZhs;(>{tMm-RwvahGej(pEtX^96`tr9 z(XO<-=ZUeJgY~a)_J@v(PqZHBOc7Uf6ux(GiDJx$Hl^?OPHi~{)H!7*7&q{JYId9G zY|qI&If8{<`NaH4jiUeVVJnsY>j*_n@Rki>=~Zf1oFkbaR^%xzDykRoM5x4AVp(d) zt^N}dZW9l$2u^4F#IAO3X2R4z_sSQZSSm52A-M00o$F&$tAHmag14nDjT!z3SPP^E zJ~5Km^z`!8vnmQnA3Bw$yU(re?nvx9+v)PQ-aGb)M$rMMD|IExmCF{K5S^HqpW8iS zSFz^_7p1*R4Uim6PY4)D*e8Om)O!)=#}b( zUsHKJWlo}Z=C+-O45cUU5(tzMBlZ0o@)%gzFV`% zwP=%;;PV?}s7fV#O?zEoYYS`ln2E*cJ4mUFo~S zA;+l~*uFfx;kh_fN`1pbrrB4Tl)n31StxpSMwjUFhf!_nGdX1AJb$gf_h;b=(~H#& zg?A1akN2DWT!Zf%=@!5&Uh}c%AdRZ!ph|3zqI{?M0S?fx!)~bZ|(Hn@`r2n z@2$oYoMzADoPO~Ax;x*p^`-B*9-1BZYf{{saI@c*O~3D#ynARj+ezqL#@c0P=Ov%= z;cF-~^549n{ou?y{Fb&CA6VXfF!RSH!7iJb-iP+2vwlihH@EVT4_`xx^7i=#y~TY> z?Xq+3E3-ck*?fAc__1q($Nd|(&uyEmuH5(B^W>L5ivDggCxz3BZnT_~ahqWOBDr4e zyYe@mV4eBQT$P{1Dx1XEKex=3`BqtNbWWF9!ttL%z!QdygI%gt0Z%rFJ{I|wAk{A4 zCDW2Lfyv<9>0Wi!rIAOw9$vj9UfnP7J9k>??Vwhhl^H&AjrU7WPAKYntyI4B@l@3{ zOBB099JxQaDC|Ey^}1(3jog+mb2KW~b6K^mSb6VRh@wB+Trate&Rw5v3ZtHAxyPQn zku_t{ZGR8Z%oH{LN~LM9pPrgzp*i`E?>~|6+f}AM7FWv_zqz#M$-y5pZ!WhrQ@A7M z|L*q6Jz~0EtAoC$XvnUdW^>YRt*!qnUXGn67iMcMy2hZ_{Yk}yNAtVQ%KWo&^DO3` z{JSM)A7A}-mPyVPOV9oFKgoS~e`Q#`-<@qgK5pkd@$coW`%mQEFKsNc(<_*>YWwm@ z>IB+zQ7fm@gfn}at?6T)4Z>xvL{jgY~ zadxu&H0}5mK6bCxzpC?ld1c?be}8^`^Z)tkkIuoxrIkTdw_e%2ykQ~t_Wa}vQ?+h= zZc*BPM8DSidEVReJLg&M&Fwa`JC|}(JM&EAx8-Xy?(KSeKGmZ5*|U?6 zl>b+5EIGA$-B~Z|=RzwCL&KhX{>|+Er1HXZ>SNuIiRy_8p1XcFR9~3uu`_|ubAfhRWD`OZnMJ@-dB6h zKiRV~rqJfK_NO$<^GB9>^=z^9tD9#qx!9%7%85gFrBkc5w=0-mq~x```G92~Q`CwR z!f8beAzpcZm^Wz@S-k&ulAU2jlM=gw`MKTz$BE54JP{cOpGKQ3I`PoJO`G9$z!L?& z$(C2{`uCiWoD%RPY-fGz+&OC<`987eE|ZV6U+FaQu;#fQ{PQJ6*^O> zy|Soqwt;Nrr5`eHBi3D6vTp<5pBtxz=a*(PSFbE`pE&zLUEz*TPcD6Za%}BQk5l2v z(`ITHum7ZCv$U(-=3{31yeV;coUVB%OoP(j%dFhJ|H;*BJnBnrl`D(tr*5~YwC=rJ z9aduC-_#j$a?y$2F5{Q;oNr!Uamw3j+q1@diLWn}r+BYd@?UdnrutOgs6)N#!YTfJ zlh|MXuia|;R$To~T>Q%ulP|4~+Loq#f^T!G^E{UBl|hGm_^c#moJo}4#46q;!>~}s z@3DcbD_?XkPxPF5%oW{A><)_#X38vg`LXbXqp+JE!}@?H8~CS3T<8cns3T&jaX5vq z*F%)We*R?T>ld4BK&7ByTG5S^M@%B=FSczA>+F1U{>cm7499shO#V(UKOtN?{eobV6dz(qFt%I z^&3yzoF&W}3r{dwWZpLEo2)orqi93JM{Wl(SH9H+3t9U;gx0uCY!>u+_Mtj&=V9vIGf#f_@XNlE`iwZI}$h-1=p1b#&(Eie* zbLS51l5gcr4}6jkX|&4d!8=8pbHR0PqEP`)9(ZIvR9W~D)5Ox@9ob?v8!i&istZfnaG?rDe}XUEiNKqI<^Z=Fs`1XTOnem z5b(r+JN-zhG}pY7w-Xy2MJyAxce3`syrmGPb9V6wM)lGfqsBvQTNa;4WIETn+=DCb z<#t<6(+hp^Z0stFCgmQ^Z@sOSrzV~*ecEW!-#)(SCeN}?R{lDw6z(-Yk0a>jt_u}Q zRQUYAZ`Nw;W%>T;tWnSwjf%$-1y4@t!OA2~(S=wl!Vv*zGqZ>wHS5tDdLzuBBWu%j6qZX8&_ZkME$?Jbh1U@l1b)ezKk3jp|sR>IS ze2nyb^F`}si`dT98JCMUTrr)M-k!NVd_$b*p8LXE$|KZ@!#H(|E;zkk5GHjo$N#$3 z9^HznX1BSUhSK!DuZ_gSBKM(Zgb7ZeJ*x==S|F~TEeeqgfK86NCaL2KI{mG59T0|p+C-?8` z7kyVdev&kD zE`C35`R4bjt~*noUUz(yXLW%2m))<7sC9eJIX=295WZYxbI!kqTMi!V*vRwcZ=}q( zT2t3`|DvZ`M>8d;|FUz_h+e1DS^W3&&zSv_=KY;}e@XI@`m^g=_Pp}=?mBfE+Y$TM z^S11Kdh)B;eP*FN@6Y{?6;~%c$vk;GG^zUk<-boId@Am*-=0`{pa1EMA1{Arr4`xt z@0L2QTKD94_|x?d#jfSpuiXFpZ>TP>N8F6NelJf>l|9L}w|-zylUSdH~sjE*psvNKKS|SQSrC^T5WHB|MSZ)Gn@B)>%Z-Z?YXOd z|6AQVCu#Tl)tW^M_Y_5Rp4eNxMaQM2<;T8*Ki)0&Vs?v}Ep?`IS~pj5xX>hxsk0dW zWW4#UoBP7;e*MS3*~@R=w5h(@b}CTw?*HJO`WnBNzx&;>sax(XuSd|g=zk1nYlEl0 z4Vf2n|NgWz_2?7FR(&ed4P2FP^E*zNv(2^U+uvi`eBb~6?x=7()b>klt?N1CLti^s zgu9)Mn+j~TRA(%E>czkSs;Qwp-%tM%3t}TCveztnU!IJ*!^-=JspY;`OdmS;^ 0pt + % Only leave this space if the footline is nonempty. + % (We lessened \vsize for it in \oddfootingxxx.) + % The \baselineskip=24pt in plain's \makefootline has no effect. + \vskip 2\baselineskip + \unvbox\footlinebox + \fi + % + \ifcropmarks + \egroup % end of \vbox\bgroup + \hfil\egroup % end of (centering) \line\bgroup + \vskip\topandbottommargin plus1fill minus1fill + \boxmaxdepth = \cornerthick + \vbox to0pt{\vss + \line{% + \vbox{\moveleft\cornerthick\nsbot}% + \hfill + \vbox{\moveright\cornerthick\nsbot}% + }% + \nointerlineskip + \line{\ewbot\hfil\ewbot}% + }% + \egroup % \vbox from first cropmarks clause + \fi + }% end of \shipout\vbox + }% end of group with \turnoffactive + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi +} + +\newinsert\margin \dimen\margin=\maxdimen + +\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +% marginal hacks, juha@viisa.uucp (Juha Takala) +\ifvoid\margin\else % marginal info is present + \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi +\dimen@=\dp#1 \unvbox#1 +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% Here are the rules for the cropmarks. Note that they are +% offset so that the space between them is truly \outerhsize or \outervsize +% (P. A. MacKay, 12 November, 1986) +% +\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} +\def\nstop{\vbox + {\hrule height\cornerthick depth\cornerlong width\cornerthick}} +\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} +\def\nsbot{\vbox + {\hrule height\cornerlong depth\cornerthick width\cornerthick}} + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% +\def\parsearg#1{% + \let\next = #1% + \begingroup + \obeylines + \futurelet\temp\parseargx +} + +% If the next token is an obeyed space (from an @example environment or +% the like), remove it and recurse. Otherwise, we're done. +\def\parseargx{% + % \obeyedspace is defined far below, after the definition of \sepspaces. + \ifx\obeyedspace\temp + \expandafter\parseargdiscardspace + \else + \expandafter\parseargline + \fi +} + +% Remove a single space (as the delimiter token to the macro call). +{\obeyspaces % + \gdef\parseargdiscardspace {\futurelet\temp\parseargx}} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + % + % First remove any @c comment, then any @comment. + % Result of each macro is put in \toks0. + \argremovec #1\c\relax % + \expandafter\argremovecomment \the\toks0 \comment\relax % + % + % Call the caller's macro, saved as \next in \parsearg. + \expandafter\next\expandafter{\the\toks0}% + }% +} + +% Since all \c{,omment} does is throw away the argument, we can let TeX +% do that for us. The \relax here is matched by the \relax in the call +% in \parseargline; it could be more or less anything, its purpose is +% just to delimit the argument to the \c. +\def\argremovec#1\c#2\relax{\toks0 = {#1}} +\def\argremovecomment#1\comment#2\relax{\toks0 = {#1}} + +% \argremovec{,omment} might leave us with trailing spaces, though; e.g., +% @end itemize @c foo +% will have two active spaces as part of the argument with the +% `itemize'. Here we remove all active spaces from #1, and assign the +% result to \toks0. +% +% This loses if there are any *other* active characters besides spaces +% in the argument -- _ ^ +, for example -- since they get expanded. +% Fortunately, Texinfo does not define any such commands. (If it ever +% does, the catcode of the characters in questionwill have to be changed +% here.) But this means we cannot call \removeactivespaces as part of +% \argremovec{,omment}, since @c uses \parsearg, and thus the argument +% that \parsearg gets might well have any character at all in it. +% +\def\removeactivespaces#1{% + \begingroup + \ignoreactivespaces + \edef\temp{#1}% + \global\toks0 = \expandafter{\temp}% + \endgroup +} + +% Change the active space to expand to nothing. +% +\begingroup + \obeyspaces + \gdef\ignoreactivespaces{\obeyspaces\let =\empty} +\endgroup + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +%% These are used to keep @begin/@end levels from running away +%% Call \inENV within environments (after a \begingroup) +\newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi} +\def\ENVcheck{% +\ifENV\errmessage{Still within an environment; press RETURN to continue} +\endgroup\fi} % This is not perfect, but it should reduce lossage + +% @begin foo is the same as @foo, for now. +\newhelp\EMsimple{Press RETURN to continue.} + +\outer\def\begin{\parsearg\beginxxx} + +\def\beginxxx #1{% +\expandafter\ifx\csname #1\endcsname\relax +{\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else +\csname #1\endcsname\fi} + +% @end foo executes the definition of \Efoo. +% +\def\end{\parsearg\endxxx} +\def\endxxx #1{% + \removeactivespaces{#1}% + \edef\endthing{\the\toks0}% + % + \expandafter\ifx\csname E\endthing\endcsname\relax + \expandafter\ifx\csname \endthing\endcsname\relax + % There's no \foo, i.e., no ``environment'' foo. + \errhelp = \EMsimple + \errmessage{Undefined command `@end \endthing'}% + \else + \unmatchedenderror\endthing + \fi + \else + % Everything's ok; the right environment has been started. + \csname E\endthing\endcsname + \fi +} + +% There is an environment #1, but it hasn't been started. Give an error. +% +\def\unmatchedenderror#1{% + \errhelp = \EMsimple + \errmessage{This `@end #1' doesn't have a matching `@#1'}% +} + +% Define the control sequence \E#1 to give an unmatched @end error. +% +\def\defineunmatchedend#1{% + \expandafter\def\csname E#1\endcsname{\unmatchedenderror{#1}}% +} + + +% Single-spacing is done by various environments (specifically, in +% \nonfillstart and \quotations). +\newskip\singlespaceskip \singlespaceskip = 12.5pt +\def\singlespace{% + % Why was this kern here? It messes up equalizing space above and below + % environments. --karl, 6may93 + %{\advance \baselineskip by -\singlespaceskip + %\kern \baselineskip}% + \setleading \singlespaceskip +} + +%% Simple single-character @ commands + +% @@ prints an @ +% Kludge this until the fonts are right (grr). +\def\@{{\tt\char64}} + +% This is turned off because it was never documented +% and you can use @w{...} around a quote to suppress ligatures. +%% Define @` and @' to be the same as ` and ' +%% but suppressing ligatures. +%\def\`{{`}} +%\def\'{{'}} + +% Used to generate quoted braces. +\def\mylbrace {{\tt\char123}} +\def\myrbrace {{\tt\char125}} +\let\{=\mylbrace +\let\}=\myrbrace +\begingroup + % Definitions to produce actual \{ & \} command in an index. + \catcode`\{ = 12 \catcode`\} = 12 + \catcode`\[ = 1 \catcode`\] = 2 + \catcode`\@ = 0 \catcode`\\ = 12 + @gdef@lbracecmd[\{]% + @gdef@rbracecmd[\}]% +@endgroup + +% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent +% Others are defined by plain TeX: @` @' @" @^ @~ @= @v @H. +\let\, = \c +\let\dotaccent = \. +\def\ringaccent#1{{\accent23 #1}} +\let\tieaccent = \t +\let\ubaraccent = \b +\let\udotaccent = \d + +% Other special characters: @questiondown @exclamdown +% Plain TeX defines: @AA @AE @O @OE @L (and lowercase versions) @ss. +\def\questiondown{?`} +\def\exclamdown{!`} + +% Dotless i and dotless j, used for accents. +\def\imacro{i} +\def\jmacro{j} +\def\dotless#1{% + \def\temp{#1}% + \ifx\temp\imacro \ptexi + \else\ifx\temp\jmacro \j + \else \errmessage{@dotless can be used only with i or j}% + \fi\fi +} + +% Be sure we're in horizontal mode when doing a tie, since we make space +% equivalent to this in @example-like environments. Otherwise, a space +% at the beginning of a line will start with \penalty -- and +% since \penalty is valid in vertical mode, we'd end up putting the +% penalty on the vertical list instead of in the new paragraph. +{\catcode`@ = 11 + % Avoid using \@M directly, because that causes trouble + % if the definition is written into an index file. + \global\let\tiepenalty = \@M + \gdef\tie{\leavevmode\penalty\tiepenalty\ } +} + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\hfil\break\hbox{}\ignorespaces} + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=3000 } + +% @! is an end-of-sentence bang. +\def\!{!\spacefactor=3000 } + +% @? is an end-of-sentence query. +\def\?{?\spacefactor=3000 } + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +\def\group{\begingroup + \ifnum\catcode13=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + % + % The \vtop we start below produces a box with normal height and large + % depth; thus, TeX puts \baselineskip glue before it, and (when the + % next line of text is done) \lineskip glue after it. (See p.82 of + % the TeXbook.) Thus, space below is not quite equal to space + % above. But it's pretty close. + \def\Egroup{% + \egroup % End the \vtop. + \endgroup % End the \group. + }% + % + \vtop\bgroup + % We have to put a strut on the last line in case the @group is in + % the midst of an example, rather than completely enclosing it. + % Otherwise, the interline space between the last line of the group + % and the first line afterwards is too small. But we can't put the + % strut in \Egroup, since there it would be on a line by itself. + % Hence this just inserts a strut at the beginning of each line. + \everypar = {\strut}% + % + % Since we have a strut on every line, we don't need any of TeX's + % normal interline spacing. + \offinterlineskip + % + % OK, but now we have to do something about blank + % lines in the input in @example-like environments, which normally + % just turn into \lisppar, which will insert no space now that we've + % turned off the interline space. Simplest is to make them be an + % empty paragraph. + \ifx\par\lisppar + \edef\par{\leavevmode \par}% + % + % Reset ^^M's definition to new definition of \par. + \obeylines + \fi + % + % Do @comment since we are called inside an environment such as + % @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +\def\need{\parsearg\needx} + +% Old definition--didn't work. +%\def\needx #1{\par % +%% This method tries to make TeX break the page naturally +%% if the depth of the box does not fit. +%{\baselineskip=0pt% +%\vtop to #1\mil{\vfil}\kern -#1\mil\nobreak +%\prevdepth=-1000pt +%}} + +\def\needx#1{% + % Ensure vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % If the @need value is less than one line space, it's useless. + \dimen0 = #1\mil + \dimen2 = \ht\strutbox + \advance\dimen2 by \dp\strutbox + \ifdim\dimen0 > \dimen2 + % + % Do a \strut just to make the height of this box be normal, so the + % normal leading is inserted relative to the preceding line. + % And a page break here is fine. + \vtop to #1\mil{\strut\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak + \fi +} + +% @br forces paragraph break + +\let\br = \par + +% @dots{} output an ellipsis using the current font. +% We do .5em per period so that it has the same spacing in a typewriter +% font as three actual period characters. +% +\def\dots{% + \leavevmode + \hbox to 1.5em{% + \hskip 0pt plus 0.25fil minus 0.25fil + .\hss.\hss.% + \hskip 0pt plus 0.5fil minus 0.5fil + }% +} + +% @enddots{} is an end-of-sentence ellipsis. +% +\def\enddots{% + \leavevmode + \hbox to 2em{% + \hskip 0pt plus 0.25fil minus 0.25fil + .\hss.\hss.\hss.% + \hskip 0pt plus 0.5fil minus 0.5fil + }% + \spacefactor=3000 +} + + +% @page forces the start of a new page +% +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\def\exdent{\parsearg\exdentyyy} +\def\exdentyyy #1{{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}} + +% This defn is used inside nofill environments such as @example. +\def\nofillexdent{\parsearg\nofillexdentyyy} +\def\nofillexdentyyy #1{{\advance \leftskip by -\exdentamount +\leftline{\hskip\leftskip{\rm#1}}}} + +% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current +% paragraph. For more general purposes, use the \margin insertion +% class. WHICH is `l' or `r'. +% +\newskip\inmarginspacing \inmarginspacing=1cm +\def\strutdepth{\dp\strutbox} +% +\def\doinmargin#1#2{\strut\vadjust{% + \nobreak + \kern-\strutdepth + \vtop to \strutdepth{% + \baselineskip=\strutdepth + \vss + % if you have multiple lines of stuff to put here, you'll need to + % make the vbox yourself of the appropriate size. + \ifx#1l% + \llap{\ignorespaces #2\hskip\inmarginspacing}% + \else + \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% + \fi + \null + }% +}} +\def\inleftmargin{\doinmargin l} +\def\inrightmargin{\doinmargin r} +% +% @inmargin{TEXT [, RIGHT-TEXT]} +% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; +% else use TEXT for both). +% +\def\inmargin#1{\parseinmargin #1,,\finish} +\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \def\lefttext{#1}% have both texts + \def\righttext{#2}% + \else + \def\lefttext{#1}% have only one text + \def\righttext{#1}% + \fi + % + \ifodd\pageno + \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin + \else + \def\temp{\inleftmargin\lefttext}% + \fi + \temp +} + +% @include file insert text of that file as input. +% Allow normal characters that we make active in the argument (a file name). +\def\include{\begingroup + \catcode`\\=12 + \catcode`~=12 + \catcode`^=12 + \catcode`_=12 + \catcode`|=12 + \catcode`<=12 + \catcode`>=12 + \catcode`+=12 + \parsearg\includezzz} +% Restore active chars for included file. +\def\includezzz#1{\endgroup\begingroup + % Read the included file in a group so nested @include's work. + \def\thisfile{#1}% + \input\thisfile +\endgroup} + +\def\thisfile{} + +% @center line outputs that line, centered + +\def\center{\parsearg\centerzzz} +\def\centerzzz #1{{\advance\hsize by -\leftskip +\advance\hsize by -\rightskip +\centerline{#1}}} + +% @sp n outputs n lines of vertical space + +\def\sp{\parsearg\spxxx} +\def\spxxx #1{\vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment + +\def\comment{\begingroup \catcode`\^^M=\other% +\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% +\commentxxx} +{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}} + +\let\c=\comment + +% @paragraphindent NCHARS +% We'll use ems for NCHARS, close enough. +% We cannot implement @paragraphindent asis, though. +% +\def\asisword{asis} % no translation, these are keywords +\def\noneword{none} +% +\def\paragraphindent{\parsearg\doparagraphindent} +\def\doparagraphindent#1{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \defaultparindent = 0pt + \else + \defaultparindent = #1em + \fi + \fi + \parindent = \defaultparindent +} + +% @exampleindent NCHARS +% We'll use ems for NCHARS like @paragraphindent. +% It seems @exampleindent asis isn't necessary, but +% I preserve it to make it similar to @paragraphindent. +\def\exampleindent{\parsearg\doexampleindent} +\def\doexampleindent#1{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \lispnarrowing = 0pt + \else + \lispnarrowing = #1em + \fi + \fi +} + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math means output in math mode. +% We don't use $'s directly in the definition of \math because control +% sequences like \math are expanded when the toc file is written. Then, +% we read the toc file back, the $'s will be normal characters (as they +% should be, according to the definition of Texinfo). So we must use a +% control sequence to switch into and out of math mode. +% +% This isn't quite enough for @math to work properly in indices, but it +% seems unlikely it will ever be needed there. +% +\let\implicitmath = $ +\def\math#1{\implicitmath #1\implicitmath} + +% @bullet and @minus need the same treatment as @math, just above. +\def\bullet{\implicitmath\ptexbullet\implicitmath} +\def\minus{\implicitmath-\implicitmath} + +% @refill is a no-op. +\let\refill=\relax + +% If working on a large document in chapters, it is convenient to +% be able to disable indexing, cross-referencing, and contents, for test runs. +% This is done with @novalidate (before @setfilename). +% +\newif\iflinks \linkstrue % by default we want the aux files. +\let\novalidate = \linksfalse + +% @setfilename is done at the beginning of every texinfo file. +% So open here the files we need to have open while reading the input. +% This makes it possible to make a .fmt file for texinfo. +\def\setfilename{% + \iflinks + \readauxfile + \fi % \openindices needs to do some work in any case. + \openindices + \fixbackslash % Turn off hack to swallow `\input texinfo'. + \global\let\setfilename=\comment % Ignore extra @setfilename cmds. + % + % If texinfo.cnf is present on the system, read it. + % Useful for site-wide @afourpaper, etc. + % Just to be on the safe side, close the input stream before the \input. + \openin 1 texinfo.cnf + \ifeof1 \let\temp=\relax \else \def\temp{\input texinfo.cnf }\fi + \closein1 + \temp + % + \comment % Ignore the actual filename. +} + +% Called from \setfilename. +% +\def\openindices{% + \newindex{cp}% + \newcodeindex{fn}% + \newcodeindex{vr}% + \newcodeindex{tp}% + \newcodeindex{ky}% + \newcodeindex{pg}% +} + +% @bye. +\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} + + +\message{pdf,} +% adobe `portable' document format +\newcount\tempnum +\newcount\lnkcount +\newtoks\filename +\newcount\filenamelength +\newcount\pgn +\newtoks\toksA +\newtoks\toksB +\newtoks\toksC +\newtoks\toksD +\newbox\boxA +\newcount\countA +\newif\ifpdf +\newif\ifpdfmakepagedest + +\ifx\pdfoutput\undefined + \pdffalse + \let\pdfmkdest = \gobble + \let\pdfurl = \gobble + \let\endlink = \relax + \let\linkcolor = \relax + \let\pdfmakeoutlines = \relax +\else + \pdftrue + \pdfoutput = 1 + \input pdfcolor + \def\dopdfimage#1#2#3{% + \def\imagewidth{#2}% + \def\imageheight{#3}% + \ifnum\pdftexversion < 14 + \pdfimage + \else + \pdfximage + \fi + \ifx\empty\imagewidth\else width \imagewidth \fi + \ifx\empty\imageheight\else height \imageheight \fi + \ifnum\pdftexversion<13 + #1.pdf% + \else + {#1.pdf}% + \fi + \ifnum\pdftexversion < 14 \else + \pdfrefximage \pdflastximage + \fi} + \def\pdfmkdest#1{\pdfdest name{#1} xyz} + \def\pdfmkpgn#1{#1@} + \let\linkcolor = \Blue % was Cyan, but that seems light? + \def\endlink{\Black\pdfendlink} + % Adding outlines to PDF; macros for calculating structure of outlines + % come from Petr Olsak + \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% + \else \csname#1\endcsname \fi} + \def\advancenumber#1{\tempnum=\expnumber{#1}\relax + \advance\tempnum by1 + \expandafter\xdef\csname#1\endcsname{\the\tempnum}} + \def\pdfmakeoutlines{{% + \openin 1 \jobname.toc + \ifeof 1\else\bgroup + \closein 1 + \indexnofonts + \def\tt{} + \let\_ = \normalunderscore + % Thanh's hack / proper braces in bookmarks + \edef\mylbrace{\iftrue \string{\else}\fi}\let\{=\mylbrace + \edef\myrbrace{\iffalse{\else\string}\fi}\let\}=\myrbrace + % + \def\chapentry ##1##2##3{} + \def\unnumbchapentry ##1##2{} + \def\secentry ##1##2##3##4{\advancenumber{chap##2}} + \def\unnumbsecentry ##1##2{} + \def\subsecentry ##1##2##3##4##5{\advancenumber{sec##2.##3}} + \def\unnumbsubsecentry ##1##2{} + \def\subsubsecentry ##1##2##3##4##5##6{\advancenumber{subsec##2.##3.##4}} + \def\unnumbsubsubsecentry ##1##2{} + \input \jobname.toc + \def\chapentry ##1##2##3{% + \pdfoutline goto name{\pdfmkpgn{##3}}count-\expnumber{chap##2}{##1}} + \def\unnumbchapentry ##1##2{% + \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} + \def\secentry ##1##2##3##4{% + \pdfoutline goto name{\pdfmkpgn{##4}}count-\expnumber{sec##2.##3}{##1}} + \def\unnumbsecentry ##1##2{% + \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} + \def\subsecentry ##1##2##3##4##5{% + \pdfoutline goto name{\pdfmkpgn{##5}}count-\expnumber{subsec##2.##3.##4}{##1}} + \def\unnumbsubsecentry ##1##2{% + \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} + \def\subsubsecentry ##1##2##3##4##5##6{% + \pdfoutline goto name{\pdfmkpgn{##6}}{##1}} + \def\unnumbsubsubsecentry ##1##2{% + \pdfoutline goto name{\pdfmkpgn{##2}}{##1}} + \input \jobname.toc + \egroup\fi + }} + \def\makelinks #1,{% + \def\params{#1}\def\E{END}% + \ifx\params\E + \let\nextmakelinks=\relax + \else + \let\nextmakelinks=\makelinks + \ifnum\lnkcount>0,\fi + \picknum{#1}% + \startlink attr{/Border [0 0 0]} + goto name{\pdfmkpgn{\the\pgn}}% + \linkcolor #1% + \advance\lnkcount by 1% + \endlink + \fi + \nextmakelinks + } + \def\picknum#1{\expandafter\pn#1} + \def\pn#1{% + \def\p{#1}% + \ifx\p\lbrace + \let\nextpn=\ppn + \else + \let\nextpn=\ppnn + \def\first{#1} + \fi + \nextpn + } + \def\ppn#1{\pgn=#1\gobble} + \def\ppnn{\pgn=\first} + \def\pdfmklnk#1{\lnkcount=0\makelinks #1,END,} + \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} + \def\skipspaces#1{\def\PP{#1}\def\D{|}% + \ifx\PP\D\let\nextsp\relax + \else\let\nextsp\skipspaces + \ifx\p\space\else\addtokens{\filename}{\PP}% + \advance\filenamelength by 1 + \fi + \fi + \nextsp} + \def\getfilename#1{\filenamelength=0\expandafter\skipspaces#1|\relax} + \ifnum\pdftexversion < 14 + \let \startlink \pdfannotlink + \else + \let \startlink \pdfstartlink + \fi + \def\pdfurl#1{% + \begingroup + \normalturnoffactive\def\@{@}% + \let\value=\expandablevalue + \leavevmode\Red + \startlink attr{/Border [0 0 0]}% + user{/Subtype /Link /A << /S /URI /URI (#1) >>}% + % #1 + \endgroup} + \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} + \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} + \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} + \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} + \def\maketoks{% + \expandafter\poptoks\the\toksA|ENDTOKS| + \ifx\first0\adn0 + \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 + \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 + \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 + \else + \ifnum0=\countA\else\makelink\fi + \ifx\first.\let\next=\done\else + \let\next=\maketoks + \addtokens{\toksB}{\the\toksD} + \ifx\first,\addtokens{\toksB}{\space}\fi + \fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \next} + \def\makelink{\addtokens{\toksB}% + {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} + \def\pdflink#1{% + \startlink attr{/Border [0 0 0]} goto name{\mkpgn{#1}} + \linkcolor #1\endlink} + \def\mkpgn#1{#1@} + \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} +\fi % \ifx\pdfoutput + + +\message{fonts,} +% Font-change commands. + +% Texinfo sort of supports the sans serif font style, which plain TeX does not. +% So we set up a \sf analogous to plain's \rm, etc. +\newfam\sffam +\def\sf{\fam=\sffam \tensf} +\let\li = \sf % Sometimes we call it \li, not \sf. + +% We don't need math for this one. +\def\ttsl{\tenttsl} + +% Use Computer Modern fonts at \magstephalf (11pt). +\newcount\mainmagstep +\mainmagstep=\magstephalf + +% Set the font macro #1 to the font named #2, adding on the +% specified font prefix (normally `cm'). +% #3 is the font's design size, #4 is a scale factor +\def\setfont#1#2#3#4{\font#1=\fontprefix#2#3 scaled #4} + +% Use cm as the default font prefix. +% To specify the font prefix, you must define \fontprefix +% before you read in texinfo.tex. +\ifx\fontprefix\undefined +\def\fontprefix{cm} +\fi +% Support font families that don't use the same naming scheme as CM. +\def\rmshape{r} +\def\rmbshape{bx} %where the normal face is bold +\def\bfshape{b} +\def\bxshape{bx} +\def\ttshape{tt} +\def\ttbshape{tt} +\def\ttslshape{sltt} +\def\itshape{ti} +\def\itbshape{bxti} +\def\slshape{sl} +\def\slbshape{bxsl} +\def\sfshape{ss} +\def\sfbshape{ss} +\def\scshape{csc} +\def\scbshape{csc} + +\ifx\bigger\relax +\let\mainmagstep=\magstep1 +\setfont\textrm\rmshape{12}{1000} +\setfont\texttt\ttshape{12}{1000} +\else +\setfont\textrm\rmshape{10}{\mainmagstep} +\setfont\texttt\ttshape{10}{\mainmagstep} +\fi +% Instead of cmb10, you many want to use cmbx10. +% cmbx10 is a prettier font on its own, but cmb10 +% looks better when embedded in a line with cmr10. +\setfont\textbf\bfshape{10}{\mainmagstep} +\setfont\textit\itshape{10}{\mainmagstep} +\setfont\textsl\slshape{10}{\mainmagstep} +\setfont\textsf\sfshape{10}{\mainmagstep} +\setfont\textsc\scshape{10}{\mainmagstep} +\setfont\textttsl\ttslshape{10}{\mainmagstep} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep + +% A few fonts for @defun, etc. +\setfont\defbf\bxshape{10}{\magstep1} %was 1314 +\setfont\deftt\ttshape{10}{\magstep1} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\setfont\smallrm\rmshape{9}{1000} +\setfont\smalltt\ttshape{9}{1000} +\setfont\smallbf\bfshape{10}{900} +\setfont\smallit\itshape{9}{1000} +\setfont\smallsl\slshape{9}{1000} +\setfont\smallsf\sfshape{9}{1000} +\setfont\smallsc\scshape{10}{900} +\setfont\smallttsl\ttslshape{10}{900} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 + +% Fonts for title page: +\setfont\titlerm\rmbshape{12}{\magstep3} +\setfont\titleit\itbshape{10}{\magstep4} +\setfont\titlesl\slbshape{10}{\magstep4} +\setfont\titlett\ttbshape{12}{\magstep3} +\setfont\titlettsl\ttslshape{10}{\magstep4} +\setfont\titlesf\sfbshape{17}{\magstep1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\authorrm{\secrm} + +% Chapter (and unnumbered) fonts (17.28pt). +\setfont\chaprm\rmbshape{12}{\magstep2} +\setfont\chapit\itbshape{10}{\magstep3} +\setfont\chapsl\slbshape{10}{\magstep3} +\setfont\chaptt\ttbshape{12}{\magstep2} +\setfont\chapttsl\ttslshape{10}{\magstep3} +\setfont\chapsf\sfbshape{17}{1000} +\let\chapbf=\chaprm +\setfont\chapsc\scbshape{10}{\magstep3} +\font\chapi=cmmi12 scaled \magstep2 +\font\chapsy=cmsy10 scaled \magstep3 + +% Section fonts (14.4pt). +\setfont\secrm\rmbshape{12}{\magstep1} +\setfont\secit\itbshape{10}{\magstep2} +\setfont\secsl\slbshape{10}{\magstep2} +\setfont\sectt\ttbshape{12}{\magstep1} +\setfont\secttsl\ttslshape{10}{\magstep2} +\setfont\secsf\sfbshape{12}{\magstep1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep2} +\font\seci=cmmi12 scaled \magstep1 +\font\secsy=cmsy10 scaled \magstep2 + +% \setfont\ssecrm\bxshape{10}{\magstep1} % This size an font looked bad. +% \setfont\ssecit\itshape{10}{\magstep1} % The letters were too crowded. +% \setfont\ssecsl\slshape{10}{\magstep1} +% \setfont\ssectt\ttshape{10}{\magstep1} +% \setfont\ssecsf\sfshape{10}{\magstep1} + +%\setfont\ssecrm\bfshape{10}{1315} % Note the use of cmb rather than cmbx. +%\setfont\ssecit\itshape{10}{1315} % Also, the size is a little larger than +%\setfont\ssecsl\slshape{10}{1315} % being scaled magstep1. +%\setfont\ssectt\ttshape{10}{1315} +%\setfont\ssecsf\sfshape{10}{1315} + +%\let\ssecbf=\ssecrm + +% Subsection fonts (13.15pt). +\setfont\ssecrm\rmbshape{12}{\magstephalf} +\setfont\ssecit\itbshape{10}{1315} +\setfont\ssecsl\slbshape{10}{1315} +\setfont\ssectt\ttbshape{12}{\magstephalf} +\setfont\ssecttsl\ttslshape{10}{1315} +\setfont\ssecsf\sfbshape{12}{\magstephalf} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{\magstep1} +\font\sseci=cmmi12 scaled \magstephalf +\font\ssecsy=cmsy10 scaled 1315 +% The smallcaps and symbol fonts should actually be scaled \magstep1.5, +% but that is not a standard magnification. + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. Since +% texinfo doesn't allow for producing subscripts and superscripts, we +% don't bother to reset \scriptfont and \scriptscriptfont (which would +% also require loading a lot more fonts). +% +\def\resetmathfonts{% + \textfont0 = \tenrm \textfont1 = \teni \textfont2 = \tensy + \textfont\itfam = \tenit \textfont\slfam = \tensl \textfont\bffam = \tenbf + \textfont\ttfam = \tentt \textfont\sffam = \tensf +} + + +% The font-changing commands redefine the meanings of \tenSTYLE, instead +% of just \STYLE. We do this so that font changes will continue to work +% in math mode, where it is the current \fam that is relevant in most +% cases, not the current font. Plain TeX does \def\bf{\fam=\bffam +% \tenbf}, for example. By redefining \tenbf, we obviate the need to +% redefine \bf itself. +\def\textfonts{% + \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl + \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy \let\tenttsl=\textttsl + \resetmathfonts} +\def\titlefonts{% + \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl + \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc + \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy + \let\tenttsl=\titlettsl + \resetmathfonts \setleading{25pt}} +\def\titlefont#1{{\titlefonts\rm #1}} +\def\chapfonts{% + \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl + \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc + \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy \let\tenttsl=\chapttsl + \resetmathfonts \setleading{19pt}} +\def\secfonts{% + \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl + \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy \let\tenttsl=\secttsl + \resetmathfonts \setleading{16pt}} +\def\subsecfonts{% + \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl + \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy \let\tenttsl=\ssecttsl + \resetmathfonts \setleading{15pt}} +\let\subsubsecfonts = \subsecfonts % Maybe make sssec fonts scaled magstephalf? +\def\smallfonts{% + \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl + \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc + \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy + \let\tenttsl=\smallttsl + \resetmathfonts \setleading{11pt}} + +% Set up the default fonts, so we can use them for creating boxes. +% +\textfonts + +% Define these so they can be easily changed for other fonts. +\def\angleleft{$\langle$} +\def\angleright{$\rangle$} + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +% Fonts for short table of contents. +\setfont\shortcontrm\rmshape{12}{1000} +\setfont\shortcontbf\bxshape{12}{1000} +\setfont\shortcontsl\slshape{12}{1000} + +%% Add scribe-like font environments, plus @l for inline lisp (usually sans +%% serif) and @ii for TeX italic + +% \smartitalic{ARG} outputs arg in italics, followed by an italic correction +% unless the following character is such as not to need one. +\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else\/\fi\fi\fi} +\def\smartslanted#1{{\sl #1}\futurelet\next\smartitalicx} +\def\smartitalic#1{{\it #1}\futurelet\next\smartitalicx} + +\let\i=\smartitalic +\let\var=\smartslanted +\let\dfn=\smartslanted +\let\emph=\smartitalic +\let\cite=\smartslanted + +\def\b#1{{\bf #1}} +\let\strong=\b + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +\def\t#1{% + {\tt \rawbackslash \frenchspacing #1}% + \null +} +\let\ttfont=\t +\def\samp#1{`\tclose{#1}'\null} +\setfont\keyrm\rmshape{8}{1000} +\font\keysy=cmsy9 +\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% + \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% + \vbox{\hrule\kern-0.4pt + \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% + \kern-0.4pt\hrule}% + \kern-.06em\raise0.4pt\hbox{\angleright}}}} +% The old definition, with no lozenge: +%\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null} +\def\ctrl #1{{\tt \rawbackslash \hat}#1} + +% @file, @option are the same as @samp. +\let\file=\samp +\let\option=\samp + +% @code is a modification of @t, +% which makes spaces the same size as normal in the surrounding text. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \rawbackslash + \frenchspacing + #1% + }% + \null +} + +% We *must* turn on hyphenation at `-' and `_' in \code. +% Otherwise, it is too hard to avoid overfull hboxes +% in the Emacs manual, the Library manual, etc. + +% Unfortunately, TeX uses one parameter (\hyphenchar) to control +% both hyphenation at - and hyphenation within words. +% We must therefore turn them both off (\tclose does that) +% and arrange explicitly to hyphenate at a dash. +% -- rms. +{ + \catcode`\-=\active + \catcode`\_=\active + % + \global\def\code{\begingroup + \catcode`\-=\active \let-\codedash + \catcode`\_=\active \let_\codeunder + \codex + } + % + % If we end up with any active - characters when handling the index, + % just treat them as a normal -. + \global\def\indexbreaks{\catcode`\-=\active \let-\realdash} +} + +\def\realdash{-} +\def\codedash{-\discretionary{}{}{}} +\def\codeunder{\ifusingtt{\normalunderscore\discretionary{}{}{}}{\_}} +\def\codex #1{\tclose{#1}\endgroup} + +%\let\exp=\tclose %Was temporary + +% @kbd is like @code, except that if the argument is just one @key command, +% then @kbd has no effect. + +% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), +% `example' (@kbd uses ttsl only inside of @example and friends), +% or `code' (@kbd uses normal tty font always). +\def\kbdinputstyle{\parsearg\kbdinputstylexxx} +\def\kbdinputstylexxx#1{% + \def\arg{#1}% + \ifx\arg\worddistinct + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% + \else\ifx\arg\wordexample + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% + \else\ifx\arg\wordcode + \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% + \fi\fi\fi +} +\def\worddistinct{distinct} +\def\wordexample{example} +\def\wordcode{code} + +% Default is kbdinputdistinct. (Too much of a hassle to call the macro, +% the catcodes are wrong for parsearg to work.) +\gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl} + +\def\xkey{\key} +\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}% +\ifx\one\xkey\ifx\threex\three \key{#2}% +\else{\tclose{\kbdfont\look}}\fi +\else{\tclose{\kbdfont\look}}\fi} + +% For @url, @env, @command quotes seem unnecessary, so use \code. +\let\url=\code +\let\env=\code +\let\command=\code + +% @uref (abbreviation for `urlref') takes an optional (comma-separated) +% second argument specifying the text to display and an optional third +% arg as text to display instead of (rather than in addition to) the url +% itself. First (mandatory) arg is the url. Perhaps eventually put in +% a hypertex \special here. +% +\def\uref#1{\douref #1,,,\finish} +\def\douref#1,#2,#3,#4\finish{\begingroup + \unsepspaces + \pdfurl{#1}% + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt + \unhbox0 % third arg given, show only that + \else + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \ifpdf + \unhbox0 % PDF: 2nd arg given, show only it + \else + \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url + \fi + \else + \code{#1}% only url given, so show it + \fi + \fi + \endlink +\endgroup} + +% rms does not like angle brackets --karl, 17may97. +% So now @email is just like @uref, unless we are pdf. +% +%\def\email#1{\angleleft{\tt #1}\angleright} +\ifpdf + \def\email#1{\doemail#1,,\finish} + \def\doemail#1,#2,#3\finish{\begingroup + \unsepspaces + \pdfurl{mailto:#1}% + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi + \endlink + \endgroup} +\else + \let\email=\uref +\fi + +% Check if we are currently using a typewriter font. Since all the +% Computer Modern typewriter fonts have zero interword stretch (and +% shrink), and it is reasonable to expect all typewriter fonts to have +% this property, we can check that font parameter. +% +\def\ifmonospace{\ifdim\fontdimen3\font=0pt } + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par} + +% @l was never documented to mean ``switch to the Lisp font'', +% and it is not used as such in any manual I can find. We need it for +% Polish suppressed-l. --karl, 22sep96. +%\def\l#1{{\li #1}\null} + +% Explicit font changes: @r, @sc, undocumented @ii. +\def\r#1{{\rm #1}} % roman font +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +% @acronym downcases the argument and prints in smallcaps. +\def\acronym#1{{\smallcaps \lowercase{#1}}} + +% @pounds{} is a sterling sign. +\def\pounds{{\it\$}} + + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\newif\ifseenauthor +\newif\iffinishedtitlepage + +% Do an implicit @contents or @shortcontents after @end titlepage if the +% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage. +% +\newif\ifsetcontentsaftertitlepage + \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue +\newif\ifsetshortcontentsaftertitlepage + \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue + +\def\shorttitlepage{\parsearg\shorttitlepagezzz} +\def\shorttitlepagezzz #1{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\def\titlepage{\begingroup \parindent=0pt \textfonts + \let\subtitlerm=\tenrm + \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}% + % + \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}% + % + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % + % Now you can print the title using @title. + \def\title{\parsearg\titlezzz}% + \def\titlezzz##1{\leftline{\titlefonts\rm ##1} + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt}% + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Now you can put text using @subtitle. + \def\subtitle{\parsearg\subtitlezzz}% + \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}% + % + % @author should come last, but may come many times. + \def\author{\parsearg\authorzzz}% + \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi + {\authorfont \leftline{##1}}}% + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \oldpage + \let\page = \oldpage + \hbox{}}% +% \def\page{\oldpage \hbox{}} +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + % + % If they want short, they certainly want long too. + \ifsetshortcontentsaftertitlepage + \shortcontents + \contents + \global\let\shortcontents = \relax + \global\let\contents = \relax + \fi + % + \ifsetcontentsaftertitlepage + \contents + \global\let\contents = \relax + \global\let\shortcontents = \relax + \fi + % + \ifpdf \pdfmakepagedesttrue \fi + % + \HEADINGSon +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +%%% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks\evenheadline % headline on even pages +\newtoks\oddheadline % headline on odd pages +\newtoks\evenfootline % footline on even pages +\newtoks\oddfootline % footline on odd pages + +% Now make Tex use those variables +\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline + \else \the\evenheadline \fi}} +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + +\def\evenheading{\parsearg\evenheadingxxx} +\def\oddheading{\parsearg\oddheadingxxx} +\def\everyheading{\parsearg\everyheadingxxx} + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\oddfooting{\parsearg\oddfootingxxx} +\def\everyfooting{\parsearg\everyfootingxxx} + +{\catcode`\@=0 % + +\gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish} +\gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish} +\gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{% +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\everyheadingxxx#1{\oddheadingxxx{#1}\evenheadingxxx{#1}}% + +\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish} +\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish} +\gdef\oddfootingyyy #1@|#2@|#3@|#4\finish{% + \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% + % + % Leave some space for the footline. Hopefully ok to assume + % @evenfooting will not be used by itself. + \global\advance\pageheight by -\baselineskip + \global\advance\vsize by -\baselineskip +} + +\gdef\everyfootingxxx#1{\oddfootingxxx{#1}\evenfootingxxx{#1}} +% +}% unbind the catcode of @. + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off at the start of a document, +% and turned `on' after @end titlepage. + +\def\headings #1 {\csname HEADINGS#1\endcsname} + +\def\HEADINGSoff{ +\global\evenheadline={\hfil} \global\evenfootline={\hfil} +\global\oddheadline={\hfil} \global\oddfootline={\hfil}} +\HEADINGSoff +% When we turn headings on, set the page number to 1. +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{ +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} +\let\contentsalignmacro = \chappager + +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{ +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} + +% Subroutines used in generating headings +% This produces Day Month Year style of output. +% Only define if not already defined, in case a txi-??.tex file has set +% up a different format (e.g., txi-cs.tex does this). +\ifx\today\undefined +\def\today{% + \number\day\space + \ifcase\month + \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr + \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug + \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec + \fi + \space\number\year} +\fi + +% @settitle line... specifies the title of the document, for headings. +% It generates no output of its own. +\def\thistitle{\putwordNoTitle} +\def\settitle{\parsearg\settitlezzz} +\def\settitlezzz #1{\gdef\thistitle{#1}} + + +\message{tables,} +% Tables -- @table, @ftable, @vtable, @item(x), @kitem(x), @xitem(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @vtable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\newif\ifitemxneedsnegativevskip + +\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\itemxpar \parsearg\itemzzz} + +\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz} +\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \itemxpar \parsearg\xitemzzz} + +\def\internalBkitem{\smallbreak \parsearg\kitemzzz} +\def\internalBkitemx{\itemxpar \parsearg\kitemzzz} + +\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}% + \itemzzz {#1}} + +\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}% + \itemzzz {#1}} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemfont{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + % + % Make this a paragraph so we get the \parskip glue and wrapping, + % but leave it ragged-right. + \begingroup + \advance\leftskip by-\tableindent + \advance\hsize by\tableindent + \advance\rightskip by0pt plus1fil + \leavevmode\unhbox0\par + \endgroup + % + % We're going to be starting a paragraph, but we don't want the + % \parskip glue -- logically it's part of the @item we just started. + \nobreak \vskip-\parskip + % + % Stop a page break at the \parskip glue coming up. Unfortunately + % we can't prevent a possible page break at the following + % \baselineskip glue. + \nobreak + \endgroup + \itemxneedsnegativevskipfalse + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. + \noindent + % Do this with kerns and \unhbox so that if there is a footnote in + % the item text, it can migrate to the main vertical list and + % eventually be printed. + \nobreak\kern-\tableindent + \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 + \unhbox0 + \nobreak\kern\dimen0 + \endgroup + \itemxneedsnegativevskiptrue + \fi +} + +\def\item{\errmessage{@item while not in a table}} +\def\itemx{\errmessage{@itemx while not in a table}} +\def\kitem{\errmessage{@kitem while not in a table}} +\def\kitemx{\errmessage{@kitemx while not in a table}} +\def\xitem{\errmessage{@xitem while not in a table}} +\def\xitemx{\errmessage{@xitemx while not in a table}} + +% Contains a kludge to get @end[description] to work. +\def\description{\tablez{\dontindex}{1}{}{}{}{}} + +% @table, @ftable, @vtable. +\def\table{\begingroup\inENV\obeylines\obeyspaces\tablex} +{\obeylines\obeyspaces% +\gdef\tablex #1^^M{% +\tabley\dontindex#1 \endtabley}} + +\def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex} +{\obeylines\obeyspaces% +\gdef\ftablex #1^^M{% +\tabley\fnitemindex#1 \endtabley +\def\Eftable{\endgraf\afterenvbreak\endgroup}% +\let\Etable=\relax}} + +\def\vtable{\begingroup\inENV\obeylines\obeyspaces\vtablex} +{\obeylines\obeyspaces% +\gdef\vtablex #1^^M{% +\tabley\vritemindex#1 \endtabley +\def\Evtable{\endgraf\afterenvbreak\endgroup}% +\let\Etable=\relax}} + +\def\dontindex #1{} +\def\fnitemindex #1{\doind {fn}{\code{#1}}}% +\def\vritemindex #1{\doind {vr}{\code{#1}}}% + +{\obeyspaces % +\gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup% +\tablez{#1}{#2}{#3}{#4}{#5}{#6}}} + +\def\tablez #1#2#3#4#5#6{% +\aboveenvbreak % +\begingroup % +\def\Edescription{\Etable}% Necessary kludge. +\let\itemindex=#1% +\ifnum 0#3>0 \advance \leftskip by #3\mil \fi % +\ifnum 0#4>0 \tableindent=#4\mil \fi % +\ifnum 0#5>0 \advance \rightskip by #5\mil \fi % +\def\itemfont{#2}% +\itemmax=\tableindent % +\advance \itemmax by -\itemmargin % +\advance \leftskip by \tableindent % +\exdentamount=\tableindent +\parindent = 0pt +\parskip = \smallskipamount +\ifdim \parskip=0pt \parskip=2pt \fi% +\def\Etable{\endgraf\afterenvbreak\endgroup}% +\let\item = \internalBitem % +\let\itemx = \internalBitemx % +\let\kitem = \internalBkitem % +\let\kitemx = \internalBkitemx % +\let\xitem = \internalBxitem % +\let\xitemx = \internalBxitemx % +} + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\def\itemize{\parsearg\itemizezzz} + +\def\itemizezzz #1{% + \begingroup % ended by the @end itemize + \itemizey {#1}{\Eitemize} +} + +\def\itemizey #1#2{% +\aboveenvbreak % +\itemmax=\itemindent % +\advance \itemmax by -\itemmargin % +\advance \leftskip by \itemindent % +\exdentamount=\itemindent +\parindent = 0pt % +\parskip = \smallskipamount % +\ifdim \parskip=0pt \parskip=2pt \fi% +\def#2{\endgraf\afterenvbreak\endgroup}% +\def\itemcontents{#1}% +\let\item=\itemizeitem} + +% Set sfcode to normal for the chars that usually have another value. +% These are `.?!:;,' +\def\frenchspacing{\sfcode46=1000 \sfcode63=1000 \sfcode33=1000 + \sfcode58=1000 \sfcode59=1000 \sfcode44=1000 } + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\def\enumerate{\parsearg\enumeratezzz} +\def\enumeratezzz #1{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + \begingroup % ended by the @end enumerate + % + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a . + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call itemizey, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \itemizey{#1.}\Eenumerate\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + +% Definition of @item while inside @itemize. + +\def\itemizeitem{% +\advance\itemno by 1 +{\let\par=\endgraf \smallbreak}% +\ifhmode \errmessage{In hmode at itemizeitem}\fi +{\parskip=0in \hskip 0pt +\hbox to 0pt{\hss \itemcontents\hskip \itemmargin}% +\vadjust{\penalty 1200}}% +\flushcr} + +% @multitable macros +% Amy Hendrickson, 8/18/94, 3/6/96 +% +% @multitable ... @end multitable will make as many columns as desired. +% Contents of each column will wrap at width given in preamble. Width +% can be specified either with sample text given in a template line, +% or in percent of \hsize, the current width of text on page. + +% Table can continue over pages but will only break between lines. + +% To make preamble: +% +% Either define widths of columns in terms of percent of \hsize: +% @multitable @columnfractions .25 .3 .45 +% @item ... +% +% Numbers following @columnfractions are the percent of the total +% current hsize to be used for each column. You may use as many +% columns as desired. + + +% Or use a template: +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item ... +% using the widest term desired in each column. +% +% For those who want to use more than one line's worth of words in +% the preamble, break the line within one argument and it +% will parse correctly, i.e., +% +% @multitable {Column 1 template} {Column 2 template} {Column 3 +% template} +% Not: +% @multitable {Column 1 template} {Column 2 template} +% {Column 3 template} + +% Each new table line starts with @item, each subsequent new column +% starts with @tab. Empty columns may be produced by supplying @tab's +% with nothing between them for as many times as empty columns are needed, +% ie, @tab@tab@tab will produce two empty columns. + +% @item, @tab, @multitable or @end multitable do not need to be on their +% own lines, but it will not hurt if they are. + +% Sample multitable: + +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item first col stuff @tab second col stuff @tab third col +% @item +% first col stuff +% @tab +% second col stuff +% @tab +% third col +% @item first col stuff @tab second col stuff +% @tab Many paragraphs of text may be used in any column. +% +% They will wrap at the width determined by the template. +% @item@tab@tab This will be in third column. +% @end multitable + +% Default dimensions may be reset by user. +% @multitableparskip is vertical space between paragraphs in table. +% @multitableparindent is paragraph indent in table. +% @multitablecolmargin is horizontal space to be left between columns. +% @multitablelinespace is space to leave between table items, baseline +% to baseline. +% 0pt means it depends on current normal line spacing. +% +\newskip\multitableparskip +\newskip\multitableparindent +\newdimen\multitablecolspace +\newskip\multitablelinespace +\multitableparskip=0pt +\multitableparindent=6pt +\multitablecolspace=12pt +\multitablelinespace=0pt + +% Macros used to set up halign preamble: +% +\let\endsetuptable\relax +\def\xendsetuptable{\endsetuptable} +\let\columnfractions\relax +\def\xcolumnfractions{\columnfractions} +\newif\ifsetpercent + +% #1 is the part of the @columnfraction before the decimal point, which +% is presumably either 0 or the empty string (but we don't check, we +% just throw it away). #2 is the decimal part, which we use as the +% percent of \hsize for this column. +\def\pickupwholefraction#1.#2 {% + \global\advance\colcount by 1 + \expandafter\xdef\csname col\the\colcount\endcsname{.#2\hsize}% + \setuptable +} + +\newcount\colcount +\def\setuptable#1{% + \def\firstarg{#1}% + \ifx\firstarg\xendsetuptable + \let\go = \relax + \else + \ifx\firstarg\xcolumnfractions + \global\setpercenttrue + \else + \ifsetpercent + \let\go\pickupwholefraction + \else + \global\advance\colcount by 1 + \setbox0=\hbox{#1\unskip }% Add a normal word space as a separator; + % typically that is always in the input, anyway. + \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% + \fi + \fi + \ifx\go\pickupwholefraction + % Put the argument back for the \pickupwholefraction call, so + % we'll always have a period there to be parsed. + \def\go{\pickupwholefraction#1}% + \else + \let\go = \setuptable + \fi% + \fi + \go +} + +% This used to have \hskip1sp. But then the space in a template line is +% not enough. That is bad. So let's go back to just & until we +% encounter the problem it was intended to solve again. +% --karl, nathan@acm.org, 20apr99. +\def\tab{&} + +% @multitable ... @end multitable definitions: +% +\def\multitable{\parsearg\dotable} +\def\dotable#1{\bgroup + \vskip\parskip + \let\item\crcr + \tolerance=9500 + \hbadness=9500 + \setmultitablespacing + \parskip=\multitableparskip + \parindent=\multitableparindent + \overfullrule=0pt + \global\colcount=0 + \def\Emultitable{\global\setpercentfalse\cr\egroup\egroup}% + % + % To parse everything between @multitable and @item: + \setuptable#1 \endsetuptable + % + % \everycr will reset column counter, \colcount, at the end of + % each line. Every column entry will cause \colcount to advance by one. + % The table preamble + % looks at the current \colcount to find the correct column width. + \everycr{\noalign{% + % + % \filbreak%% keeps underfull box messages off when table breaks over pages. + % Maybe so, but it also creates really weird page breaks when the table + % breaks over pages. Wouldn't \vfil be better? Wait until the problem + % manifests itself, so it can be fixed for real --karl. + \global\colcount=0\relax}}% + % + % This preamble sets up a generic column definition, which will + % be used as many times as user calls for columns. + % \vtop will set a single line and will also let text wrap and + % continue for many paragraphs if desired. + \halign\bgroup&\global\advance\colcount by 1\relax + \multistrut\vtop{\hsize=\expandafter\csname col\the\colcount\endcsname + % + % In order to keep entries from bumping into each other + % we will add a \leftskip of \multitablecolspace to all columns after + % the first one. + % + % If a template has been used, we will add \multitablecolspace + % to the width of each template entry. + % + % If the user has set preamble in terms of percent of \hsize we will + % use that dimension as the width of the column, and the \leftskip + % will keep entries from bumping into each other. Table will start at + % left margin and final column will justify at right margin. + % + % Make sure we don't inherit \rightskip from the outer environment. + \rightskip=0pt + \ifnum\colcount=1 + % The first column will be indented with the surrounding text. + \advance\hsize by\leftskip + \else + \ifsetpercent \else + % If user has not set preamble in terms of percent of \hsize + % we will advance \hsize by \multitablecolspace. + \advance\hsize by \multitablecolspace + \fi + % In either case we will make \leftskip=\multitablecolspace: + \leftskip=\multitablecolspace + \fi + % Ignoring space at the beginning and end avoids an occasional spurious + % blank line, when TeX decides to break the line at the space before the + % box from the multistrut, so the strut ends up on a line by itself. + % For example: + % @multitable @columnfractions .11 .89 + % @item @code{#} + % @tab Legal holiday which is valid in major parts of the whole country. + % Is automatically provided with highlighting sequences respectively marking + % characters. + \noindent\ignorespaces##\unskip\multistrut}\cr +} + +\def\setmultitablespacing{% test to see if user has set \multitablelinespace. +% If so, do nothing. If not, give it an appropriate dimension based on +% current baselineskip. +\ifdim\multitablelinespace=0pt +\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip +\global\advance\multitablelinespace by-\ht0 +%% strut to put in table in case some entry doesn't have descenders, +%% to keep lines equally spaced +\let\multistrut = \strut +\else +%% FIXME: what is \box0 supposed to be? +\gdef\multistrut{\vrule height\multitablelinespace depth\dp0 +width0pt\relax} \fi +%% Test to see if parskip is larger than space between lines of +%% table. If not, do nothing. +%% If so, set to same dimension as multitablelinespace. +\ifdim\multitableparskip>\multitablelinespace +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi% +\ifdim\multitableparskip=0pt +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi} + + +\message{conditionals,} +% Prevent errors for section commands. +% Used in @ignore and in failing conditionals. +\def\ignoresections{% + \let\chapter=\relax + \let\unnumbered=\relax + \let\top=\relax + \let\unnumberedsec=\relax + \let\unnumberedsection=\relax + \let\unnumberedsubsec=\relax + \let\unnumberedsubsection=\relax + \let\unnumberedsubsubsec=\relax + \let\unnumberedsubsubsection=\relax + \let\section=\relax + \let\subsec=\relax + \let\subsubsec=\relax + \let\subsection=\relax + \let\subsubsection=\relax + \let\appendix=\relax + \let\appendixsec=\relax + \let\appendixsection=\relax + \let\appendixsubsec=\relax + \let\appendixsubsection=\relax + \let\appendixsubsubsec=\relax + \let\appendixsubsubsection=\relax + \let\contents=\relax + \let\smallbook=\relax + \let\titlepage=\relax +} + +% Used in nested conditionals, where we have to parse the Texinfo source +% and so want to turn off most commands, in case they are used +% incorrectly. +% +\def\ignoremorecommands{% + \let\defcodeindex = \relax + \let\defcv = \relax + \let\deffn = \relax + \let\deffnx = \relax + \let\defindex = \relax + \let\defivar = \relax + \let\defmac = \relax + \let\defmethod = \relax + \let\defop = \relax + \let\defopt = \relax + \let\defspec = \relax + \let\deftp = \relax + \let\deftypefn = \relax + \let\deftypefun = \relax + \let\deftypeivar = \relax + \let\deftypeop = \relax + \let\deftypevar = \relax + \let\deftypevr = \relax + \let\defun = \relax + \let\defvar = \relax + \let\defvr = \relax + \let\ref = \relax + \let\xref = \relax + \let\printindex = \relax + \let\pxref = \relax + \let\settitle = \relax + \let\setchapternewpage = \relax + \let\setchapterstyle = \relax + \let\everyheading = \relax + \let\evenheading = \relax + \let\oddheading = \relax + \let\everyfooting = \relax + \let\evenfooting = \relax + \let\oddfooting = \relax + \let\headings = \relax + \let\include = \relax + \let\lowersections = \relax + \let\down = \relax + \let\raisesections = \relax + \let\up = \relax + \let\set = \relax + \let\clear = \relax + \let\item = \relax +} + +% Ignore @ignore ... @end ignore. +% +\def\ignore{\doignore{ignore}} + +% Ignore @ifinfo, @ifhtml, @ifnottex, @html, @menu, and @direntry text. +% +\def\ifinfo{\doignore{ifinfo}} +\def\ifhtml{\doignore{ifhtml}} +\def\ifnottex{\doignore{ifnottex}} +\def\html{\doignore{html}} +\def\menu{\doignore{menu}} +\def\direntry{\doignore{direntry}} + +% @dircategory CATEGORY -- specify a category of the dir file +% which this file should belong to. Ignore this in TeX. +\let\dircategory = \comment + +% Ignore text until a line `@end #1'. +% +\def\doignore#1{\begingroup + % Don't complain about control sequences we have declared \outer. + \ignoresections + % + % Define a command to swallow text until we reach `@end #1'. + % This @ is a catcode 12 token (that is the normal catcode of @ in + % this texinfo.tex file). We change the catcode of @ below to match. + \long\def\doignoretext##1@end #1{\enddoignore}% + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \catcode32 = 10 + % + % Ignore braces, too, so mismatched braces don't cause trouble. + \catcode`\{ = 9 + \catcode`\} = 9 + % + % We must not have @c interpreted as a control sequence. + \catcode`\@ = 12 + % + % Make the letter c a comment character so that the rest of the line + % will be ignored. This way, the document can have (for example) + % @c @end ifinfo + % and the @end ifinfo will be properly ignored. + % (We've just changed @ to catcode 12.) + \catcode`\c = 14 + % + % And now expand that command. + \doignoretext +} + +% What we do to finish off ignored text. +% +\def\enddoignore{\endgroup\ignorespaces}% + +\newif\ifwarnedobs\warnedobsfalse +\def\obstexwarn{% + \ifwarnedobs\relax\else + % We need to warn folks that they may have trouble with TeX 3.0. + % This uses \immediate\write16 rather than \message to get newlines. + \immediate\write16{} + \immediate\write16{WARNING: for users of Unix TeX 3.0!} + \immediate\write16{This manual trips a bug in TeX version 3.0 (tex hangs).} + \immediate\write16{If you are running another version of TeX, relax.} + \immediate\write16{If you are running Unix TeX 3.0, kill this TeX process.} + \immediate\write16{ Then upgrade your TeX installation if you can.} + \immediate\write16{ (See ftp://ftp.gnu.org/pub/gnu/TeX.README.)} + \immediate\write16{If you are stuck with version 3.0, run the} + \immediate\write16{ script ``tex3patch'' from the Texinfo distribution} + \immediate\write16{ to use a workaround.} + \immediate\write16{} + \global\warnedobstrue + \fi +} + +% **In TeX 3.0, setting text in \nullfont hangs tex. For a +% workaround (which requires the file ``dummy.tfm'' to be installed), +% uncomment the following line: +%%%%%\font\nullfont=dummy\let\obstexwarn=\relax + +% Ignore text, except that we keep track of conditional commands for +% purposes of nesting, up to an `@end #1' command. +% +\def\nestedignore#1{% + \obstexwarn + % We must actually expand the ignored text to look for the @end + % command, so that nested ignore constructs work. Thus, we put the + % text into a \vbox and then do nothing with the result. To minimize + % the change of memory overflow, we follow the approach outlined on + % page 401 of the TeXbook: make the current font be a dummy font. + % + \setbox0 = \vbox\bgroup + % Don't complain about control sequences we have declared \outer. + \ignoresections + % + % Define `@end #1' to end the box, which will in turn undefine the + % @end command again. + \expandafter\def\csname E#1\endcsname{\egroup\ignorespaces}% + % + % We are going to be parsing Texinfo commands. Most cause no + % trouble when they are used incorrectly, but some commands do + % complicated argument parsing or otherwise get confused, so we + % undefine them. + % + % We can't do anything about stray @-signs, unfortunately; + % they'll produce `undefined control sequence' errors. + \ignoremorecommands + % + % Set the current font to be \nullfont, a TeX primitive, and define + % all the font commands to also use \nullfont. We don't use + % dummy.tfm, as suggested in the TeXbook, because not all sites + % might have that installed. Therefore, math mode will still + % produce output, but that should be an extremely small amount of + % stuff compared to the main input. + % + \nullfont + \let\tenrm=\nullfont \let\tenit=\nullfont \let\tensl=\nullfont + \let\tenbf=\nullfont \let\tentt=\nullfont \let\smallcaps=\nullfont + \let\tensf=\nullfont + % Similarly for index fonts (mostly for their use in smallexample). + \let\smallrm=\nullfont \let\smallit=\nullfont \let\smallsl=\nullfont + \let\smallbf=\nullfont \let\smalltt=\nullfont \let\smallsc=\nullfont + \let\smallsf=\nullfont + % + % Don't complain when characters are missing from the fonts. + \tracinglostchars = 0 + % + % Don't bother to do space factor calculations. + \frenchspacing + % + % Don't report underfull hboxes. + \hbadness = 10000 + % + % Do minimal line-breaking. + \pretolerance = 10000 + % + % Do not execute instructions in @tex + \def\tex{\doignore{tex}}% + % Do not execute macro definitions. + % `c' is a comment character, so the word `macro' will get cut off. + \def\macro{\doignore{ma}}% +} + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. Make sure the catcode of space is correct to avoid +% losing inside @example, for instance. +% +\def\set{\begingroup\catcode` =10 + \catcode`\-=12 \catcode`\_=12 % Allow - and _ in VAR. + \parsearg\setxxx} +\def\setxxx#1{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + \def\temp{#2}% + \ifx\temp\empty \global\expandafter\let\csname SET#1\endcsname = \empty + \else \setzzz{#1}#2\endsetzzz % Remove the trailing space \setxxx inserted. + \fi + \endgroup +} +% Can't use \xdef to pre-expand #2 and save some time, since \temp or +% \next or other control sequences that we've defined might get us into +% an infinite loop. Consider `@set foo @cite{bar}'. +\def\setzzz#1#2 \endsetzzz{\expandafter\gdef\csname SET#1\endcsname{#2}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\def\clear{\parsearg\clearxxx} +\def\clearxxx#1{\global\expandafter\let\csname SET#1\endcsname=\relax} + +% @value{foo} gets the text saved in variable foo. +{ + \catcode`\_ = \active + % + % We might end up with active _ or - characters in the argument if + % we're called from @code, as @code{@value{foo-bar_}}. So \let any + % such active characters to their normal equivalents. + \gdef\value{\begingroup + \catcode`\-=12 \catcode`\_=12 + \indexbreaks \let_\normalunderscore + \valuexxx} +} +\def\valuexxx#1{\expandablevalue{#1}\endgroup} + +% We have this subroutine so that we can handle at least some @value's +% properly in indexes (we \let\value to this in \indexdummies). Ones +% whose names contain - or _ still won't work, but we can't do anything +% about that. The command has to be fully expandable, since the result +% winds up in the index file. This means that if the variable's value +% contains other Texinfo commands, it's almost certain it will fail +% (although perhaps we could fix that with sufficient work to do a +% one-level expansion on the result, instead of complete). +% +\def\expandablevalue#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + {[No value for ``#1'']}% + \else + \csname SET#1\endcsname + \fi +} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +\def\ifset{\parsearg\ifsetxxx} +\def\ifsetxxx #1{% + \expandafter\ifx\csname SET#1\endcsname\relax + \expandafter\ifsetfail + \else + \expandafter\ifsetsucceed + \fi +} +\def\ifsetsucceed{\conditionalsucceed{ifset}} +\def\ifsetfail{\nestedignore{ifset}} +\defineunmatchedend{ifset} + +% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +\def\ifclear{\parsearg\ifclearxxx} +\def\ifclearxxx #1{% + \expandafter\ifx\csname SET#1\endcsname\relax + \expandafter\ifclearsucceed + \else + \expandafter\ifclearfail + \fi +} +\def\ifclearsucceed{\conditionalsucceed{ifclear}} +\def\ifclearfail{\nestedignore{ifclear}} +\defineunmatchedend{ifclear} + +% @iftex, @ifnothtml, @ifnotinfo always succeed; we read the text +% following, through the first @end iftex (etc.). Make `@end iftex' +% (etc.) valid only after an @iftex. +% +\def\iftex{\conditionalsucceed{iftex}} +\def\ifnothtml{\conditionalsucceed{ifnothtml}} +\def\ifnotinfo{\conditionalsucceed{ifnotinfo}} +\defineunmatchedend{iftex} +\defineunmatchedend{ifnothtml} +\defineunmatchedend{ifnotinfo} + +% We can't just want to start a group at @iftex (for example) and end it +% at @end iftex, since then @set commands inside the conditional have no +% effect (they'd get reverted at the end of the group). So we must +% define \Eiftex to redefine itself to be its previous value. (We can't +% just define it to fail again with an ``unmatched end'' error, since +% the @ifset might be nested.) +% +\def\conditionalsucceed#1{% + \edef\temp{% + % Remember the current value of \E#1. + \let\nece{prevE#1} = \nece{E#1}% + % + % At the `@end #1', redefine \E#1 to be its previous value. + \def\nece{E#1}{\let\nece{E#1} = \nece{prevE#1}}% + }% + \temp +} + +% We need to expand lots of \csname's, but we don't want to expand the +% control sequences after we've constructed them. +% +\def\nece#1{\expandafter\noexpand\csname#1\endcsname} + +% @defininfoenclose. +\let\definfoenclose=\comment + + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within \newindex. +{\catcode`\@=11 +\gdef\newwrite{\alloc@7\write\chardef\sixt@@n}} + +% \newindex {foo} defines an index named foo. +% It automatically defines \fooindex such that +% \fooindex ...rest of line... puts an entry in the index foo. +% It also defines \fooindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is foo. +% The name of an index should be no more than 2 characters long +% for the sake of vms. +% +\def\newindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 % Open the file + \fi + \expandafter\xdef\csname#1index\endcsname{% % Define @#1index + \noexpand\doindex{#1}} +} + +% @defindex foo == \newindex{foo} +% +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. +% +\def\defcodeindex{\parsearg\newcodeindex} +% +\def\newcodeindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 + \fi + \expandafter\xdef\csname#1index\endcsname{% + \noexpand\docodeindex{#1}}% +} + + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +% +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +% +\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} +\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} + +% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), +% #3 the target index (bar). +\def\dosynindex#1#2#3{% + % Only do \closeout if we haven't already done it, else we'll end up + % closing the target index. + \expandafter \ifx\csname donesynindex#2\endcsname \undefined + % The \closeout helps reduce unnecessary open files; the limit on the + % Acorn RISC OS is a mere 16 files. + \expandafter\closeout\csname#2indfile\endcsname + \expandafter\let\csname\donesynindex#2\endcsname = 1 + \fi + % redefine \fooindfile: + \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname + \expandafter\let\csname#2indfile\endcsname=\temp + % redefine \fooindex: + \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% +} + +% Define \doindex, the driver for all \fooindex macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it is "foo", the name of the index. + +% \doindex just uses \parsearg; it calls \doind for the actual work. +% This is because \doind is more useful to call from other macros. + +% There is also \dosubind {index}{topic}{subtopic} +% which makes an entry in a two-level index such as the operation index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} +\def\singleindexer #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} +\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} + +\def\indexdummies{% +\def\ { }% +% Take care of the plain tex accent commands. +\def\"{\realbackslash "}% +\def\`{\realbackslash `}% +\def\'{\realbackslash '}% +\def\^{\realbackslash ^}% +\def\~{\realbackslash ~}% +\def\={\realbackslash =}% +\def\b{\realbackslash b}% +\def\c{\realbackslash c}% +\def\d{\realbackslash d}% +\def\u{\realbackslash u}% +\def\v{\realbackslash v}% +\def\H{\realbackslash H}% +% Take care of the plain tex special European modified letters. +\def\oe{\realbackslash oe}% +\def\ae{\realbackslash ae}% +\def\aa{\realbackslash aa}% +\def\OE{\realbackslash OE}% +\def\AE{\realbackslash AE}% +\def\AA{\realbackslash AA}% +\def\o{\realbackslash o}% +\def\O{\realbackslash O}% +\def\l{\realbackslash l}% +\def\L{\realbackslash L}% +\def\ss{\realbackslash ss}% +% Take care of texinfo commands likely to appear in an index entry. +% (Must be a way to avoid doing expansion at all, and thus not have to +% laboriously list every single command here.) +\def\@{@}% will be @@ when we switch to @ as escape char. +% Need these in case \tex is in effect and \{ is a \delimiter again. +% But can't use \lbracecmd and \rbracecmd because texindex assumes +% braces and backslashes are used only as delimiters. +\let\{ = \mylbrace +\let\} = \myrbrace +\def\_{{\realbackslash _}}% +\def\w{\realbackslash w }% +\def\bf{\realbackslash bf }% +%\def\rm{\realbackslash rm }% +\def\sl{\realbackslash sl }% +\def\sf{\realbackslash sf}% +\def\tt{\realbackslash tt}% +\def\gtr{\realbackslash gtr}% +\def\less{\realbackslash less}% +\def\hat{\realbackslash hat}% +\def\TeX{\realbackslash TeX}% +\def\dots{\realbackslash dots }% +\def\result{\realbackslash result}% +\def\equiv{\realbackslash equiv}% +\def\expansion{\realbackslash expansion}% +\def\print{\realbackslash print}% +\def\error{\realbackslash error}% +\def\point{\realbackslash point}% +\def\copyright{\realbackslash copyright}% +\def\tclose##1{\realbackslash tclose {##1}}% +\def\code##1{\realbackslash code {##1}}% +\def\uref##1{\realbackslash uref {##1}}% +\def\url##1{\realbackslash url {##1}}% +\def\env##1{\realbackslash env {##1}}% +\def\command##1{\realbackslash command {##1}}% +\def\option##1{\realbackslash option {##1}}% +\def\dotless##1{\realbackslash dotless {##1}}% +\def\samp##1{\realbackslash samp {##1}}% +\def\,##1{\realbackslash ,{##1}}% +\def\t##1{\realbackslash t {##1}}% +\def\r##1{\realbackslash r {##1}}% +\def\i##1{\realbackslash i {##1}}% +\def\b##1{\realbackslash b {##1}}% +\def\sc##1{\realbackslash sc {##1}}% +\def\cite##1{\realbackslash cite {##1}}% +\def\key##1{\realbackslash key {##1}}% +\def\file##1{\realbackslash file {##1}}% +\def\var##1{\realbackslash var {##1}}% +\def\kbd##1{\realbackslash kbd {##1}}% +\def\dfn##1{\realbackslash dfn {##1}}% +\def\emph##1{\realbackslash emph {##1}}% +\def\acronym##1{\realbackslash acronym {##1}}% +% +% Handle some cases of @value -- where the variable name does not +% contain - or _, and the value does not contain any +% (non-fully-expandable) commands. +\let\value = \expandablevalue +% +\unsepspaces +% Turn off macro expansion +\turnoffmacros +} + +% If an index command is used in an @example environment, any spaces +% therein should become regular spaces in the raw index file, not the +% expansion of \tie (\\leavevmode \penalty \@M \ ). +{\obeyspaces + \gdef\unsepspaces{\obeyspaces\let =\space}} + +% \indexnofonts no-ops all font-change commands. +% This is used when outputting the strings to sort the index by. +\def\indexdummyfont#1{#1} +\def\indexdummytex{TeX} +\def\indexdummydots{...} + +\def\indexnofonts{% +% Just ignore accents. +\let\,=\indexdummyfont +\let\"=\indexdummyfont +\let\`=\indexdummyfont +\let\'=\indexdummyfont +\let\^=\indexdummyfont +\let\~=\indexdummyfont +\let\==\indexdummyfont +\let\b=\indexdummyfont +\let\c=\indexdummyfont +\let\d=\indexdummyfont +\let\u=\indexdummyfont +\let\v=\indexdummyfont +\let\H=\indexdummyfont +\let\dotless=\indexdummyfont +% Take care of the plain tex special European modified letters. +\def\oe{oe}% +\def\ae{ae}% +\def\aa{aa}% +\def\OE{OE}% +\def\AE{AE}% +\def\AA{AA}% +\def\o{o}% +\def\O{O}% +\def\l{l}% +\def\L{L}% +\def\ss{ss}% +\let\w=\indexdummyfont +\let\t=\indexdummyfont +\let\r=\indexdummyfont +\let\i=\indexdummyfont +\let\b=\indexdummyfont +\let\emph=\indexdummyfont +\let\strong=\indexdummyfont +\let\cite=\indexdummyfont +\let\sc=\indexdummyfont +%Don't no-op \tt, since it isn't a user-level command +% and is used in the definitions of the active chars like <, >, |... +%\let\tt=\indexdummyfont +\let\tclose=\indexdummyfont +\let\code=\indexdummyfont +\let\url=\indexdummyfont +\let\uref=\indexdummyfont +\let\env=\indexdummyfont +\let\acronym=\indexdummyfont +\let\command=\indexdummyfont +\let\option=\indexdummyfont +\let\file=\indexdummyfont +\let\samp=\indexdummyfont +\let\kbd=\indexdummyfont +\let\key=\indexdummyfont +\let\var=\indexdummyfont +\let\TeX=\indexdummytex +\let\dots=\indexdummydots +\def\@{@}% +} + +% To define \realbackslash, we must make \ not be an escape. +% We must first make another character (@) an escape +% so we do not become unable to do a definition. + +{\catcode`\@=0 \catcode`\\=\other + @gdef@realbackslash{\}} + +\let\indexbackslash=0 %overridden during \printindex. +\let\SETmarginindex=\relax % put index entries in margin (undocumented)? + +% For \ifx comparisons. +\def\emptymacro{\empty} + +% Most index entries go through here, but \dosubind is the general case. +% +\def\doind#1#2{\dosubind{#1}{#2}\empty} + +% Workhorse for all \fooindexes. +% #1 is name of index, #2 is stuff to put there, #3 is subentry -- +% \empty if called from \doind, as we usually are. The main exception +% is with defuns, which call us directly. +% +\def\dosubind#1#2#3{% + % Put the index entry in the margin if desired. + \ifx\SETmarginindex\relax\else + \insert\margin{\hbox{\vrule height8pt depth3pt width0pt #2}}% + \fi + {% + \count255=\lastpenalty + {% + \indexdummies % Must do this here, since \bf, etc expand at this stage + \escapechar=`\\ + {% + \let\folio = 0% We will expand all macros now EXCEPT \folio. + \def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now + % so it will be output as is; and it will print as backslash. + % + \def\thirdarg{#3}% + % + % If third arg is present, precede it with space in sort key. + \ifx\thirdarg\emptymacro + \let\subentry = \empty + \else + \def\subentry{ #3}% + \fi + % + % First process the index entry with all font commands turned + % off to get the string to sort by. + {\indexnofonts \xdef\indexsorttmp{#2\subentry}}% + % + % Now the real index entry with the fonts. + \toks0 = {#2}% + % + % If the third (subentry) arg is present, add it to the index + % line to write. + \ifx\thirdarg\emptymacro \else + \toks0 = \expandafter{\the\toks0{#3}}% + \fi + % + % Set up the complete index entry, with both the sort key and + % the original text, including any font commands. We write + % three arguments to \entry to the .?? file (four in the + % subentry case), texindex reduces to two when writing the .??s + % sorted result. + \edef\temp{% + \write\csname#1indfile\endcsname{% + \realbackslash entry{\indexsorttmp}{\folio}{\the\toks0}}% + }% + % + % If a skip is the last thing on the list now, preserve it + % by backing up by \lastskip, doing the \write, then inserting + % the skip again. Otherwise, the whatsit generated by the + % \write will make \lastskip zero. The result is that sequences + % like this: + % @end defun + % @tindex whatever + % @defun ... + % will have extra space inserted, because the \medbreak in the + % start of the @defun won't see the skip inserted by the @end of + % the previous defun. + % + % But don't do any of this if we're not in vertical mode. We + % don't want to do a \vskip and prematurely end a paragraph. + % + % Avoid page breaks due to these extra skips, too. + % + \iflinks + \ifvmode + \skip0 = \lastskip + \ifdim\lastskip = 0pt \else \nobreak\vskip-\lastskip \fi + \fi + % + \temp % do the write + % + % + \ifvmode \ifdim\skip0 = 0pt \else \nobreak\vskip\skip0 \fi \fi + \fi + }% + }% + \penalty\count255 + }% +} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +\def\cindexsub {\begingroup\obeylines\cindexsub} +{\obeylines % +\gdef\cindexsub "#1" #2^^M{\endgroup % +\dosubind{cp}{#2}{#1}}} + +% Define the macros used in formatting output of the sorted index material. + +% @printindex causes a particular index (the ??s file) to get printed. +% It does not print any chapter heading (usually an @unnumbered). +% +\def\printindex{\parsearg\doprintindex} +\def\doprintindex#1{\begingroup + \dobreak \chapheadingskip{10000}% + % + \smallfonts \rm + \tolerance = 9500 + \indexbreaks + % + % See if the index file exists and is nonempty. + % Change catcode of @ here so that if the index file contains + % \initial {@} + % as its first line, TeX doesn't complain about mismatched braces + % (because it thinks @} is a control sequence). + \catcode`\@ = 11 + \openin 1 \jobname.#1s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + \putwordIndexNonexistent + \else + % + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \temp + \ifeof 1 + \putwordIndexIsEmpty + \else + % Index files are almost Texinfo source, but we use \ as the escape + % character. It would be better to use @, but that's too big a change + % to make right now. + \def\indexbackslash{\rawbackslashxx}% + \catcode`\\ = 0 + \escapechar = `\\ + \begindoublecolumns + \input \jobname.#1s + \enddoublecolumns + \fi + \fi + \closein 1 +\endgroup} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +\def\initial#1{{% + % Some minor font changes for the special characters. + \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt + % + % Remove any glue we may have, we'll be inserting our own. + \removelastskip + % + % We like breaks before the index initials, so insert a bonus. + \penalty -300 + % + % Typeset the initial. Making this add up to a whole number of + % baselineskips increases the chance of the dots lining up from column + % to column. It still won't often be perfect, because of the stretch + % we need before each entry, but it's better. + % + % No shrink because it confuses \balancecolumns. + \vskip 1.67\baselineskip plus .5\baselineskip + \leftline{\secbf #1}% + \vskip .33\baselineskip plus .1\baselineskip + % + % Do our best not to break after the initial. + \nobreak +}} + +% This typesets a paragraph consisting of #1, dot leaders, and then #2 +% flush to the right margin. It is used for index and table of contents +% entries. The paragraph is indented by \leftskip. +% +\def\entry#1#2{\begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % Do not fill out the last line with white space. + \parfillskip = 0in + % + % No extra space above this paragraph. + \parskip = 0in + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % \hangindent is only relevant when the entry text and page number + % don't both fit on one line. In that case, bob suggests starting the + % dots pretty far over on the line. Unfortunately, a large + % indentation looks wrong when the entry text itself is broken across + % lines. So we use a small indentation and put up with long leaders. + % + % \hangafter is reset to 1 (which is the value we want) at the start + % of each paragraph, so we need not do anything with that. + \hangindent = 2em + % + % When the entry text needs to be broken, just fill out the first line + % with blank space. + \rightskip = 0pt plus1fil + % + % A bit of stretch before each entry for the benefit of balancing columns. + \vskip 0pt plus1pt + % + % Start a ``paragraph'' for the index entry so the line breaking + % parameters we've set above will have an effect. + \noindent + % + % Insert the text of the index entry. TeX will do line-breaking on it. + #1% + % The following is kludged to not output a line of dots in the index if + % there are no page numbers. The next person who breaks this will be + % cursed by a Unix daemon. + \def\tempa{{\rm }}% + \def\tempb{#2}% + \edef\tempc{\tempa}% + \edef\tempd{\tempb}% + \ifx\tempc\tempd\ \else% + % + % If we must, put the page number on a line of its own, and fill out + % this line with blank space. (The \hfil is overwhelmed with the + % fill leaders glue in \indexdotfill if the page number does fit.) + \hfil\penalty50 + \null\nobreak\indexdotfill % Have leaders before the page number. + % + % The `\ ' here is removed by the implicit \unskip that TeX does as + % part of (the primitive) \par. Without it, a spurious underfull + % \hbox ensues. + \ifpdf + \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. + \else + \ #2% The page number ends the paragraph. + \fi + \fi% + \par +\endgroup} + +% Like \dotfill except takes at least 1 em. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu ${\it .}$ \mkern1.5mu$}\hskip 1em plus 1fill} + +\def\primary #1{\line{#1\hfil}} + +\newskip\secondaryindent \secondaryindent=0.5cm +\def\secondary#1#2{{% + \parfillskip=0in + \parskip=0in + \hangindent=1in + \hangafter=1 + \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill + \ifpdf + \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. + \else + #2 + \fi + \par +}} + +% Define two-column mode, which we use to typeset indexes. +% Adapted from the TeXbook, page 416, which is to say, +% the manmac.tex format used to print the TeXbook itself. +\catcode`\@=11 + +\newbox\partialpage +\newdimen\doublecolumnhsize + +\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns + % Grab any single-column material above us. + \output = {% + % + % Here is a possibility not foreseen in manmac: if we accumulate a + % whole lot of material, we might end up calling this \output + % routine twice in a row (see the doublecol-lose test, which is + % essentially a couple of indexes with @setchapternewpage off). In + % that case we just ship out what is in \partialpage with the normal + % output routine. Generally, \partialpage will be empty when this + % runs and this will be a no-op. See the indexspread.tex test case. + \ifvoid\partialpage \else + \onepageout{\pagecontents\partialpage}% + \fi + % + \global\setbox\partialpage = \vbox{% + % Unvbox the main output page. + \unvbox\PAGE + \kern-\topskip \kern\baselineskip + }% + }% + \eject % run that output routine to set \partialpage + % + % Use the double-column output routine for subsequent pages. + \output = {\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it in one place. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +-<1pt) + % as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Double the \vsize as well. (We don't need a separate register here, + % since nobody clobbers \vsize.) + \vsize = 2\vsize +} + +% The double-column output routine for all double-column pages except +% the last. +% +\def\doublecolumnout{% + \splittopskip=\topskip \splitmaxdepth=\maxdepth + % Get the available space for the double columns -- the normal + % (undoubled) page height minus any material left over from the + % previous page. + \dimen@ = \vsize + \divide\dimen@ by 2 + \advance\dimen@ by -\ht\partialpage + % + % box0 will be the left-hand column, box2 the right. + \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ + \onepageout\pagesofar + \unvbox255 + \penalty\outputpenalty +} +% +% Re-output the contents of the output page -- any previous material, +% followed by the two boxes we just split, in box0 and box2. +\def\pagesofar{% + \unvbox\partialpage + % + \hsize = \doublecolumnhsize + \wd0=\hsize \wd2=\hsize + \hbox to\pagewidth{\box0\hfil\box2}% +} +% +% All done with double columns. +\def\enddoublecolumns{% + \output = {% + % Split the last of the double-column material. Leave it on the + % current page, no automatic page break. + \balancecolumns + % + % If we end up splitting too much material for the current page, + % though, there will be another page break right after this \output + % invocation ends. Having called \balancecolumns once, we do not + % want to call it again. Therefore, reset \output to its normal + % definition right away. (We hope \balancecolumns will never be + % called on to balance too much material, but if it is, this makes + % the output somewhat more palatable.) + \global\output = {\onepageout{\pagecontents\PAGE}}% + }% + \eject + \endgroup % started in \begindoublecolumns + % + % \pagegoal was set to the doubled \vsize above, since we restarted + % the current page. We're now back to normal single-column + % typesetting, so reset \pagegoal to the normal \vsize (after the + % \endgroup where \vsize got restored). + \pagegoal = \vsize +} +% +% Called at the end of the double column material. +\def\balancecolumns{% + \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120. + \dimen@ = \ht0 + \advance\dimen@ by \topskip + \advance\dimen@ by-\baselineskip + \divide\dimen@ by 2 % target to split to + %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}% + \splittopskip = \topskip + % Loop until we get a decent breakpoint. + {% + \vbadness = 10000 + \loop + \global\setbox3 = \copy0 + \global\setbox1 = \vsplit3 to \dimen@ + \ifdim\ht3>\dimen@ + \global\advance\dimen@ by 1pt + \repeat + }% + %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}% + \setbox0=\vbox to\dimen@{\unvbox1}% + \setbox2=\vbox to\dimen@{\unvbox3}% + % + \pagesofar +} +\catcode`\@ = \other + + +\message{sectioning,} +% Chapters, sections, etc. + +\newcount\chapno +\newcount\secno \secno=0 +\newcount\subsecno \subsecno=0 +\newcount\subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount\appendixno \appendixno = `\@ +% \def\appendixletter{\char\the\appendixno} +% We do the following for the sake of pdftex, which needs the actual +% letter in the expansion, not just typeset. +\def\appendixletter{% + \ifnum\appendixno=`A A% + \else\ifnum\appendixno=`B B% + \else\ifnum\appendixno=`C C% + \else\ifnum\appendixno=`D D% + \else\ifnum\appendixno=`E E% + \else\ifnum\appendixno=`F F% + \else\ifnum\appendixno=`G G% + \else\ifnum\appendixno=`H H% + \else\ifnum\appendixno=`I I% + \else\ifnum\appendixno=`J J% + \else\ifnum\appendixno=`K K% + \else\ifnum\appendixno=`L L% + \else\ifnum\appendixno=`M M% + \else\ifnum\appendixno=`N N% + \else\ifnum\appendixno=`O O% + \else\ifnum\appendixno=`P P% + \else\ifnum\appendixno=`Q Q% + \else\ifnum\appendixno=`R R% + \else\ifnum\appendixno=`S S% + \else\ifnum\appendixno=`T T% + \else\ifnum\appendixno=`U U% + \else\ifnum\appendixno=`V V% + \else\ifnum\appendixno=`W W% + \else\ifnum\appendixno=`X X% + \else\ifnum\appendixno=`Y Y% + \else\ifnum\appendixno=`Z Z% + % The \the is necessary, despite appearances, because \appendixletter is + % expanded while writing the .toc file. \char\appendixno is not + % expandable, thus it is written literally, thus all appendixes come out + % with the same letter (or @) in the toc without it. + \else\char\the\appendixno + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} + +% Each @chapter defines this as the name of the chapter. +% page headings and footings can use it. @section does likewise. +\def\thischapter{} +\def\thissection{} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raise/lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} +\let\up=\raisesections % original BFox name + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} +\let\down=\lowersections % original BFox name + +% Choose a numbered-heading macro +% #1 is heading level if unmodified by @raisesections or @lowersections +% #2 is text for heading +\def\numhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \chapterzzz{#2} +\or + \seczzz{#2} +\or + \numberedsubseczzz{#2} +\or + \numberedsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \chapterzzz{#2} + \else + \numberedsubsubseczzz{#2} + \fi +\fi +} + +% like \numhead, but chooses appendix heading levels +\def\apphead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \appendixzzz{#2} +\or + \appendixsectionzzz{#2} +\or + \appendixsubseczzz{#2} +\or + \appendixsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \appendixzzz{#2} + \else + \appendixsubsubseczzz{#2} + \fi +\fi +} + +% like \numhead, but chooses numberless heading levels +\def\unnmhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \unnumberedzzz{#2} +\or + \unnumberedseczzz{#2} +\or + \unnumberedsubseczzz{#2} +\or + \unnumberedsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \unnumberedzzz{#2} + \else + \unnumberedsubsubseczzz{#2} + \fi +\fi +} + +% @chapter, @appendix, @unnumbered. +\def\thischaptername{No Chapter Title} +\outer\def\chapter{\parsearg\chapteryyy} +\def\chapteryyy #1{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz #1{% +\secno=0 \subsecno=0 \subsubsecno=0 +\global\advance \chapno by 1 \message{\putwordChapter\space \the\chapno}% +\chapmacro {#1}{\the\chapno}% +\gdef\thissection{#1}% +\gdef\thischaptername{#1}% +% We don't substitute the actual chapter name into \thischapter +% because we don't want its macros evaluated now. +\xdef\thischapter{\putwordChapter{} \the\chapno: \noexpand\thischaptername}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash chapentry{\the\toks0}% + {\the\chapno}}}% +\temp +\donoderef +\global\let\section = \numberedsec +\global\let\subsection = \numberedsubsec +\global\let\subsubsection = \numberedsubsubsec +} + +\outer\def\appendix{\parsearg\appendixyyy} +\def\appendixyyy #1{\apphead0{#1}} % normally apphead0 calls appendixzzz +\def\appendixzzz #1{% +\secno=0 \subsecno=0 \subsubsecno=0 +\global\advance \appendixno by 1 +\message{\putwordAppendix\space \appendixletter}% +\chapmacro {#1}{\putwordAppendix{} \appendixletter}% +\gdef\thissection{#1}% +\gdef\thischaptername{#1}% +\xdef\thischapter{\putwordAppendix{} \appendixletter: \noexpand\thischaptername}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash chapentry{\the\toks0}% + {\putwordAppendix{} \appendixletter}}}% +\temp +\appendixnoderef +\global\let\section = \appendixsec +\global\let\subsection = \appendixsubsec +\global\let\subsubsection = \appendixsubsubsec +} + +% @centerchap is like @unnumbered, but the heading is centered. +\outer\def\centerchap{\parsearg\centerchapyyy} +\def\centerchapyyy #1{{\let\unnumbchapmacro=\centerchapmacro \unnumberedyyy{#1}}} + +% @top is like @unnumbered. +\outer\def\top{\parsearg\unnumberedyyy} + +\outer\def\unnumbered{\parsearg\unnumberedyyy} +\def\unnumberedyyy #1{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz +\def\unnumberedzzz #1{% +\secno=0 \subsecno=0 \subsubsecno=0 +% +% This used to be simply \message{#1}, but TeX fully expands the +% argument to \message. Therefore, if #1 contained @-commands, TeX +% expanded them. For example, in `@unnumbered The @cite{Book}', TeX +% expanded @cite (which turns out to cause errors because \cite is meant +% to be executed, not expanded). +% +% Anyway, we don't want the fully-expanded definition of @cite to appear +% as a result of the \message, we just want `@cite' itself. We use +% \the to achieve this: TeX expands \the only once, +% simply yielding the contents of . (We also do this for +% the toc entries.) +\toks0 = {#1}\message{(\the\toks0)}% +% +\unnumbchapmacro {#1}% +\gdef\thischapter{#1}\gdef\thissection{#1}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash unnumbchapentry{\the\toks0}}}% +\temp +\unnumbnoderef +\global\let\section = \unnumberedsec +\global\let\subsection = \unnumberedsubsec +\global\let\subsubsection = \unnumberedsubsubsec +} + +% Sections. +\outer\def\numberedsec{\parsearg\secyyy} +\def\secyyy #1{\numhead1{#1}} % normally calls seczzz +\def\seczzz #1{% +\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % +\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash secentry{\the\toks0}% + {\the\chapno}{\the\secno}}}% +\temp +\donoderef +\nobreak +} + +\outer\def\appendixsection{\parsearg\appendixsecyyy} +\outer\def\appendixsec{\parsearg\appendixsecyyy} +\def\appendixsecyyy #1{\apphead1{#1}} % normally calls appendixsectionzzz +\def\appendixsectionzzz #1{% +\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % +\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash secentry{\the\toks0}% + {\appendixletter}{\the\secno}}}% +\temp +\appendixnoderef +\nobreak +} + +\outer\def\unnumberedsec{\parsearg\unnumberedsecyyy} +\def\unnumberedsecyyy #1{\unnmhead1{#1}} % normally calls unnumberedseczzz +\def\unnumberedseczzz #1{% +\plainsecheading {#1}\gdef\thissection{#1}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash unnumbsecentry{\the\toks0}}}% +\temp +\unnumbnoderef +\nobreak +} + +% Subsections. +\outer\def\numberedsubsec{\parsearg\numberedsubsecyyy} +\def\numberedsubsecyyy #1{\numhead2{#1}} % normally calls numberedsubseczzz +\def\numberedsubseczzz #1{% +\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % +\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash subsecentry{\the\toks0}% + {\the\chapno}{\the\secno}{\the\subsecno}}}% +\temp +\donoderef +\nobreak +} + +\outer\def\appendixsubsec{\parsearg\appendixsubsecyyy} +\def\appendixsubsecyyy #1{\apphead2{#1}} % normally calls appendixsubseczzz +\def\appendixsubseczzz #1{% +\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % +\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash subsecentry{\the\toks0}% + {\appendixletter}{\the\secno}{\the\subsecno}}}% +\temp +\appendixnoderef +\nobreak +} + +\outer\def\unnumberedsubsec{\parsearg\unnumberedsubsecyyy} +\def\unnumberedsubsecyyy #1{\unnmhead2{#1}} %normally calls unnumberedsubseczzz +\def\unnumberedsubseczzz #1{% +\plainsubsecheading {#1}\gdef\thissection{#1}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash unnumbsubsecentry% + {\the\toks0}}}% +\temp +\unnumbnoderef +\nobreak +} + +% Subsubsections. +\outer\def\numberedsubsubsec{\parsearg\numberedsubsubsecyyy} +\def\numberedsubsubsecyyy #1{\numhead3{#1}} % normally numberedsubsubseczzz +\def\numberedsubsubseczzz #1{% +\gdef\thissection{#1}\global\advance \subsubsecno by 1 % +\subsubsecheading {#1} + {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash subsubsecentry{\the\toks0}% + {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}}}% +\temp +\donoderef +\nobreak +} + +\outer\def\appendixsubsubsec{\parsearg\appendixsubsubsecyyy} +\def\appendixsubsubsecyyy #1{\apphead3{#1}} % normally appendixsubsubseczzz +\def\appendixsubsubseczzz #1{% +\gdef\thissection{#1}\global\advance \subsubsecno by 1 % +\subsubsecheading {#1} + {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash subsubsecentry{\the\toks0}% + {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}}}% +\temp +\appendixnoderef +\nobreak +} + +\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubsecyyy} +\def\unnumberedsubsubsecyyy #1{\unnmhead3{#1}} %normally unnumberedsubsubseczzz +\def\unnumberedsubsubseczzz #1{% +\plainsubsubsecheading {#1}\gdef\thissection{#1}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash unnumbsubsubsecentry% + {\the\toks0}}}% +\temp +\unnumbnoderef +\nobreak +} + +% These are variants which are not "outer", so they can appear in @ifinfo. +% Actually, they should now be obsolete; ordinary section commands should work. +\def\infotop{\parsearg\unnumberedzzz} +\def\infounnumbered{\parsearg\unnumberedzzz} +\def\infounnumberedsec{\parsearg\unnumberedseczzz} +\def\infounnumberedsubsec{\parsearg\unnumberedsubseczzz} +\def\infounnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz} + +\def\infoappendix{\parsearg\appendixzzz} +\def\infoappendixsec{\parsearg\appendixseczzz} +\def\infoappendixsubsec{\parsearg\appendixsubseczzz} +\def\infoappendixsubsubsec{\parsearg\appendixsubsubseczzz} + +\def\infochapter{\parsearg\chapterzzz} +\def\infosection{\parsearg\sectionzzz} +\def\infosubsection{\parsearg\subsectionzzz} +\def\infosubsubsection{\parsearg\subsubsectionzzz} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\global\let\section = \numberedsec +\global\let\subsection = \numberedsubsec +\global\let\subsubsection = \numberedsubsubsec + +% Define @majorheading, @heading and @subheading + +% NOTE on use of \vbox for chapter headings, section headings, and such: +% 1) We use \vbox rather than the earlier \line to permit +% overlong headings to fold. +% 2) \hyphenpenalty is set to 10000 because hyphenation in a +% heading is obnoxious; this forbids it. +% 3) Likewise, headings look best if no \parindent is used, and +% if justification is not attempted. Hence \raggedright. + + +\def\majorheading{\parsearg\majorheadingzzz} +\def\majorheadingzzz #1{% +{\advance\chapheadingskip by 10pt \chapbreak }% +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 200} + +\def\chapheading{\parsearg\chapheadingzzz} +\def\chapheadingzzz #1{\chapbreak % +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 200} + +% @heading, @subheading, @subsubheading. +\def\heading{\parsearg\plainsecheading} +\def\subheading{\parsearg\plainsubsecheading} +\def\subsubheading{\parsearg\plainsubsubsecheading} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +%%% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +\def\setchapterstyle #1 {\csname CHAPF#1\endcsname} + +%%% Define plain chapter starts, and page on/off switching for it +% Parameter controlling skip before chapter headings (if needed) + +\newskip\chapheadingskip + +\def\chapbreak{\dobreak \chapheadingskip {-4000}} +\def\chappager{\par\vfill\supereject} +\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi} + +\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chapbreak +\global\let\pagealignmacro=\chappager} + +\def\CHAPPAGon{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chappager +\global\let\pagealignmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{ +\global\let\contentsalignmacro = \chapoddpage +\global\let\pchapsepmacro=\chapoddpage +\global\let\pagealignmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +\def\CHAPFplain{ +\global\let\chapmacro=\chfplain +\global\let\unnumbchapmacro=\unnchfplain +\global\let\centerchapmacro=\centerchfplain} + +% Plain chapter opening. +% #1 is the text, #2 the chapter number or empty if unnumbered. +\def\chfplain#1#2{% + \pchapsepmacro + {% + \chapfonts \rm + \def\chapnum{#2}% + \setbox0 = \hbox{#2\ifx\chapnum\empty\else\enspace\fi}% + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright + \hangindent = \wd0 \centerparametersmaybe + \unhbox0 #1\par}% + }% + \nobreak\bigskip % no page break after a chapter title + \nobreak +} + +% Plain opening for unnumbered. +\def\unnchfplain#1{\chfplain{#1}{}} + +% @centerchap -- centered and unnumbered. +\let\centerparametersmaybe = \relax +\def\centerchfplain#1{{% + \def\centerparametersmaybe{% + \advance\rightskip by 3\rightskip + \leftskip = \rightskip + \parfillskip = 0pt + }% + \chfplain{#1}{}% +}} + +\CHAPFplain % The default + +\def\unnchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\nobreak +} + +\def\chfopen #1#2{\chapoddpage {\chapfonts +\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% +\par\penalty 5000 % +} + +\def\centerchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt + \hfill {\rm #1}\hfill}}\bigskip \par\nobreak +} + +\def\CHAPFopen{ +\global\let\chapmacro=\chfopen +\global\let\unnumbchapmacro=\unnchfopen +\global\let\centerchapmacro=\centerchfopen} + + +% Section titles. +\newskip\secheadingskip +\def\secheadingbreak{\dobreak \secheadingskip {-1000}} +\def\secheading#1#2#3{\sectionheading{sec}{#2.#3}{#1}} +\def\plainsecheading#1{\sectionheading{sec}{}{#1}} + +% Subsection titles. +\newskip \subsecheadingskip +\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}} +\def\subsecheading#1#2#3#4{\sectionheading{subsec}{#2.#3.#4}{#1}} +\def\plainsubsecheading#1{\sectionheading{subsec}{}{#1}} + +% Subsubsection titles. +\let\subsubsecheadingskip = \subsecheadingskip +\let\subsubsecheadingbreak = \subsecheadingbreak +\def\subsubsecheading#1#2#3#4#5{\sectionheading{subsubsec}{#2.#3.#4.#5}{#1}} +\def\plainsubsubsecheading#1{\sectionheading{subsubsec}{}{#1}} + + +% Print any size section title. +% +% #1 is the section type (sec/subsec/subsubsec), #2 is the section +% number (maybe empty), #3 the text. +\def\sectionheading#1#2#3{% + {% + \expandafter\advance\csname #1headingskip\endcsname by \parskip + \csname #1headingbreak\endcsname + }% + {% + % Switch to the right set of fonts. + \csname #1fonts\endcsname \rm + % + % Only insert the separating space if we have a section number. + \def\secnum{#2}% + \setbox0 = \hbox{#2\ifx\secnum\empty\else\enspace\fi}% + % + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright + \hangindent = \wd0 % zero if no section number + \unhbox0 #3}% + }% + \ifdim\parskip<10pt \nobreak\kern10pt\nobreak\kern-\parskip\fi \nobreak +} + + +\message{toc,} +% Table of contents. +\newwrite\tocfile + +% Write an entry to the toc file, opening it if necessary. +% Called from @chapter, etc. We supply {\folio} at the end of the +% argument, which will end up as the last argument to the \...entry macro. +% +% We open the .toc file here instead of at @setfilename or any other +% given time so that @contents can be put in the document anywhere. +% +\newif\iftocfileopened +\def\writetocentry#1{% + \iftocfileopened\else + \immediate\openout\tocfile = \jobname.toc + \global\tocfileopenedtrue + \fi + \iflinks \write\tocfile{#1{\folio}}\fi +} + +\newskip\contentsrightmargin \contentsrightmargin=1in +\newcount\savepageno +\newcount\lastnegativepageno \lastnegativepageno = -1 + +% Finish up the main text and prepare to read what we've written +% to \tocfile. +% +\def\startcontents#1{% + % If @setchapternewpage on, and @headings double, the contents should + % start on an odd page, unlike chapters. Thus, we maintain + % \contentsalignmacro in parallel with \pagealignmacro. + % From: Torbjorn Granlund + \contentsalignmacro + \immediate\closeout\tocfile + % + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \unnumbchapmacro{#1}\def\thischapter{}% + \savepageno = \pageno + \begingroup % Set up to handle contents files properly. + \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11 + % We can't do this, because then an actual ^ in a section + % title fails, e.g., @chapter ^ -- exponentiation. --karl, 9jul97. + %\catcode`\^=7 % to see ^^e4 as \"a etc. juha@piuha.ydi.vtt.fi + \raggedbottom % Worry more about breakpoints than the bottom. + \advance\hsize by -\contentsrightmargin % Don't use the full line length. + % + % Roman numerals for page numbers. + \ifnum \pageno>0 \pageno = \lastnegativepageno \fi +} + + +% Normal (long) toc. +\def\contents{% + \startcontents{\putwordTOC}% + \openin 1 \jobname.toc + \ifeof 1 \else + \closein 1 + \input \jobname.toc + \fi + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \pdfmakeoutlines + \endgroup + \lastnegativepageno = \pageno + \pageno = \savepageno +} + +% And just the chapters. +\def\summarycontents{% + \startcontents{\putwordShortTOC}% + % + \let\chapentry = \shortchapentry + \let\unnumbchapentry = \shortunnumberedentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl + \rm + \hyphenpenalty = 10000 + \advance\baselineskip by 1pt % Open it up a little. + \def\secentry ##1##2##3##4{} + \def\unnumbsecentry ##1##2{} + \def\subsecentry ##1##2##3##4##5{} + \def\unnumbsubsecentry ##1##2{} + \def\subsubsecentry ##1##2##3##4##5##6{} + \def\unnumbsubsubsecentry ##1##2{} + \openin 1 \jobname.toc + \ifeof 1 \else + \closein 1 + \input \jobname.toc + \fi + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \endgroup + \lastnegativepageno = \pageno + \pageno = \savepageno +} +\let\shortcontents = \summarycontents + +\ifpdf + \pdfcatalog{/PageMode /UseOutlines}% +\fi + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Chapter-level things, for both the long and short contents. +\def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}} + +% See comments in \dochapentry re vbox and related settings +\def\shortchapentry#1#2#3{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#3\egroup}% +} + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g. `Appendix A' for an appendix, or `3' for a chapter. +% We could simplify the code here by writing out an \appendixentry +% command in the toc file for appendices, instead of using \chapentry +% for both, but it doesn't seem worth it. +% +\newdimen\shortappendixwidth +% +\def\shortchaplabel#1{% + % Compute width of word "Appendix", may change with language. + \setbox0 = \hbox{\shortcontrm \putwordAppendix}% + \shortappendixwidth = \wd0 + % + % We typeset #1 in a box of constant width, regardless of the text of + % #1, so the chapter titles will come out aligned. + \setbox0 = \hbox{#1}% + \dimen0 = \ifdim\wd0 > \shortappendixwidth \shortappendixwidth \else 0pt \fi + % + % This space should be plenty, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in by \shortchapentry above.) + \advance\dimen0 by 1.1em + \hbox to \dimen0{#1\hfil}% +} + +\def\unnumbchapentry#1#2{\dochapentry{#1}{#2}} +\def\shortunnumberedentry#1#2{\tocentry{#1}{\doshortpageno\bgroup#2\egroup}} + +% Sections. +\def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}} +\def\unnumbsecentry#1#2{\dosecentry{#1}{#2}} + +% Subsections. +\def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}} +\def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}} + +% And subsubsections. +\def\subsubsecentry#1#2#3#4#5#6{% + \dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}} +\def\unnumbsubsubsecentry#1#2{\dosubsubsecentry{#1}{#2}} + +% This parameter controls the indentation of the various levels. +\newdimen\tocindent \tocindent = 3pc + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we want it to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip + \begingroup + \chapentryfonts + \tocentry{#1}{\dopageno\bgroup#2\egroup}% + \endgroup + \nobreak\vskip .25\baselineskip plus.1\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +% Final typesetting of a toc entry; we use the same \entry macro as for +% the index entries, but we want to suppress hyphenation here. (We +% can't do that in the \entry macro, since index entries might consist +% of hyphenated-identifiers-that-do-not-fit-on-a-line-and-nothing-else.) +\def\tocentry#1#2{\begingroup + \vskip 0pt plus1pt % allow a little stretch for the sake of nice page breaks + % Do not use \turnoffactive in these arguments. Since the toc is + % typeset in cmr, so characters such as _ would come out wrong; we + % have to do the usual translation tricks. + \entry{#1}{#2}% +\endgroup} + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\let\subsecentryfonts = \textfonts +\let\subsubsecentryfonts = \textfonts + + +\message{environments,} +% @foo ... @end foo. + +% Since these characters are used in examples, it should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% Furthermore, these definitions must come after we define our fonts. +\newbox\dblarrowbox \newbox\longdblarrowbox +\newbox\pushcharbox \newbox\bullbox +\newbox\equivbox \newbox\errorbox + +%{\tentt +%\global\setbox\dblarrowbox = \hbox to 1em{\hfil$\Rightarrow$\hfil} +%\global\setbox\longdblarrowbox = \hbox to 1em{\hfil$\mapsto$\hfil} +%\global\setbox\pushcharbox = \hbox to 1em{\hfil$\dashv$\hfil} +%\global\setbox\equivbox = \hbox to 1em{\hfil$\ptexequiv$\hfil} +% Adapted from the manmac format (p.420 of TeXbook) +%\global\setbox\bullbox = \hbox to 1em{\kern.15em\vrule height .75ex width .85ex +% depth .1ex\hfil} +%} + +% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +\def\point{$\star$} +\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} +\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% Adapted from the TeXbook's \boxit. +{\tentt \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \tensf error\kern-1.5pt} + +\global\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{ + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} + +% The @error{} command. +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @tex ... @end tex escapes into raw Tex temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain tex @ character. + +\def\tex{\begingroup + \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 + \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 + \catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie + \catcode `\%=14 + \catcode 43=12 % plus + \catcode`\"=12 + \catcode`\==12 + \catcode`\|=12 + \catcode`\<=12 + \catcode`\>=12 + \escapechar=`\\ + % + \let\b=\ptexb + \let\bullet=\ptexbullet + \let\c=\ptexc + \let\,=\ptexcomma + \let\.=\ptexdot + \let\dots=\ptexdots + \let\equiv=\ptexequiv + \let\!=\ptexexclam + \let\i=\ptexi + \let\{=\ptexlbrace + \let\+=\tabalign + \let\}=\ptexrbrace + \let\*=\ptexstar + \let\t=\ptext + % + \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% + \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% + \def\@{@}% +\let\Etex=\endgroup} + +% Define @lisp ... @endlisp. +% @lisp does a \begingroup so it can rebind things, +% including the definition of @endlisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^^M gets inside @lisp, @example, and other +% such environments. \null is better than a space, since it doesn't +% have any width. +\def\lisppar{\null\endgraf} + +% Make each space character in the input produce a normal interword +% space in the output. Don't allow a line break at this space, as this +% is used only in environments like @example, where each line of input +% should produce a line of output anyway. +% +{\obeyspaces % +\gdef\sepspaces{\obeyspaces\let =\tie}} + +% Define \obeyedspace to be our active space, whatever it is. This is +% for use in \parsearg. +{\sepspaces% +\global\let\obeyedspace= } + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. We use \parskip here +% to help in doing that, since in @example-like environments \parskip +% is reset to zero; thus the \afterenvbreak inserts no space -- but the +% start of the next paragraph will insert \parskip +% +\def\aboveenvbreak{{% + \ifnum\lastpenalty < 10000 + \advance\envskipamount by \parskip + \endgraf + \ifdim\lastskip<\envskipamount + \removelastskip + \penalty-50 + \vskip\envskipamount + \fi + \fi +}} + +\let\afterenvbreak = \aboveenvbreak + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins. +\let\nonarrowing=\relax + +% @cartouche ... @end cartouche: draw rectangle w/rounded corners around +% environment contents. +\font\circle=lcircle10 +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip +\circthick=\fontdimen8\circle +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +\long\def\cartouche{% +\begingroup + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt %we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18.4pt % allow for 3pt kerns on either +% side, and for 6pt waste from +% each corner char, and rule thickness + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % Flag to tell @lisp, etc., not to narrow margin. + \let\nonarrowing=\comment + \vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \hsize=\cartinner + \kern3pt + \begingroup + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip +\def\Ecartouche{% + \endgroup + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup +\endgroup +}} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\def\nonfillstart{% + \aboveenvbreak + \inENV % This group ends at the end of the body + \hfuzz = 12pt % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \singlespace + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + \parindent = 0pt + \emergencystretch = 0pt % don't try to avoid overfull boxes + % @cartouche defines \nonarrowing to inhibit narrowing + % at next level down. + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \let\exdent=\nofillexdent + \let\nonarrowing=\relax + \fi +} + +% Define the \E... control sequence only if we are inside the particular +% environment, so the error checking in \end will work. +% +% To end an @example-like environment, we first end the paragraph (via +% \afterenvbreak's vertical glue), and then the group. That way we keep +% the zero \parskip that the environments set -- \parskip glue will be +% inserted at the beginning of the next paragraph in the document, after +% the environment. +% +\def\nonfillfinish{\afterenvbreak\endgroup} + +% @lisp: indented, narrowed, typewriter font. +\def\lisp{\begingroup + \nonfillstart + \let\Elisp = \nonfillfinish + \tt + \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. + \gobble % eat return +} + +% @example: Same as @lisp. +\def\example{\begingroup \def\Eexample{\nonfillfinish\endgroup}\lisp} + +% @small... is usually equivalent to the non-small (@smallbook +% redefines). We must call \example (or whatever) last in the +% definition, since it reads the return following the @example (or +% whatever) command. +% +% This actually allows (for example) @end display inside an +% @smalldisplay. Too bad, but makeinfo will catch the error anyway. +% +\def\smalldisplay{\begingroup\def\Esmalldisplay{\nonfillfinish\endgroup}\display} +\def\smallexample{\begingroup\def\Esmallexample{\nonfillfinish\endgroup}\lisp} +\def\smallformat{\begingroup\def\Esmallformat{\nonfillfinish\endgroup}\format} +\def\smalllisp{\begingroup\def\Esmalllisp{\nonfillfinish\endgroup}\lisp} + +% Real @smallexample and @smalllisp (when @smallbook): use smaller fonts. +% Originally contributed by Pavel@xerox. +\def\smalllispx{\begingroup + \def\Esmalllisp{\nonfillfinish\endgroup}% + \def\Esmallexample{\nonfillfinish\endgroup}% + \smallfonts + \lisp +} + +% @display: same as @lisp except keep current font. +% +\def\display{\begingroup + \nonfillstart + \let\Edisplay = \nonfillfinish + \gobble +} + +% @smalldisplay (when @smallbook): @display plus smaller fonts. +% +\def\smalldisplayx{\begingroup + \def\Esmalldisplay{\nonfillfinish\endgroup}% + \smallfonts \rm + \display +} + +% @format: same as @display except don't narrow margins. +% +\def\format{\begingroup + \let\nonarrowing = t + \nonfillstart + \let\Eformat = \nonfillfinish + \gobble +} + +% @smallformat (when @smallbook): @format plus smaller fonts. +% +\def\smallformatx{\begingroup + \def\Esmallformat{\nonfillfinish\endgroup}% + \smallfonts \rm + \format +} + +% @flushleft (same as @format). +% +\def\flushleft{\begingroup \def\Eflushleft{\nonfillfinish\endgroup}\format} + +% @flushright. +% +\def\flushright{\begingroup + \let\nonarrowing = t + \nonfillstart + \let\Eflushright = \nonfillfinish + \advance\leftskip by 0pt plus 1fill + \gobble +} + + +% @quotation does normal linebreaking (hence we can't use \nonfillstart) +% and narrows the margins. +% +\def\quotation{% + \begingroup\inENV %This group ends at the end of the @quotation body + {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip + \singlespace + \parindent=0pt + % We have retained a nonzero parskip for the environment, since we're + % doing normal filling. So to avoid extra space below the environment... + \def\Equotation{\parskip = 0pt \nonfillfinish}% + % + % @cartouche defines \nonarrowing to inhibit narrowing at next level down. + \ifx\nonarrowing\relax + \advance\leftskip by \lispnarrowing + \advance\rightskip by \lispnarrowing + \exdentamount = \lispnarrowing + \let\nonarrowing = \relax + \fi +} + + +% LaTeX-like @verbatim...@end verbatim and @verb{...} +% If we want to allow any as delimiter, +% we need the curly braces so that makeinfo sees the @verb command, eg: +% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org +% +% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. +% +% [Knuth] p. 344; only we need to do '@' too +\def\dospecials{% + \do\ \do\\\do\@\do\{\do\}\do\$\do\&% + \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~} +% +% [Knuth] p. 380 +\def\uncatcodespecials{% + \def\do##1{\catcode`##1=12}\dospecials} +% +% [Knuth] pp. 380,381,391 +% Disable Spanish ligatures ?` and !` of \tt font +\begingroup + \catcode`\`=\active\gdef`{\relax\lq} +\endgroup +% +% Setup for the @verb command. +% +% Eight spaces for a tab +\begingroup + \catcode`\^^I=\active + \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} +\endgroup +% +\def\setupverb{% + \tt % easiest (and conventionally used) font for verbatim + \def\par{\leavevmode\endgraf}% + \catcode`\`=\active + \tabeightspaces + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces +} + +% Setup for the @verbatim environment +% +% Real tab expansion +\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount +% +\def\starttabbox{\setbox0=\hbox\bgroup} +\begingroup + \catcode`\^^I=\active + \gdef\tabexpand{% + \catcode`\^^I=\active + \def^^I{\leavevmode\egroup + \dimen0=\wd0 % the width so far, or since the previous tab + \divide\dimen0 by\tabw + \multiply\dimen0 by\tabw % compute previous multiple of \tabw + \advance\dimen0 by\tabw % advance to next multiple of \tabw + \wd0=\dimen0 \box0 \starttabbox + }% + } +\endgroup +\def\setupverbatim{% + % Easiest (and conventionally used) font for verbatim + \tt + \def\par{\leavevmode\egroup\box0\endgraf}% + \catcode`\`=\active + \tabexpand + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces + \everypar{\starttabbox}% +} + +% Do the @verb magic: verbatim text is quoted by unique +% delimiter characters. Before first delimiter expect a +% right brace, after last delimiter expect closing brace: +% +% \def\doverb'{'#1'}'{#1} +% +% [Knuth] p. 382; only eat outer {} +\begingroup + \catcode`[=1\catcode`]=2\catcode`\{=12\catcode`\}=12 + \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] +\endgroup +% +\def\verb{\begingroup\setupverb\doverb} +% +% +% Do the @verbatim magic: define the macro \doverbatim so that +% the (first) argument ends when '@end verbatim' is reached, ie: +% +% \def\doverbatim#1@end verbatim{#1} +% +% For Texinfo it's a lot easier than for LaTeX, +% because texinfo's \verbatim doesn't stop at '\end{verbatim}': +% we need not redefine '\', '{' and '}' +% +% Inspired by LaTeX's verbatim command set [latex.ltx] +%% Include LaTeX hack for completeness -- never know +%% \begingroup +%% \catcode`|=0 \catcode`[=1 +%% \catcode`]=2\catcode`\{=12\catcode`\}=12\catcode`\ =\active +%% \catcode`\\=12|gdef|doverbatim#1@end verbatim[ +%% #1|endgroup|def|Everbatim[]|end[verbatim]] +%% |endgroup +\begingroup + \catcode`\ =\active + \gdef\doverbatim#1@end verbatim{#1\end{verbatim}} +\endgroup +% +\def\verbatim{% + \def\Everbatim{\nonfillfinish\endgroup}% + \begingroup + \nonfillstart + \advance\leftskip by -\defbodyindent + \begingroup\setupverbatim\doverbatim +} + +% @verbatiminclude FILE - insert text of file in verbatim environment. +% +% Allow normal characters that we make active in the argument (a file name). +\def\verbatiminclude{% + \begingroup + \catcode`\\=12 + \catcode`~=12 + \catcode`^=12 + \catcode`_=12 + \catcode`|=12 + \catcode`<=12 + \catcode`>=12 + \catcode`+=12 + \parsearg\doverbatiminclude +} +\def\setupverbatiminclude{% + \begingroup + \nonfillstart + \advance\leftskip by -\defbodyindent + \begingroup\setupverbatim +} +% +\def\doverbatiminclude#1{% + % Restore active chars for included file. + \endgroup + \begingroup + \def\thisfile{#1}% + \expandafter\expandafter\setupverbatiminclude\input\thisfile + \endgroup\nonfillfinish\endgroup +} + + +\message{defuns,} +% @defun etc. + +% Allow user to change definition object font (\df) internally +\def\setdeffont #1 {\csname DEF#1\endcsname} + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deftypemargin \deftypemargin=12pt +\newskip\deflastargmargin \deflastargmargin=18pt + +\newcount\parencount +% define \functionparens, which makes ( and ) and & do special things. +% \functionparens affects the group it is contained in. +\def\activeparens{% +\catcode`\(=\active \catcode`\)=\active \catcode`\&=\active +\catcode`\[=\active \catcode`\]=\active} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +{\activeparens % Now, smart parens don't turn on until &foo (see \amprm) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +\global\let(=\lparen \global\let)=\rparen +\global\let[=\lbrack \global\let]=\rbrack + +\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 } +\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} +% This is used to turn on special parens +% but make & act ordinary (given that it's active). +\gdef\boldbraxnoamp{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb\let&=\ampnr} + +% Definitions of (, ) and & used in args for functions. +% This is the definition of ( outside of all parentheses. +\gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested + \global\advance\parencount by 1 +} +% +% This is the definition of ( when already inside a level of parens. +\gdef\opnested{\char`\(\global\advance\parencount by 1 } +% +\gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0. + % also in that case restore the outer-level definition of (. + \ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi + \global\advance \parencount by -1 } +% If we encounter &foo, then turn on ()-hacking afterwards +\gdef\amprm#1 {{\rm\}\let(=\oprm \let)=\clrm\ } +% +\gdef\normalparens{\boldbrax\let&=\ampnr} +} % End of definition inside \activeparens +%% These parens (in \boldbrax) actually are a little bolder than the +%% contained text. This is especially needed for [ and ] +\def\opnr{{\sf\char`\(}\global\advance\parencount by 1 } +\def\clnr{{\sf\char`\)}\global\advance\parencount by -1 } +\let\ampnr = \& +\def\lbrb{{\bf\char`\[}} +\def\rbrb{{\bf\char`\]}} + +% Active &'s sneak into the index arguments, so make sure it's defined. +{ + \catcode`& = 13 + \global\let& = \ampnr +} + +% First, defname, which formats the header line itself. +% #1 should be the function name. +% #2 should be the type of definition, such as "Function". + +\def\defname #1#2{% +% Get the values of \leftskip and \rightskip as they were +% outside the @def... +\dimen2=\leftskip +\advance\dimen2 by -\defbodyindent +\noindent +\setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}% +\dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line +\dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations +\parshape 2 0in \dimen0 \defargsindent \dimen1 +% Now output arg 2 ("Function" or some such) +% ending at \deftypemargin from the right margin, +% but stuck inside a box of width 0 so it does not interfere with linebreaking +{% Adjust \hsize to exclude the ambient margins, +% so that \rightline will obey them. +\advance \hsize by -\dimen2 +\rlap{\rightline{{\rm #2}\hskip -1.25pc }}}% +% Make all lines underfull and no complaints: +\tolerance=10000 \hbadness=10000 +\advance\leftskip by -\defbodyindent +\exdentamount=\defbodyindent +{\df #1}\enskip % Generate function name +} + +% Actually process the body of a definition +% #1 should be the terminating control sequence, such as \Edefun. +% #2 should be the "another name" control sequence, such as \defunx. +% #3 should be the control sequence that actually processes the header, +% such as \defunheader. + +\def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2{\begingroup\obeylines\activeparens\spacesplit#3}% +\parindent=0in +\advance\leftskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup % +\catcode 61=\active % 61 is `=' +\obeylines\activeparens\spacesplit#3} + +% #1 is the \E... control sequence to end the definition (which we define). +% #2 is the \...x control sequence for consecutive fns (which we define). +% #3 is the control sequence to call to resume processing. +% #4, delimited by the space, is the class name. +% +\def\defmethparsebody#1#2#3#4 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}% +\parindent=0in +\advance\leftskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\activeparens\spacesplit{#3{#4}}} + +% Used for @deftypemethod and @deftypeivar. +% #1 is the \E... control sequence to end the definition (which we define). +% #2 is the \...x control sequence for consecutive fns (which we define). +% #3 is the control sequence to call to resume processing. +% #4, delimited by a space, is the class name. +% #5 is the method's return type. +% +\def\deftypemethparsebody#1#2#3#4 #5 {\begingroup\inENV + \medbreak + \def#1{\endgraf\endgroup\medbreak}% + \def#2##1 ##2 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}{##2}}}% + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent + \begingroup\obeylines\activeparens\spacesplit{#3{#4}{#5}}} + +% Used for @deftypeop. The change from \deftypemethparsebody is an +% extra argument at the beginning which is the `category', instead of it +% being the hardwired string `Method' or `Instance Variable'. We have +% to account for this both in the \...x definition and in parsing the +% input at hand. Thus also need a control sequence (passed as #5) for +% the \E... definition to assign the category name to. +% +\def\deftypeopparsebody#1#2#3#4#5 #6 {\begingroup\inENV + \medbreak + \def#1{\endgraf\endgroup\medbreak}% + \def#2##1 ##2 ##3 {% + \def#4{##1}% + \begingroup\obeylines\activeparens\spacesplit{#3{##2}{##3}}}% + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent + \begingroup\obeylines\activeparens\spacesplit{#3{#5}{#6}}} + +\def\defopparsebody #1#2#3#4#5 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 ##2 {\def#4{##1}% +\begingroup\obeylines\activeparens\spacesplit{#3{##2}}}% +\parindent=0in +\advance\leftskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\activeparens\spacesplit{#3{#5}}} + +% These parsing functions are similar to the preceding ones +% except that they do not make parens into active characters. +% These are used for "variables" since they have no arguments. + +\def\defvarparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2{\begingroup\obeylines\spacesplit#3}% +\parindent=0in +\advance\leftskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup % +\catcode 61=\active % +\obeylines\spacesplit#3} + +% This is used for \def{tp,vr}parsebody. It could probably be used for +% some of the others, too, with some judicious conditionals. +% +\def\parsebodycommon#1#2#3{% + \begingroup\inENV % + \medbreak % + % Define the end token that this defining construct specifies + % so that it will exit this group. + \def#1{\endgraf\endgroup\medbreak}% + \def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}% + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent + \begingroup\obeylines +} + +\def\defvrparsebody#1#2#3#4 {% + \parsebodycommon{#1}{#2}{#3}% + \spacesplit{#3{#4}}% +} + +% This loses on `@deftp {Data Type} {struct termios}' -- it thinks the +% type is just `struct', because we lose the braces in `{struct +% termios}' when \spacesplit reads its undelimited argument. Sigh. +% \let\deftpparsebody=\defvrparsebody +% +% So, to get around this, we put \empty in with the type name. That +% way, TeX won't find exactly `{...}' as an undelimited argument, and +% won't strip off the braces. +% +\def\deftpparsebody #1#2#3#4 {% + \parsebodycommon{#1}{#2}{#3}% + \spacesplit{\parsetpheaderline{#3{#4}}}\empty +} + +% Fine, but then we have to eventually remove the \empty *and* the +% braces (if any). That's what this does. +% +\def\removeemptybraces\empty#1\relax{#1} + +% After \spacesplit has done its work, this is called -- #1 is the final +% thing to call, #2 the type name (which starts with \empty), and #3 +% (which might be empty) the arguments. +% +\def\parsetpheaderline#1#2#3{% + #1{\removeemptybraces#2\relax}{#3}% +}% + +\def\defopvarparsebody #1#2#3#4#5 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 ##2 {\def#4{##1}% +\begingroup\obeylines\spacesplit{#3{##2}}}% +\parindent=0in +\advance\leftskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\spacesplit{#3{#5}}} + +% Split up #2 at the first space token. +% call #1 with two arguments: +% the first is all of #2 before the space token, +% the second is all of #2 after that space token. +% If #2 contains no space token, all of it is passed as the first arg +% and the second is passed as empty. + +{\obeylines +\gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}% +\long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{% +\ifx\relax #3% +#1{#2}{}\else #1{#2}{#3#4}\fi}} + +% So much for the things common to all kinds of definitions. + +% Define @defun. + +% First, define the processing that is wanted for arguments of \defun +% Use this to expand the args and terminate the paragraph they make up + +\def\defunargs#1{\functionparens \sl +% Expand, preventing hyphenation at `-' chars. +% Note that groups don't affect changes in \hyphenchar. +% Set the font temporarily and use \font in case \setfont made \tensl a macro. +{\tensl\hyphenchar\font=0}% +#1% +{\tensl\hyphenchar\font=45}% +\ifnum\parencount=0 \else \errmessage{Unbalanced parentheses in @def}\fi% +\interlinepenalty=10000 +\advance\rightskip by 0pt plus 1fil +\endgraf\nobreak\vskip -\parskip\nobreak +} + +\def\deftypefunargs #1{% +% Expand, preventing hyphenation at `-' chars. +% Note that groups don't affect changes in \hyphenchar. +% Use \boldbraxnoamp, not \functionparens, so that & is not special. +\boldbraxnoamp +\tclose{#1}% avoid \code because of side effects on active chars +\interlinepenalty=10000 +\advance\rightskip by 0pt plus 1fil +\endgraf\nobreak\vskip -\parskip\nobreak +} + +% Do complete processing of one @defun or @defunx line already parsed. + +% @deffn Command forward-char nchars + +\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader} + +\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}% +\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defun == @deffn Function + +\def\defun{\defparsebody\Edefun\defunx\defunheader} + +\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{\putwordDeffunc}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @deftypefun int foobar (int @var{foo}, float @var{bar}) + +\def\deftypefun{\defparsebody\Edeftypefun\deftypefunx\deftypefunheader} + +% #1 is the data type. #2 is the name and args. +\def\deftypefunheader #1#2{\deftypefunheaderx{#1}#2 \relax} +% #1 is the data type, #2 the name, #3 the args. +\def\deftypefunheaderx #1#2 #3\relax{% +\doind {fn}{\code{#2}}% Make entry in function index +\begingroup\defname {\defheaderxcond#1\relax$$$#2}{\putwordDeftypefun}% +\deftypefunargs {#3}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @deftypefn {Library Function} int foobar (int @var{foo}, float @var{bar}) + +\def\deftypefn{\defmethparsebody\Edeftypefn\deftypefnx\deftypefnheader} + +% \defheaderxcond#1\relax$$$ +% puts #1 in @code, followed by a space, but does nothing if #1 is null. +\def\defheaderxcond#1#2$$${\ifx#1\relax\else\code{#1#2} \fi} + +% #1 is the classification. #2 is the data type. #3 is the name and args. +\def\deftypefnheader #1#2#3{\deftypefnheaderx{#1}{#2}#3 \relax} +% #1 is the classification, #2 the data type, #3 the name, #4 the args. +\def\deftypefnheaderx #1#2#3 #4\relax{% +\doind {fn}{\code{#3}}% Make entry in function index +\begingroup +\normalparens % notably, turn off `&' magic, which prevents +% at least some C++ text from working +\defname {\defheaderxcond#2\relax$$$#3}{#1}% +\deftypefunargs {#4}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defmac == @deffn Macro + +\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader} + +\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{\putwordDefmac}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defspec == @deffn Special Form + +\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader} + +\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{\putwordDefspec}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defop CATEGORY CLASS OPERATION ARG... +% +\def\defop #1 {\def\defoptype{#1}% +\defopparsebody\Edefop\defopx\defopheader\defoptype} +% +\def\defopheader#1#2#3{% +\dosubind {fn}{\code{#2}}{\putwordon\ #1}% Make entry in function index +\begingroup\defname {#2}{\defoptype\ \putwordon\ #1}% +\defunargs {#3}\endgroup % +} + +% @deftypeop CATEGORY CLASS TYPE OPERATION ARG... +% +\def\deftypeop #1 {\def\deftypeopcategory{#1}% + \deftypeopparsebody\Edeftypeop\deftypeopx\deftypeopheader + \deftypeopcategory} +% +% #1 is the class name, #2 the data type, #3 the operation name, #4 the args. +\def\deftypeopheader#1#2#3#4{% + \dosubind{fn}{\code{#3}}{\putwordon\ \code{#1}}% entry in function index + \begingroup + \defname{\defheaderxcond#2\relax$$$#3} + {\deftypeopcategory\ \putwordon\ \code{#1}}% + \deftypefunargs{#4}% + \endgroup +} + +% @deftypemethod CLASS TYPE METHOD ARG... +% +\def\deftypemethod{% + \deftypemethparsebody\Edeftypemethod\deftypemethodx\deftypemethodheader} +% +% #1 is the class name, #2 the data type, #3 the method name, #4 the args. +\def\deftypemethodheader#1#2#3#4{% + \dosubind{fn}{\code{#3}}{\putwordon\ \code{#1}}% entry in function index + \begingroup + \defname{\defheaderxcond#2\relax$$$#3}{\putwordMethodon\ \code{#1}}% + \deftypefunargs{#4}% + \endgroup +} + +% @deftypeivar CLASS TYPE VARNAME +% +\def\deftypeivar{% + \deftypemethparsebody\Edeftypeivar\deftypeivarx\deftypeivarheader} +% +% #1 is the class name, #2 the data type, #3 the variable name. +\def\deftypeivarheader#1#2#3{% + \dosubind{vr}{\code{#3}}{\putwordof\ \code{#1}}% entry in variable index + \begingroup + \defname{\defheaderxcond#2\relax$$$#3} + {\putwordInstanceVariableof\ \code{#1}}% + \defvarargs{#3}% + \endgroup +} + +% @defmethod == @defop Method +% +\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader} +% +% #1 is the class name, #2 the method name, #3 the args. +\def\defmethodheader#1#2#3{% + \dosubind{fn}{\code{#2}}{\putwordon\ \code{#1}}% entry in function index + \begingroup + \defname{#2}{\putwordMethodon\ \code{#1}}% + \defunargs{#3}% + \endgroup +} + +% @defcv {Class Option} foo-class foo-flag + +\def\defcv #1 {\def\defcvtype{#1}% +\defopvarparsebody\Edefcv\defcvx\defcvarheader\defcvtype} + +\def\defcvarheader #1#2#3{% +\dosubind {vr}{\code{#2}}{\putwordof\ #1}% Make entry in var index +\begingroup\defname {#2}{\defcvtype\ \putwordof\ #1}% +\defvarargs {#3}\endgroup % +} + +% @defivar CLASS VARNAME == @defcv {Instance Variable} CLASS VARNAME +% +\def\defivar{\defvrparsebody\Edefivar\defivarx\defivarheader} +% +\def\defivarheader#1#2#3{% + \dosubind {vr}{\code{#2}}{\putwordof\ #1}% entry in var index + \begingroup + \defname{#2}{\putwordInstanceVariableof\ #1}% + \defvarargs{#3}% + \endgroup +} + +% @defvar +% First, define the processing that is wanted for arguments of @defvar. +% This is actually simple: just print them in roman. +% This must expand the args and terminate the paragraph they make up +\def\defvarargs #1{\normalparens #1% +\interlinepenalty=10000 +\endgraf\nobreak\vskip -\parskip\nobreak} + +% @defvr Counter foo-count + +\def\defvr{\defvrparsebody\Edefvr\defvrx\defvrheader} + +\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}% +\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup} + +% @defvar == @defvr Variable + +\def\defvar{\defvarparsebody\Edefvar\defvarx\defvarheader} + +\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index +\begingroup\defname {#1}{\putwordDefvar}% +\defvarargs {#2}\endgroup % +} + +% @defopt == @defvr {User Option} + +\def\defopt{\defvarparsebody\Edefopt\defoptx\defoptheader} + +\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index +\begingroup\defname {#1}{\putwordDefopt}% +\defvarargs {#2}\endgroup % +} + +% @deftypevar int foobar + +\def\deftypevar{\defvarparsebody\Edeftypevar\deftypevarx\deftypevarheader} + +% #1 is the data type. #2 is the name, perhaps followed by text that +% is actually part of the data type, which should not be put into the index. +\def\deftypevarheader #1#2{% +\dovarind#2 \relax% Make entry in variables index +\begingroup\defname {\defheaderxcond#1\relax$$$#2}{\putwordDeftypevar}% +\interlinepenalty=10000 +\endgraf\nobreak\vskip -\parskip\nobreak +\endgroup} +\def\dovarind#1 #2\relax{\doind{vr}{\code{#1}}} + +% @deftypevr {Global Flag} int enable + +\def\deftypevr{\defvrparsebody\Edeftypevr\deftypevrx\deftypevrheader} + +\def\deftypevrheader #1#2#3{\dovarind#3 \relax% +\begingroup\defname {\defheaderxcond#2\relax$$$#3}{#1} +\interlinepenalty=10000 +\endgraf\nobreak\vskip -\parskip\nobreak +\endgroup} + +% Now define @deftp +% Args are printed in bold, a slight difference from @defvar. + +\def\deftpargs #1{\bf \defvarargs{#1}} + +% @deftp Class window height width ... + +\def\deftp{\deftpparsebody\Edeftp\deftpx\deftpheader} + +\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}% +\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup} + +% These definitions are used if you use @defunx (etc.) +% anywhere other than immediately after a @defun or @defunx. +% +\def\defcvx#1 {\errmessage{@defcvx in invalid context}} +\def\deffnx#1 {\errmessage{@deffnx in invalid context}} +\def\defivarx#1 {\errmessage{@defivarx in invalid context}} +\def\defmacx#1 {\errmessage{@defmacx in invalid context}} +\def\defmethodx#1 {\errmessage{@defmethodx in invalid context}} +\def\defoptx #1 {\errmessage{@defoptx in invalid context}} +\def\defopx#1 {\errmessage{@defopx in invalid context}} +\def\defspecx#1 {\errmessage{@defspecx in invalid context}} +\def\deftpx#1 {\errmessage{@deftpx in invalid context}} +\def\deftypefnx#1 {\errmessage{@deftypefnx in invalid context}} +\def\deftypefunx#1 {\errmessage{@deftypefunx in invalid context}} +\def\deftypeivarx#1 {\errmessage{@deftypeivarx in invalid context}} +\def\deftypemethodx#1 {\errmessage{@deftypemethodx in invalid context}} +\def\deftypeopx#1 {\errmessage{@deftypeopx in invalid context}} +\def\deftypevarx#1 {\errmessage{@deftypevarx in invalid context}} +\def\deftypevrx#1 {\errmessage{@deftypevrx in invalid context}} +\def\defunx#1 {\errmessage{@defunx in invalid context}} +\def\defvarx#1 {\errmessage{@defvarx in invalid context}} +\def\defvrx#1 {\errmessage{@defvrx in invalid context}} + + +\message{macros,} +% @macro. + +% To do this right we need a feature of e-TeX, \scantokens, +% which we arrange to emulate with a temporary file in ordinary TeX. +\ifx\eTeXversion\undefined + \newwrite\macscribble + \def\scanmacro#1{% + \begingroup \newlinechar`\^^M + % Undo catcode changes of \startcontents and \doprintindex + \catcode`\@=0 \catcode`\\=12 \escapechar=`\@ + % Append \endinput to make sure that TeX does not see the ending newline. + \toks0={#1\endinput}% + \immediate\openout\macscribble=\jobname.tmp + \immediate\write\macscribble{\the\toks0}% + \immediate\closeout\macscribble + \let\xeatspaces\eatspaces + \input \jobname.tmp + \endgroup +} +\else +\def\scanmacro#1{% +\begingroup \newlinechar`\^^M +% Undo catcode changes of \startcontents and \doprintindex +\catcode`\@=0 \catcode`\\=12 \escapechar=`\@ +\let\xeatspaces\eatspaces\scantokens{#1\endinput}\endgroup} +\fi + +\newcount\paramno % Count of parameters +\newtoks\macname % Macro name +\newif\ifrecursive % Is it recursive? +\def\macrolist{} % List of all defined macros in the form + % \do\macro1\do\macro2... + +% Utility routines. +% Thisdoes \let #1 = #2, except with \csnames. +\def\cslet#1#2{% +\expandafter\expandafter +\expandafter\let +\expandafter\expandafter +\csname#1\endcsname +\csname#2\endcsname} + +% Trim leading and trailing spaces off a string. +% Concepts from aro-bend problem 15 (see CTAN). +{\catcode`\@=11 +\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} +\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} +\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} +\def\unbrace#1{#1} +\unbrace{\gdef\trim@@@ #1 } #2@{#1} +} + +% Trim a single trailing ^^M off a string. +{\catcode`\^^M=12\catcode`\Q=3% +\gdef\eatcr #1{\eatcra #1Q^^MQ}% +\gdef\eatcra#1^^MQ{\eatcrb#1Q}% +\gdef\eatcrb#1Q#2Q{#1}% +} + +% Macro bodies are absorbed as an argument in a context where +% all characters are catcode 10, 11 or 12, except \ which is active +% (as in normal texinfo). It is necessary to change the definition of \. + +% It's necessary to have hard CRs when the macro is executed. This is +% done by making ^^M (\endlinechar) catcode 12 when reading the macro +% body, and then making it the \newlinechar in \scanmacro. + +\def\macrobodyctxt{% + \catcode`\~=12 + \catcode`\^=12 + \catcode`\_=12 + \catcode`\|=12 + \catcode`\<=12 + \catcode`\>=12 + \catcode`\+=12 + \catcode`\{=12 + \catcode`\}=12 + \catcode`\@=12 + \catcode`\^^M=12 + \usembodybackslash} + +\def\macroargctxt{% + \catcode`\~=12 + \catcode`\^=12 + \catcode`\_=12 + \catcode`\|=12 + \catcode`\<=12 + \catcode`\>=12 + \catcode`\+=12 + \catcode`\@=12 + \catcode`\\=12} + +% \mbodybackslash is the definition of \ in @macro bodies. +% It maps \foo\ => \csname macarg.foo\endcsname => #N +% where N is the macro parameter number. +% We define \csname macarg.\endcsname to be \realbackslash, so +% \\ in macro replacement text gets you a backslash. + +{\catcode`@=0 @catcode`@\=@active + @gdef@usembodybackslash{@let\=@mbodybackslash} + @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} +} +\expandafter\def\csname macarg.\endcsname{\realbackslash} + +\def\macro{\recursivefalse\parsearg\macroxxx} +\def\rmacro{\recursivetrue\parsearg\macroxxx} + +\def\macroxxx#1{% + \getargs{#1}% now \macname is the macname and \argl the arglist + \ifx\argl\empty % no arguments + \paramno=0% + \else + \expandafter\parsemargdef \argl;% + \fi + \if1\csname ismacro.\the\macname\endcsname + \message{Warning: redefining \the\macname}% + \else + \expandafter\ifx\csname \the\macname\endcsname \relax + \else \errmessage{The name \the\macname\space is reserved}\fi + \global\cslet{macsave.\the\macname}{\the\macname}% + \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% + % Add the macroname to \macrolist + \toks0 = \expandafter{\macrolist\do}% + \xdef\macrolist{\the\toks0 + \expandafter\noexpand\csname\the\macname\endcsname}% + \fi + \begingroup \macrobodyctxt + \ifrecursive \expandafter\parsermacbody + \else \expandafter\parsemacbody + \fi} + +\def\unmacro{\parsearg\unmacroxxx} +\def\unmacroxxx#1{% + \if1\csname ismacro.#1\endcsname + \global\cslet{#1}{macsave.#1}% + \global\expandafter\let \csname ismacro.#1\endcsname=0% + % Remove the macro name from \macrolist + \begingroup + \edef\tempa{\expandafter\noexpand\csname#1\endcsname}% + \def\do##1{% + \def\tempb{##1}% + \ifx\tempa\tempb + % remove this + \else + \toks0 = \expandafter{\newmacrolist\do}% + \edef\newmacrolist{\the\toks0\expandafter\noexpand\tempa}% + \fi}% + \def\newmacrolist{}% + % Execute macro list to define \newmacrolist + \macrolist + \global\let\macrolist\newmacrolist + \endgroup + \else + \errmessage{Macro #1 not defined}% + \fi +} + +% This makes use of the obscure feature that if the last token of a +% is #, then the preceding argument is delimited by +% an opening brace, and that opening brace is not consumed. +\def\getargs#1{\getargsxxx#1{}} +\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} +\def\getmacname #1 #2\relax{\macname={#1}} +\def\getmacargs#1{\def\argl{#1}} + +% Parse the optional {params} list. Set up \paramno and \paramlist +% so \defmacro knows what to do. Define \macarg.blah for each blah +% in the params list, to be ##N where N is the position in that list. +% That gets used by \mbodybackslash (above). + +% We need to get `macro parameter char #' into several definitions. +% The technique used is stolen from LaTeX: let \hash be something +% unexpandable, insert that wherever you need a #, and then redefine +% it to # just before using the token list produced. +% +% The same technique is used to protect \eatspaces till just before +% the macro is used. + +\def\parsemargdef#1;{\paramno=0\def\paramlist{}% + \let\hash\relax\let\xeatspaces\relax\parsemargdefxxx#1,;,} +\def\parsemargdefxxx#1,{% + \if#1;\let\next=\relax + \else \let\next=\parsemargdefxxx + \advance\paramno by 1% + \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname + {\xeatspaces{\hash\the\paramno}}% + \edef\paramlist{\paramlist\hash\the\paramno,}% + \fi\next} + +% These two commands read recursive and nonrecursive macro bodies. +% (They're different since rec and nonrec macros end differently.) + +\long\def\parsemacbody#1@end macro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% +\long\def\parsermacbody#1@end rmacro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% + +% This defines the macro itself. There are six cases: recursive and +% nonrecursive macros of zero, one, and many arguments. +% Much magic with \expandafter here. +% \xdef is used so that macro definitions will survive the file +% they're defined in; @include reads the file inside a group. +\def\defmacro{% + \let\hash=##% convert placeholders to macro parameter chars + \ifrecursive + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\scanmacro{\temp}}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup\noexpand\scanmacro{\temp}}% + \else % many + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{\egroup\noexpand\scanmacro{\temp}}% + \fi + \else + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \else % many + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \expandafter\noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \fi + \fi} + +\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} + +% \braceorline decides whether the next nonwhitespace character is a +% {. If so it reads up to the closing }, if not, it reads the whole +% line. Whatever was read is then fed to the next control sequence +% as an argument (by \parsebrace or \parsearg) +\def\braceorline#1{\let\next=#1\futurelet\nchar\braceorlinexxx} +\def\braceorlinexxx{% + \ifx\nchar\bgroup\else + \expandafter\parsearg + \fi \next} + +% We mant to disable all macros during \shipout so that they are not +% expanded by \write. +\def\turnoffmacros{\begingroup \def\do##1{\let\noexpand##1=\relax}% + \edef\next{\macrolist}\expandafter\endgroup\next} + + +% @alias. +% We need some trickery to remove the optional spaces around the equal +% sign. Just make them active and then expand them all to nothing. +\def\alias{\begingroup\obeyspaces\parsearg\aliasxxx} +\def\aliasxxx #1{\aliasyyy#1\relax} +\def\aliasyyy #1=#2\relax{\ignoreactivespaces +\edef\next{\global\let\expandafter\noexpand\csname#1\endcsname=% + \expandafter\noexpand\csname#2\endcsname}% +\expandafter\endgroup\next} + + +\message{cross references,} +% @xref etc. + +\newwrite\auxfile + +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% @inforef is relatively simple. +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +% @node's job is to define \lastnode. +\def\node{\ENVcheck\parsearg\nodezzz} +\def\nodezzz#1{\nodexxx [#1,]} +\def\nodexxx[#1,#2]{\gdef\lastnode{#1}} +\let\nwnode=\node +\let\lastnode=\relax + +% The sectioning commands (@chapter, etc.) call these. +\def\donoderef{% + \ifx\lastnode\relax\else + \expandafter\expandafter\expandafter\setref{\lastnode}% + {Ysectionnumberandtype}% + \global\let\lastnode=\relax + \fi +} +\def\unnumbnoderef{% + \ifx\lastnode\relax\else + \expandafter\expandafter\expandafter\setref{\lastnode}{Ynothing}% + \global\let\lastnode=\relax + \fi +} +\def\appendixnoderef{% + \ifx\lastnode\relax\else + \expandafter\expandafter\expandafter\setref{\lastnode}% + {Yappendixletterandtype}% + \global\let\lastnode=\relax + \fi +} + + +% @anchor{NAME} -- define xref target at arbitrary point. +% +\newcount\savesfregister +\gdef\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} +\gdef\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} +\gdef\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} + +% \setref{NAME}{SNT} defines a cross-reference point NAME, namely +% NAME-title, NAME-pg, and NAME-SNT. Called from \foonoderef. We have +% to set \indexdummies so commands such as @code in a section title +% aren't expanded. It would be nicer not to expand the titles in the +% first place, but there's so many layers that that is hard to do. +% +\def\setref#1#2{{% + \indexdummies + \pdfmkdest{#1}% + \dosetq{#1-title}{Ytitle}% + \dosetq{#1-pg}{Ypagenumber}% + \dosetq{#1-snt}{#2}% +}} + +% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is +% the node name, #2 the name of the Info cross-reference, #3 the printed +% node name, #4 the name of the Info file, #5 the name of the printed +% manual. All but the node name can be omitted. +% +\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} +\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} +\def\ref#1{\xrefX[#1,,,,,,,]} +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup + \unsepspaces + \def\printedmanual{\ignorespaces #5}% + \def\printednodename{\ignorespaces #3}% + \setbox1=\hbox{\printedmanual}% + \setbox0=\hbox{\printednodename}% + \ifdim \wd0 = 0pt + % No printed node name was explicitly given. + \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax + % Use the node name inside the square brackets. + \def\printednodename{\ignorespaces #1}% + \else + % Use the actual chapter/section title appear inside + % the square brackets. Use the real section title if we have it. + \ifdim \wd1 > 0pt + % It is in another manual, so we don't have it. + \def\printednodename{\ignorespaces #1}% + \else + \ifhavexrefs + % We know the real title if we have the xref values. + \def\printednodename{\refx{#1-title}{}}% + \else + % Otherwise just copy the Info node name. + \def\printednodename{\ignorespaces #1}% + \fi% + \fi + \fi + \fi + % + % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not + % insert empty discretionaries after hyphens, which means that it will + % not find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, this + % is a loss. Therefore, we give the text of the node name again, so it + % is as if TeX is seeing it for the first time. + \ifpdf + \leavevmode + \getfilename{#4}% + \ifnum\filenamelength>0 + \startlink attr{/Border [0 0 0]}% + goto file{\the\filename.pdf} name{#1@}% + \else + \startlink attr{/Border [0 0 0]}% + goto name{#1@}% + \fi + \linkcolor + \fi + % + \ifdim \wd1 > 0pt + \putwordsection{} ``\printednodename'' \putwordin{} \cite{\printedmanual}% + \else + % _ (for example) has to be the character _ for the purposes of the + % control sequence corresponding to the node, but it has to expand + % into the usual \leavevmode...\vrule stuff for purposes of + % printing. So we \turnoffactive for the \refx-snt, back on for the + % printing, back off for the \refx-pg. + {\normalturnoffactive + % Only output a following space if the -snt ref is nonempty; for + % @unnumbered and @anchor, it won't be. + \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% + \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi + }% + % [mynode], + [\printednodename],\space + % page 3 + \turnoffactive \putwordpage\tie\refx{#1-pg}{}% + \fi + \endlink +\endgroup} + +% \dosetq is the interface for calls from other macros + +% Use \normalturnoffactive so that punctuation chars such as underscore +% and backslash work in node names. (\turnoffactive doesn't do \.) +\def\dosetq#1#2{% + {\let\folio=0% + \normalturnoffactive + \edef\next{\write\auxfile{\internalsetq{#1}{#2}}}% + \iflinks + \next + \fi + }% +} + +% \internalsetq {foo}{page} expands into +% CHARACTERS 'xrdef {foo}{...expansion of \Ypage...} +% When the aux file is read, ' is the escape character + +\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}} + +% Things to be expanded by \internalsetq + +\def\Ypagenumber{\folio} + +\def\Ytitle{\thissection} + +\def\Ynothing{} + +\def\Ysectionnumberandtype{% +\ifnum\secno=0 \putwordChapter\xreftie\the\chapno % +\else \ifnum \subsecno=0 \putwordSection\xreftie\the\chapno.\the\secno % +\else \ifnum \subsubsecno=0 % +\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno % +\else % +\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno % +\fi \fi \fi } + +\def\Yappendixletterandtype{% +\ifnum\secno=0 \putwordAppendix\xreftie'char\the\appendixno{}% +\else \ifnum \subsecno=0 \putwordSection\xreftie'char\the\appendixno.\the\secno % +\else \ifnum \subsubsecno=0 % +\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno % +\else % +\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno % +\fi \fi \fi } + +\gdef\xreftie{'tie} + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Non-3.0. +\else + \def\linenumber{\the\inputlineno:\space} +\fi + +% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. +% If its value is nonempty, SUFFIX is output afterward. + +\def\refx#1#2{% + \expandafter\ifx\csname X#1\endcsname\relax + % If not defined, say something at least. + \angleleft un\-de\-fined\angleright + \iflinks + \ifhavexrefs + \message{\linenumber Undefined cross reference `#1'.}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \fi + \else + % It's defined, so just use it. + \csname X#1\endcsname + \fi + #2% Output the suffix in any case. +} + +% This is the macro invoked by entries in the aux file. +% +\def\xrdef#1{\begingroup + % Reenable \ as an escape while reading the second argument. + \catcode`\\ = 0 + \afterassignment\endgroup + \expandafter\gdef\csname X#1\endcsname +} + +% Read the last existing aux file, if any. No error if none exists. +\def\readauxfile{\begingroup + \catcode`\^^@=\other + \catcode`\^^A=\other + \catcode`\^^B=\other + \catcode`\^^C=\other + \catcode`\^^D=\other + \catcode`\^^E=\other + \catcode`\^^F=\other + \catcode`\^^G=\other + \catcode`\^^H=\other + \catcode`\^^K=\other + \catcode`\^^L=\other + \catcode`\^^N=\other + \catcode`\^^P=\other + \catcode`\^^Q=\other + \catcode`\^^R=\other + \catcode`\^^S=\other + \catcode`\^^T=\other + \catcode`\^^U=\other + \catcode`\^^V=\other + \catcode`\^^W=\other + \catcode`\^^X=\other + \catcode`\^^Z=\other + \catcode`\^^[=\other + \catcode`\^^\=\other + \catcode`\^^]=\other + \catcode`\^^^=\other + \catcode`\^^_=\other + \catcode`\@=\other + \catcode`\^=\other + % It was suggested to define this as 7, which would allow ^^e4 etc. + % in xref tags, i.e., node names. But since ^^e4 notation isn't + % supported in the main text, it doesn't seem desirable. Furthermore, + % that is not enough: for node names that actually contain a ^ + % character, we would end up writing a line like this: 'xrdef {'hat + % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first + % argument, and \hat is not an expandable control sequence. It could + % all be worked out, but why? Either we support ^^ or we don't. + % + % The other change necessary for this was to define \auxhat: + % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter + % and then to call \auxhat in \setq. + % + \catcode`\~=\other + \catcode`\[=\other + \catcode`\]=\other + \catcode`\"=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\$=\other + \catcode`\#=\other + \catcode`\&=\other + \catcode`+=\other % avoid \+ for paranoia even though we've turned it off + % Make the characters 128-255 be printing characters + {% + \count 1=128 + \def\loop{% + \catcode\count 1=\other + \advance\count 1 by 1 + \ifnum \count 1<256 \loop \fi + }% + }% + % The aux file uses ' as the escape (for now). + % Turn off \ as an escape so we do not lose on + % entries which were dumped with control sequences in their names. + % For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^ + % Reference to such entries still does not work the way one would wish, + % but at least they do not bomb out when the aux file is read in. + \catcode`\{=1 + \catcode`\}=2 + \catcode`\%=\other + \catcode`\'=0 + \catcode`\\=\other + % + \openin 1 \jobname.aux + \ifeof 1 \else + \closein 1 + \input \jobname.aux + \global\havexrefstrue + \global\warnedobstrue + \fi + % Open the new aux file. TeX will close it automatically at exit. + \openout\auxfile=\jobname.aux +\endgroup} + + +% Footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. (Generally, numeric constants should always be followed by a +% space to prevent strange expansion errors.) +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for info output only. +\let\footnotestyle=\comment + +\let\ptexfootnote=\footnote + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \footnotezzz +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +% Oh yes, they do; otherwise, @ifset and anything else that uses +% \parseargline fail inside footnotes because the tokens are fixed when +% the footnote is read. --karl, 16nov96. +% +\long\gdef\footnotezzz{\insert\footins\bgroup + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + \smallfonts \rm + % + % Hang the footnote text off the number. + \hang + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + \futurelet\next\fo@t +} +\def\fo@t{\ifcat\bgroup\noexpand\next \let\next\f@@t + \else\let\next\f@t\fi \next} +\def\f@@t{\bgroup\aftergroup\@foot\let\next} +\def\f@t#1{#1\@foot} +\def\@foot{\strut\par\egroup} + +}%end \catcode `\@=11 + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +\def\setleading#1{% + \normalbaselineskip = #1\relax + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt} + +% @image. We use the macros from epsf.tex to support this. +% If epsf.tex is not installed and @image is used, we complain. +% +% Check for and read epsf.tex up front. If we read it only at @image +% time, we might be inside a group, and then its definitions would get +% undone and the next image would fail. +\openin 1 = epsf.tex +\ifeof 1 \else + \closein 1 + % Do not bother showing banner with post-v2.7 epsf.tex (available in + % doc/epsf.tex until it shows up on ctan). + \def\epsfannounce{\toks0 = }% + \input epsf.tex +\fi +% +% We will only complain once about lack of epsf.tex. +\newif\ifwarnednoepsf +\newhelp\noepsfhelp{epsf.tex must be installed for images to + work. It is also included in the Texinfo distribution, or you can get + it from ftp://tug.org/tex/epsf.tex.} +% +\def\image#1{% + \ifx\epsfbox\undefined + \ifwarnednoepsf \else + \errhelp = \noepsfhelp + \errmessage{epsf.tex not found, images will be ignored}% + \global\warnednoepsftrue + \fi + \else + \imagexxx #1,,,\finish + \fi +} +% +% Arguments to @image: +% #1 is (mandatory) image filename; we tack on .eps extension. +% #2 is (optional) width, #3 is (optional) height. +% #4 is just the usual extra ignored arg for parsing this stuff. +\def\imagexxx#1,#2,#3,#4\finish{% + \ifpdf + \centerline{\dopdfimage{#1}{#2}{#3}}% + \else + % \epsfbox itself resets \epsf?size at each figure. + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi + \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi + \begingroup + \catcode`\^^M = 5 % in case we're inside an example + \normalturnoffactive % allow _ et al. in names + % If the image is by itself, center it. + \ifvmode + \nobreak\bigskip + % Usually we'll have text after the image which will insert + % \parskip glue, so insert it here too to equalize the space + % above and below. + \nobreak\vskip\parskip + \nobreak + \centerline{\epsfbox{#1.eps}}% + \bigbreak + \else + % In the middle of a paragraph, no extra space. + \epsfbox{#1.eps}% + \fi + \endgroup + \fi +} + + +\message{localization,} +% and i18n. + +% @documentlanguage is usually given very early, just after +% @setfilename. If done too late, it may not override everything +% properly. Single argument is the language abbreviation. +% It would be nice if we could set up a hyphenation file here. +% +\def\documentlanguage{\parsearg\dodocumentlanguage} +\def\dodocumentlanguage#1{% + \tex % read txi-??.tex file in plain TeX. + % Read the file if it exists. + \openin 1 txi-#1.tex + \ifeof1 + \errhelp = \nolanghelp + \errmessage{Cannot read language file txi-#1.tex}% + \let\temp = \relax + \else + \def\temp{\input txi-#1.tex }% + \fi + \temp + \endgroup +} +\newhelp\nolanghelp{The given language definition file cannot be found or +is empty. Maybe you need to install it? In the current directory +should work if nowhere else does.} + + +% @documentencoding should change something in TeX eventually, most +% likely, but for now just recognize it. +\let\documentencoding = \comment + + +% Page size parameters. +% +\newdimen\defaultparindent \defaultparindent = 15pt + +\chapheadingskip = 15pt plus 4pt minus 2pt +\secheadingskip = 12pt plus 3pt minus 2pt +\subsecheadingskip = 9pt plus 2pt minus 2pt + +% Prevent underfull vbox error messages. +\vbadness = 10000 + +% Don't be so finicky about underfull hboxes, either. +\hbadness = 2000 + +% Following George Bush, just get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. We call this whenever the paper size is set. +% +\def\setemergencystretch{% + \ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% + \else + \emergencystretch = .15\hsize + \fi +} + +% Parameters in order: 1) textheight; 2) textwidth; 3) voffset; +% 4) hoffset; 5) binding offset; 6) topskip. Then whoever calls us can +% set \parskip and call \setleading for \baselineskip. +% +\def\internalpagesizes#1#2#3#4#5#6{% + \voffset = #3\relax + \topskip = #6\relax + \splittopskip = \topskip + % + \vsize = #1\relax + \advance\vsize by \topskip + \outervsize = \vsize + \advance\outervsize by 2\topandbottommargin + \pageheight = \vsize + % + \hsize = #2\relax + \outerhsize = \hsize + \advance\outerhsize by 0.5in + \pagewidth = \hsize + % + \normaloffset = #4\relax + \bindingoffset = #5\relax + % + \parindent = \defaultparindent + \setemergencystretch +} + +% Use `small' versions. +% +\def\smallenvironments{% + \let\smalldisplay = \smalldisplayx + \let\smallexample = \smalllispx + \let\smallformat = \smallformatx + \let\smalllisp = \smalllispx +} + +% @letterpaper (the default). +\def\letterpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \setleading{13.2pt}% + % + % If page is nothing but text, make it come out even. + \internalpagesizes{46\baselineskip}{6in}{\voffset}{.25in}{\bindingoffset}{36pt}% +}} + +% Use @smallbook to reset parameters for 7x9.5 (or so) format. +\def\smallbook{{\globaldefs = 1 + \parskip = 2pt plus 1pt + \setleading{12pt}% + % + \internalpagesizes{7.5in}{5.in}{\voffset}{.25in}{\bindingoffset}{16pt}% + % + \lispnarrowing = 0.3in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \deftypemargin = 0pt + \defbodyindent = .5cm + \smallenvironments +}} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{{\globaldefs = 1 + \setleading{12pt}% + \parskip = 3pt plus 2pt minus 1pt + % + \internalpagesizes{53\baselineskip}{160mm}{\voffset}{4mm}{\bindingoffset}{44pt}% + % + \tolerance = 700 + \hfuzz = 1pt +}} + +% Use @afivepaper to print on European A5 paper. +% From romildo@urano.iceb.ufop.br, 2 July 2000. +% He also recommends making @example and @lisp be small. +\def\afivepaper{{\globaldefs = 1 + \setleading{12.5pt}% + \parskip = 2pt plus 1pt minus 0.1pt + % + \internalpagesizes{166mm}{120mm}{\voffset}{-8mm}{\bindingoffset}{8pt}% + % + \lispnarrowing = 0.2in + \tolerance = 800 + \hfuzz = 1.2pt + \contentsrightmargin = 0mm + \deftypemargin = 0pt + \defbodyindent = 2mm + \tableindent = 12mm + % + \smallenvironments +}} + +% A specific text layout, 24x15cm overall, intended for A4 paper. Top margin +% 29mm, hence bottom margin 28mm, nominal side margin 3cm. +\def\afourlatex{{\globaldefs = 1 + \setleading{13.6pt}% + % + \afourpaper + \internalpagesizes{237mm}{150mm}{3.6mm}{3.6mm}{3mm}{7mm}% + % + \globaldefs = 0 +}} + +% Use @afourwide to print on European A4 paper in wide format. +\def\afourwide{% + \afourpaper + \internalpagesizes{6.5in}{9.5in}{\hoffset}{\normaloffset}{\bindingoffset}{7mm}% + % + \globaldefs = 0 +} + +% @pagesizes TEXTHEIGHT[,TEXTWIDTH] +% Perhaps we should allow setting the margins, \topskip, \parskip, +% and/or leading, also. Or perhaps we should compute them somehow. +% +\def\pagesizes{\parsearg\pagesizesxxx} +\def\pagesizesxxx#1{\pagesizesyyy #1,,\finish} +\def\pagesizesyyy#1,#2,#3\finish{{% + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi + \globaldefs = 1 + % + \parskip = 3pt plus 2pt minus 1pt + \setleading{13.2pt}% + % + \internalpagesizes{#1}{\hsize}{\voffset}{\normaloffset}{\bindingoffset}{44pt}% +}} + +% Set default to letter. +% +\letterpaper + + +\message{and turning on texinfo input format.} + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other +\catcode`\~=\other +\catcode`\^=\other +\catcode`\_=\other +\catcode`\|=\other +\catcode`\<=\other +\catcode`\>=\other +\catcode`\+=\other +\catcode`\$=\other +\def\normaldoublequote{"} +\def\normaltilde{~} +\def\normalcaret{^} +\def\normalunderscore{_} +\def\normalverticalbar{|} +\def\normalless{<} +\def\normalgreater{>} +\def\normalplus{+} +\def\normaldollar{$} + +% This macro is used to make a character print one way in ttfont +% where it can probably just be output, and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} + +% Same as above, but check for italic font. Actually this also catches +% non-italic slanted fonts since it is impossible to distinguish them from +% italic fonts. But since this is only used by $ and it uses \sl anyway +% this is not a problem. +\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} + +% Turn off all special characters except @ +% (and those which the user can use as if they were ordinary). +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. + +\catcode`\"=\active +\def\activedoublequote{{\tt\char34}} +\let"=\activedoublequote +\catcode`\~=\active +\def~{{\tt\char126}} +\chardef\hat=`\^ +\catcode`\^=\active +\def^{{\tt \hat}} + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +% Subroutine for the previous macro. +\def\_{\leavevmode \kern.06em \vbox{\hrule width.3em height.1ex}} + +\catcode`\|=\active +\def|{{\tt\char124}} +\chardef \less=`\< +\catcode`\<=\active +\def<{{\tt \less}} +\chardef \gtr=`\> +\catcode`\>=\active +\def>{{\tt \gtr}} +\catcode`\+=\active +\def+{{\tt \char 43}} +\catcode`\$=\active +\def${\ifusingit{{\sl\$}}\normaldollar} +%\catcode 27=\active +%\def^^[{$\diamondsuit$} + +% Set up an active definition for =, but don't enable it most of the time. +{\catcode`\==\active +\global\def={{\tt \char 61}}} + +\catcode`+=\active +\catcode`\_=\active + +% If a .fmt file is being used, characters that might appear in a file +% name cannot be active until we have parsed the command line. +% So turn them off again, and have \everyjob (or @setfilename) turn them on. +% \otherifyactive is called near the end of this file. +\def\otherifyactive{\catcode`+=\other \catcode`\_=\other} + +\catcode`\@=0 + +% \rawbackslashxx output one backslash character in current font +\global\chardef\rawbackslashxx=`\\ +%{\catcode`\\=\other +%@gdef@rawbackslashxx{\}} + +% \rawbackslash redefines \ as input to do \rawbackslashxx. +{\catcode`\\=\active +@gdef@rawbackslash{@let\=@rawbackslashxx }} + +% \normalbackslash outputs one backslash in fixed width font. +\def\normalbackslash{{\tt\rawbackslashxx}} + +% \catcode 17=0 % Define control-q +\catcode`\\=\active + +% Used sometimes to turn off (effectively) the active characters +% even after parsing them. +@def@turnoffactive{@let"=@normaldoublequote +@let\=@realbackslash +@let~=@normaltilde +@let^=@normalcaret +@let_=@normalunderscore +@let|=@normalverticalbar +@let<=@normalless +@let>=@normalgreater +@let+=@normalplus +@let$=@normaldollar} + +@def@normalturnoffactive{@let"=@normaldoublequote +@let\=@normalbackslash +@let~=@normaltilde +@let^=@normalcaret +@let_=@normalunderscore +@let|=@normalverticalbar +@let<=@normalless +@let>=@normalgreater +@let+=@normalplus +@let$=@normaldollar} + +% Make _ and + \other characters, temporarily. +% This is canceled by @fixbackslash. +@otherifyactive + +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% +@gdef@eatinput input texinfo{@fixbackslash} +@global@let\ = @eatinput + +% On the other hand, perhaps the file did not have a `\input texinfo'. Then +% the first `\{ in the file would cause an error. This macro tries to fix +% that, assuming it is called before the first `\' could plausibly occur. +% Also back turn on active characters that might appear in the input +% file name, in case not using a pre-dumped format. +% +@gdef@fixbackslash{% + @ifx\@eatinput @let\ = @normalbackslash @fi + @catcode`+=@active + @catcode`@_=@active +} + +% Say @foo, not \foo, in error messages. +@escapechar = `@@ + +% These look ok in all fonts, so just make them not special. +@catcode`@& = @other +@catcode`@# = @other +@catcode`@% = @other + +@c Set initial fonts. +@textfonts +@rm + + +@c Local variables: +@c eval: (add-hook 'write-file-hooks 'time-stamp) +@c page-delimiter: "^\\\\message" +@c time-stamp-start: "def\\\\texinfoversion{" +@c time-stamp-format: "%:y-%02m-%02d.%02H" +@c time-stamp-end: "}" +@c End: diff --git a/lbtt/src/Alloc.h b/lbtt/src/Alloc.h new file mode 100644 index 000000000..73fe13ef0 --- /dev/null +++ b/lbtt/src/Alloc.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef ALLOC_H +#define ALLOC_H + +#include + +#ifdef HAVE_SINGLE_CLIENT_ALLOC +#define ALLOC(typename) single_client_alloc +#else +#define ALLOC(typename) allocator +#endif /* HAVE_SINGLE_CLIENT_ALLOC */ + +#ifdef HAVE_OBSTACK_H + +#include +#include +#include + +/****************************************************************************** + * + * A wrapper class for allocating memory through an obstack. + * + *****************************************************************************/ + +class ObstackAllocator +{ +public: + ObstackAllocator(); /* Constructor. */ + + ~ObstackAllocator(); /* Destructor. */ + + void* alloc(int size); /* Allocates memory. */ + + void free(void* obj); /* Deallocates memory. */ + + static void failure(); /* Callback function for + * reporting a memory + * allocation failure. + */ +private: + ObstackAllocator(const ObstackAllocator&); /* Prevent copying and */ + ObstackAllocator& operator= /* assignment of */ + (const ObstackAllocator&); /* ObstackAllocator + * objects. + */ + + struct obstack store; /* The obstack. */ +}; + +#define obstack_chunk_alloc std::malloc +#define obstack_chunk_free std::free + + + +/****************************************************************************** + * + * Inline function definitions for class ObstackAllocator. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline ObstackAllocator::ObstackAllocator() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class ObstackAllocator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + obstack_init(&store); +} + +/* ========================================================================= */ +inline ObstackAllocator::~ObstackAllocator() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class ObstackAllocator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + obstack_free(&store, NULL); +} + +/* ========================================================================= */ +inline void* ObstackAllocator::alloc(int size) +/* ---------------------------------------------------------------------------- + * + * Description: Interface to the memory allocator. + * + * Argument: size -- Number of bytes to allocate. + * + * Returns: A pointer to the beginning of the newly allocated memory. + * + * ------------------------------------------------------------------------- */ +{ + return obstack_alloc(&store, size); +} + +/* ========================================================================= */ +inline void ObstackAllocator::free(void* obj) +/* ---------------------------------------------------------------------------- + * + * Description: Interface to the memory deallocation function. + * + * Argument: obj -- A pointer to the object to deallocate. (Because the + * underlying memory allocator is an obstack, freeing + * an object also releases all objects allocated after + * the given object.) + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + obstack_free(&store, obj); +} + +/* ========================================================================= */ +inline void ObstackAllocator::failure() +/* ---------------------------------------------------------------------------- + * + * Description: Callback function for reporting memory allocation failures. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + throw std::bad_alloc(); +} + +#endif /* HAVE_OBSTACK_H */ + +#endif /* !ALLOC_H */ diff --git a/lbtt/src/BitArray.cc b/lbtt/src/BitArray.cc new file mode 100644 index 000000000..f2086ab47 --- /dev/null +++ b/lbtt/src/BitArray.cc @@ -0,0 +1,457 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#ifdef HAVE_SSTREAM +#include +#else +#include +#endif /* HAVE_SSTREAM */ +#include "BitArray.h" +#include "Exception.h" + +/****************************************************************************** + * + * `bit_counts[i]' tells the number of 1-bits in the 8-bit integer `i'. + * + *****************************************************************************/ + +const unsigned char BitArray::bit_counts[] = + {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, + 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, + 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, + 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, + 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, + 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, + 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, + 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, + 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, + 6, 7, 6, 7, 7, 8}; + + + +/****************************************************************************** + * + * Function definitions for class BitArray. + * + *****************************************************************************/ + +/* ========================================================================= */ +BitArray::BitArray(const unsigned long int size) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class BitArray. Creates a bit array of a + * given size. + * + * Argument: size -- Number of bits to allocate. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + unsigned long int bsize = (size + 7) >> 3; + + bits = new unsigned char[bsize]; +} + +/* ========================================================================= */ +BitArray::~BitArray() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class BitArray. Deallocates the memory used by + * the bit array. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + delete[] bits; +} + +/* ========================================================================= */ +void BitArray::copy + (const BitArray& bitarray, const unsigned long int bit_count) +/* ---------------------------------------------------------------------------- + * + * Description: Copy the first `bit_count' bits from `bitarray' to `this' + * BitArray object. (As a side effect, the capacity of `this' + * bit array is set to `bit_count'.) + * + * Argument: bitarray -- Reference to a constant bit array which should + * be copied. + * bit_count -- Number of bits to copy. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (this != &bitarray) + { + delete[] bits; + + unsigned long int bsize = (bit_count + 7) >> 3; + + bits = new unsigned char[bsize]; + memcpy(static_cast(bits), static_cast(bitarray.bits), + bsize); + } +} + +/* ========================================================================= */ +bool BitArray::equal + (const BitArray& bitarray, const unsigned long int bit_count) const +/* ---------------------------------------------------------------------------- + * + * Description: Test whether the first `bit_count' bits of `this' BitArray + * are equal to the correspoding bits of `bitarray'. + * + * Argument: bitarray -- Target of the comparison. + * bit_count -- Number of bits to compare. + * + * Returns: `true' if the compared bits agree and `false' otherwise. + * + * ------------------------------------------------------------------------- */ +{ + if (bit_count > 0) + { + unsigned long int bsize = bit_count >> 3; + + for (unsigned long int i = 0; i < bsize; ++i) + { + if (bits[i] != bitarray.bits[i]) + return false; + } + + if ((bit_count & 0x07) == 0) + return true; + + unsigned char mask = (1 << (bit_count & 0x07)) - 1; + + if (((bits[bsize] ^ bitarray.bits[bsize]) & mask) != 0) + return false; + } + + return true; +} + +/* ========================================================================= */ +BitArray& BitArray::bitwiseOr + (const BitArray& bitarray, const unsigned long int bit_count) +/* ---------------------------------------------------------------------------- + * + * Description: Compute the bitwise disjunction of the first `bit_count' bits + * of two BitArrays (storing the result in `this' BitArray). + * + * Arguments: bitarray -- A reference to a constant BitArray. + * bit_count -- Number of bits to include in the computation. + * + * Returns: A reference to `this' BitArray. + * + * ------------------------------------------------------------------------- */ +{ + unsigned long int bsize = bit_count >> 3; + + for (unsigned long int i = 0; i < bsize; ++i) + bits[i] |= bitarray.bits[i]; + + if ((bit_count & 0x07) != 0) + bits[bsize] |= (bitarray.bits[bsize] & ((1 << (bit_count & 7)) - 1)); + + return *this; +} + +/* ========================================================================= */ +BitArray& BitArray::bitwiseAnd + (const BitArray& bitarray, const unsigned long int bit_count) +/* ---------------------------------------------------------------------------- + * + * Description: Compute the bitwise conjunction of the first `bit_count' bits + * of two BitArrays (storing the result in `this' BitArray). + * + * Arguments: bitarray -- A reference to a constant BitArray. + * bit_count -- Number of bits to include in the computation. + * + * Returns: A reference to `this' BitArray. + * + * ------------------------------------------------------------------------- */ +{ + unsigned long int bsize = bit_count >> 3; + + for (unsigned long int i = 0; i < bsize; ++i) + bits[i] &= bitarray.bits[i]; + + if ((bit_count & 0x07) != 0) + bits[bsize] &= (bitarray.bits[bsize] | ~((1 << (bit_count & 7)) - 1)); + + return *this; +} + +/* ========================================================================= */ +BitArray& BitArray::bitwiseXor + (const BitArray& bitarray, const unsigned long int bit_count) +/* ---------------------------------------------------------------------------- + * + * Description: Compute the bitwise "exclusive or" of the first `bit_count' + * bits of two BitArrays (storing the result in `this' + * BitArray). + * + * Arguments: bitarray -- A reference to a constant BitArray. + * bit_count -- Number of bits to include in the computation. + * + * Returns: A reference to `this' BitArray. + * + * ------------------------------------------------------------------------- */ +{ + unsigned long int bsize = bit_count >> 3; + + for (unsigned long int i = 0; i < bsize; ++i) + bits[i] ^= bitarray.bits[i]; + + if ((bit_count & 0x07) != 0) + bits[bsize] ^= (bitarray.bits[bsize] & ((1 << (bit_count & 7)) - 1)); + + return *this; +} + +/* ========================================================================= */ +bool BitArray::subset + (const BitArray& bitarray, const unsigned long int bit_count) const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether the first `bit_count' bits of `this' BitArray + * are set also in `bitarray'. + * + * Argument bitarray -- Target of the comparison. + * bit_count -- Number of bits to test. + * + * Returns: Truth value depending on the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + if (bit_count > 0) + { + unsigned long int bsize = bit_count >> 3; + + for (unsigned long int i = 0; i < bsize; ++i) + { + if ((bits[i] & ~bitarray.bits[i]) != 0) + return false; + } + + if ((bit_count & 0x07) == 0) + return true; + + unsigned char mask = (1 << (bit_count & 7)) - 1; + + if (((bits[bsize] & ~bitarray.bits[bsize]) & mask) != 0) + return false; + } + + return true; +} + +/* ========================================================================= */ +unsigned long int BitArray::count(const unsigned long int bit_count) const +/* ---------------------------------------------------------------------------- + * + * Description: Counts the number of 1-bits in the first `bit_count' bits of + * the BitArray. + * + * Arguments: bit_count -- Number of bits to include in the computation. + * + * Returns: Number of 1-bits in the first `bit_count' bits of the + * BitArray. + * + * ------------------------------------------------------------------------- */ +{ + if (bit_count == 0) + return 0; + + unsigned long int bsize = bit_count >> 3; + unsigned long int result = 0; + + for (unsigned long int i = 0; i < bsize; ++i) + result += bit_counts[bits[i]]; + + if ((bit_count & 0x07) == 0) + return result; + + unsigned char mask = (1 << (bit_count & 7)) - 1; + + result += bit_counts[bits[bsize] & mask]; + + return result; +} + +/* ========================================================================= */ +unsigned long int BitArray::hammingDistance + (const BitArray& bitarray, const unsigned long int bit_count) const +/* ---------------------------------------------------------------------------- + * + * Description: Computes the Hamming distance (the number of bit positions in + * which two bit vectors differ) between two bit vectors + * comprising the first `bit_count' bits of two bit arrays. + * + * Argument: bitarray -- A reference to a constant BitArray. + * bit_count -- Number of bits to include in the computation. + * + * Returns: The Hamming distance between the leftmost `bit_count' bits of + * two BitArrays. + * + * ------------------------------------------------------------------------- */ +{ + unsigned long int result = 0; + unsigned long int bsize = bit_count >> 3; + + for (unsigned long int i = 0; i < bsize; ++i) + result += bit_counts[bits[i] ^ bitarray.bits[i]]; + + if ((bit_count & 0x07) == 0) + return result; + + unsigned char mask = (1 << (bit_count & 7)) - 1; + + result += bit_counts[(bits[bsize] ^ bitarray.bits[bsize]) & mask]; + + return result; +} + +/* ========================================================================= */ +void BitArray::flip(const unsigned long int bit_count) +/* ---------------------------------------------------------------------------- + * + * Description: Changes the state of the first `bit_count' bits in the + * bit array. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + unsigned long bsize = (bit_count + 7) >> 3; + + for (unsigned long int i = 0; i < bsize; ++i) + bits[i] ^= 0xFF; +} + +/* ========================================================================= */ +unsigned long int BitArray::find(const unsigned long int max_count) const +/* ---------------------------------------------------------------------------- + * + * Description: Finds the first 1-bit in the bit array with an index less + * than `max_count'. + * + * Arguments: max_count -- Upper bound for the bit index. + * + * Returns: Index of the first 1-bit with an index less than `max_count' + * or `max_count' if there are no 1-bits with such an index in + * the array. + * + * ------------------------------------------------------------------------- */ +{ + unsigned long int bsize = (max_count + 7) >> 3; + unsigned long int i; + for (i = 0; i < bsize && bits[i] == 0; ++i) + ; + + if (i == max_count) + return max_count; + + unsigned char c = bits[i]; + i <<= 3; + + while ((c & 0x01) == 0 && i < max_count) + { + c >>= 1; + ++i; + } + + return i; +} + +/* ========================================================================= */ +string BitArray::toString(const unsigned long int bit_count) const +/* ---------------------------------------------------------------------------- + * + * Description: Converts the first `bit_count' bits of the bit array to a + * string (a sequence of characters `0' and `1'). The leftmost + * bit in the string corresponds to the first bit in the array. + * + * Arguments: bit_count -- Number of bits to convert into a string. + * + * Returns: String representation of the first `bit_count' bits of the + * bit array. + * + * ------------------------------------------------------------------------- */ +{ +#ifdef HAVE_SSTREAM + ostringstream bitstring; + print(bit_count, bitstring); + return bitstring.str(); +#else + ostrstream bitstring; + print(bit_count, bitstring); + bitstring << ends; + string result(bitstring.str()); + bitstring.freeze(0); + return result; +#endif /* HAVE_SSTREAM */ +} + +/* ========================================================================= */ +void BitArray::print(const unsigned long int bit_count, ostream& stream) const +/* ---------------------------------------------------------------------------- + * + * Description: Prints the first `bit_count' bits of the bit array as a + * sequence of characters `0' and `1'. The bits are printed in + * the same order as they are in the array; thus, the first + * printed bit corresponds to the bit with the lowest index in + * the array. + * + * Arguments: bit_count -- Number of bits to print. + * stream -- A reference to the output stream. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + unsigned long int i, j, bsize = (bit_count + 7) >> 3; + unsigned short int maskbit; + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + j = 0; + for (i = 0; i < bsize; ++i) + { + maskbit = 1; + while (j < bit_count && maskbit <= 0x80) + { + estream << ((bits[i] & maskbit) ? '1' : '0'); + ++j; + maskbit <<= 1; + } + } +} diff --git a/lbtt/src/BitArray.h b/lbtt/src/BitArray.h new file mode 100644 index 000000000..d8ef12b96 --- /dev/null +++ b/lbtt/src/BitArray.h @@ -0,0 +1,430 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef BITARRAY_H +#define BITARRAY_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include + +using namespace std; + +class Bitset; + +/****************************************************************************** + * + * A class for arrays of bits. + * + *****************************************************************************/ + +class BitArray +{ +public: + explicit BitArray /* Constructor. */ + (const unsigned long int size = 0); + + ~BitArray(); /* Destructor. */ + +private: + BitArray(const BitArray& bitarray); /* Prevent copying and */ + BitArray& operator=(const BitArray& bitarray); /* assignment of + * BitArray objects. + * (Copying and + * assignment requires + * information about + * the number of bits in + * the bit array; this + * information is not + * included in the + * object itself.) + */ + +public: + bool operator[](const unsigned long int index) /* Tell the value of a */ + const; /* bit in a given array */ + bool test(const unsigned long int index) const; /* index. + */ + + + void copy /* Duplicate a bit */ + (const BitArray& bitarray, /* array. */ + const unsigned long int bit_count); + + void copy(const Bitset& bitset); /* Copy bits from a */ + BitArray& operator=(const Bitset& bitset); /* Bitset. */ + + bool equal /* Test whether the */ + (const BitArray& bitarray, /* first `bit_count' */ + const unsigned long int bit_count) const; /* bits of two BitArrays + * are equal. + */ + + bool equal(const Bitset& bitset) const; /* Compare a BitArray to */ + bool operator==(const Bitset& bitset) const; /* a Bitset. */ + + BitArray& bitwiseOr /* Compute the bitwise */ + (const BitArray& bitarray, /* disjunction of the */ + const unsigned long int bit_count); /* first `bit_count' + * bits of two + * BitArrays. + */ + + BitArray& bitwiseAnd /* Compute the bitwise */ + (const BitArray& bitarray, /* conjunction of the */ + const unsigned long int bit_count); /* first `bit_count' + * bits of two + * BitArrays. + */ + + BitArray& bitwiseXor /* Compute the bitwise */ + (const BitArray& bitarray, /* "exclusive or" of the */ + const unsigned long int bit_count); /* first `bit_count' + * bits of two + * BitArrays. + */ + + bool subset /* Test whether the */ + (const BitArray& bitarray, /* first `bit_count' */ + const unsigned long int bit_count) const; /* bits of `this' + * BitArray are included + * in another BitArray. + */ + + bool subset(const Bitset& bitset) const; /* Test for inclusion */ + bool operator<=(const Bitset& bitset) const; /* into a Bitset. */ + + unsigned long int count /* Compute the number of */ + (const unsigned long int bit_count) const; /* 1-bits in the first + * `bit_count' bits of + * the BitArray. + */ + + unsigned long int hammingDistance /* Compute the Hamming */ + (const BitArray& bitarray, /* distance between two */ + const unsigned long int bit_count) const; /* bit vectors + * consisting of the + * first `bit_count' + * bits of two + * BitArrays. + */ + + unsigned long int hammingDistance /* Compute the Hamming */ + (const Bitset& bitset) const; /* distance between a + * BitArray and a + * Bitset. + */ + + void set(const unsigned long int bit_count); /* Set a given number of + * bits in the bit array. + */ + + void setBit(const unsigned long int index); /* Set a single bit in the + * bit array. + */ + + void clear(const unsigned long int bit_count); /* Clear the first + * `bit_count' bits in the + * bit array. + */ + + void clearBit(const unsigned long int index); /* Clear a single bit in + * the bit array. + */ + + void flip(const unsigned long int bit_count); /* Flip the first + * `bit_count' bits in the + * bit array. + */ + + void flipBit(const unsigned long int index); /* Flip a single bit in the + * bit array. + */ + + unsigned long int find /* Return the index of */ + (const unsigned long int max_count) const; /* the first 1-bit in + * the bit array with a + * bit index less than + * `max_count'. + */ + + void print /* Print the first */ + (const unsigned long int bit_count, /* `bit_count' bits of */ + ostream& stream = cout) const; /* the bit array. */ + + string toString /* Convert a prefix of */ + (const unsigned long int bit_count) const; /* a BitArray into a + * string of 0's and + * 1's. + */ + +private: + unsigned char* bits; /* Storage for the bits. */ + + static const unsigned char bit_counts[]; /* `bit_counts[i]' gives + * the number of bits in + * the 8-bit integer `i'. + * (Used by the member + * function `count'.) + */ + + friend class Bitset; +}; + + + +#include "Bitset.h" + + + +/****************************************************************************** + * + * Inline function definitions for class BitArray. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool BitArray::operator[](const unsigned long int index) const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the state of a bit in a bit array. + * + * Argument: index -- Index of the bit to test. + * + * Returns: State of the bit in the bit array. + * + * ------------------------------------------------------------------------- */ +{ + return ((bits[index >> 3] & (1 << (index & 7))) != 0); +} + +/* ========================================================================= */ +inline void BitArray::copy(const Bitset& bitset) +/* ---------------------------------------------------------------------------- + * + * Description: Initializes the BitArray with bits in a Bitset. + * + * Argument: bitset -- Bitset to copy. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + return copy(bitset, bitset.capacity()); +} + +/* ========================================================================= */ +inline BitArray& BitArray::operator=(const Bitset& bitset) +/* ---------------------------------------------------------------------------- + * + * Description: Initializes the BitArray with bits in a Bitset. + * + * Argument: bitset -- Bitset to copy. + * + * Returns: A reference to `this' BitArray. + * + * ------------------------------------------------------------------------- */ +{ + copy(bitset, bitset.capacity()); + return *this; +} + +/* ========================================================================= */ +inline bool BitArray::equal(const Bitset& bitset) const +/* ---------------------------------------------------------------------------- + * + * Description: Compares the `bitset.capacity()' first bits of the BitArray + * to `bitset'. The BitArray is assumed to be at least as large + * as the Bitset. + * + * Argument: bitset -- Target of the comparison. + * + * Returns: Truth value based on the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + return equal(bitset, bitset.capacity()); +} + +/* ========================================================================= */ +inline bool BitArray::operator==(const Bitset& bitset) const +/* ---------------------------------------------------------------------------- + * + * Description: Compares the `bitset.capacity()' first bits of the BitArray + * to `bitset'. The BitArray is assumed to be at least as large + * as the Bitset. + * + * Argument: bitset -- Target of the comparison. + * + * Returns: Truth value according to the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + return equal(bitset, bitset.capacity()); +} + +/* ========================================================================= */ +inline bool BitArray::subset(const Bitset& bitset) const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether all of the first `bitset.capacity()' bits of + * the BitArray are included in `bitset'. The BitArray should + * be at least as large as the Bitset. + * + * Argument: bitset -- Target of the comparison. + * + * Returns: Truth value according to the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + return subset(bitset, bitset.capacity()); +} + +/* ========================================================================= */ +inline bool BitArray::operator<=(const Bitset& bitset) const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether all of the first `bitset.capacity()' bits of + * the BitArray are included in `bitset'. The BitArray should be + * at least as large as the Bitset. + * + * Argument: bitset -- Target of the comparison. + * + * Returns: Truth value according to the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + return subset(bitset, bitset.capacity()); +} + +/* ========================================================================= */ +inline unsigned long int BitArray::hammingDistance(const Bitset& bitset) const +/* ---------------------------------------------------------------------------- + * + * Description: Computer the Hamming distance (number of differing bits) + * between the first `bitset.capacity()' bits of `bitset' and + * the BitArray. + * + * Argument: bitset -- Target of the computation. + * + * Returns: The Hamming distance between `bitset' and the BitArray. + * + * ------------------------------------------------------------------------- */ +{ + return hammingDistance(bitset, bitset.capacity()); +} + +/* ========================================================================= */ +inline bool BitArray::test(const unsigned long int index) const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the state of a bit in a bit array. + * + * Argument: index -- Index of bit to test. + * + * Returns: Truth value according to the state of the bit. + * + * ------------------------------------------------------------------------- */ +{ + return operator[](index); +} + +/* ========================================================================= */ +inline void BitArray::set(const unsigned long int bit_count) +/* ---------------------------------------------------------------------------- + * + * Description: Fills the first `bit_count' bits of the bit array with + * 1-bits. + * + * Arguments: bit_count -- Number of bits to set to 1. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + memset(static_cast(bits), 0xFF, (bit_count + 7) >> 3); +} + +/* ========================================================================= */ +inline void BitArray::setBit(const unsigned long int index) +/* ---------------------------------------------------------------------------- + * + * Description: Sets a single bit in the bit array. + * + * Argument: index -- Index of the bit to be set. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + bits[index >> 3] |= 1 << (index & 7); +} + +/* ========================================================================= */ +inline void BitArray::clear(const unsigned long int bit_count) +/* ---------------------------------------------------------------------------- + * + * Description: Fills the first `bit_count' bits of the bit array with + * 0-bits. + * + * Arguments: bit_count -- Number of bits to set to 0. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + memset(static_cast(bits), 0, (bit_count + 7) >> 3); +} + +/* ========================================================================= */ +inline void BitArray::clearBit(const unsigned long int index) +/* ---------------------------------------------------------------------------- + * + * Description: Clears a single bit in the bit array. + * + * Argument: index -- Index of the bit to be cleared. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + bits[index >> 3] &= ~(1 << (index & 7)); +} + +/* ========================================================================= */ +inline void BitArray::flipBit(const unsigned long int index) +/* ---------------------------------------------------------------------------- + * + * Description: Switches the state of a single bit in the bit array. + * + * Arguments: index -- Index of the bit. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + bits[index >> 3] ^= (1 << (index & 7)); +} + +#endif /* !BITARRAY_H */ diff --git a/lbtt/src/Bitset.h b/lbtt/src/Bitset.h new file mode 100644 index 000000000..82f87667c --- /dev/null +++ b/lbtt/src/Bitset.h @@ -0,0 +1,626 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef BITSET_H +#define BITSET_H + +#include +#include +#include +#include "Exception.h" + +using namespace std; + +/****************************************************************************** + * + * A class for sets of bits. + * + *****************************************************************************/ + +class Bitset +{ +public: + explicit Bitset /* Creates an empty bit */ + (const unsigned long int capacity = 0); /* set. */ + + explicit Bitset /* Creates a bit set */ + (const BitArray& bitarray, /* from a bit array. */ + const unsigned long int capacity); + + Bitset(const Bitset& bitset); /* Copy constructor. */ + + ~Bitset(); /* Destructor. */ + + operator const BitArray&() const; /* A Bitset can always be + * cast into a constant + * BitArray. + */ + + Bitset& operator=(const Bitset& bitset); /* Assignment operator. */ + + bool operator[](const unsigned long int index) /* Tell the value of a */ + const; /* bit in a given index. */ + bool test(const unsigned long int index) const; + + bool operator==(const Bitset& bitset) const; /* Test whether two */ + bool equal(const Bitset& bitset) const; /* Bitsets are equal. */ + + bool operator<=(const Bitset& bitset) const; /* Test whether `this' */ + bool subset(const Bitset& bitset) const; /* Bitset is a subset + * of another Bitset. + */ + + unsigned long int capacity() const; /* Tells the capacity of + * the bit set. + */ + + unsigned long int count() const; /* Compute the number of + * 1-bits in the Bitset. + */ + + unsigned long int hammingDistance /* Compute the Hamming */ + (const Bitset& bitset) const; /* distance between two + * Bitsets. + */ + + void set(); /* Set all bits in the bit + * set. + */ + + void setBit(const unsigned long int index); /* Set a single bit in the + * bit set. + */ + + void clear(); /* Clear all bits in the + * bit set. + */ + + void clearBit(const unsigned long int index); /* Clear a single bit in + * the bit set. + */ + + void flip(); /* Flip all bits in the + * bit set. + */ + + void flipBit(const unsigned long int index); /* Flip a single bit in the + * bit set. + */ + + unsigned long int find() const; /* Return the index of the + * first 1-bit in the bit + * set. + */ + + void print(ostream& stream = cout) const; /* Print the bit set. */ + + string toString() const; /* Convert the Bitset into + * a string if 0's and 1's. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class BitIndexException; /* An exception class for + * reporting indexing + * errors. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +private: + unsigned long int number_of_bits; /* Capacity of the bit + * set. + */ + + BitArray bits; /* Storage for the bits. */ +}; + + + +/****************************************************************************** + * + * An exception class for reporting bitset indexing errors. + * + *****************************************************************************/ + +class Bitset::BitIndexException : public Exception +{ +public: + BitIndexException(); /* Constructor. */ + + /* default copy constructor */ + + ~BitIndexException() throw(); /* Destructor. */ + + BitIndexException& /* Assignment operator. */ + operator=(const BitIndexException& e); + + /* `what' inherited from class Exception */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class Bitset. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline Bitset::Bitset(const unsigned long int capacity) : + number_of_bits(capacity), bits(capacity) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Bitset. + * + * Argument: capacity -- Number of bits to allocate. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Bitset::Bitset + (const BitArray& bitarray, const unsigned long int capacity) : + number_of_bits(capacity), bits(capacity) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Bitset. Creates a Bitset from the + * first `capacity' bits of a BitArray. + * + * Arguments: bitarray -- A reference to a constant BitArray. + * capacity -- Capacity of the new bit set. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + memcpy(static_cast(bits.bits), + static_cast(bitarray.bits), + (capacity + 7) >> 3); +} + +/* ========================================================================= */ +inline Bitset::Bitset(const Bitset& bitset) : + number_of_bits(bitset.number_of_bits), bits(bitset.number_of_bits) +/* ---------------------------------------------------------------------------- + * + * Description: Copy constructor for class Bitset. + * + * Argument: bitset -- Bitset to be copied. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + memcpy(static_cast(bits.bits), + static_cast(bitset.bits.bits), + (number_of_bits + 7) >> 3); +} + +/* ========================================================================= */ +inline Bitset::~Bitset() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Bitset. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Bitset::operator const BitArray&() const +/* ---------------------------------------------------------------------------- + * + * Description: Converts a Bitset into a BitArray. + * + * Arguments: None. + * + * Returns: The bits associated with the Bitset object as a reference to + * a constant BitArray. + * + * ------------------------------------------------------------------------- */ +{ + return bits; +} + +/* ========================================================================= */ +inline Bitset& Bitset::operator=(const Bitset& bitset) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class Bitset. + * + * Arguments: bitset -- A reference to a Bitset to be assigned to `this' + * Bitset. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (&bitset != this) + { + if ((number_of_bits >> 3) != (bitset.number_of_bits >> 3)) + bits.copy(bitset.bits, number_of_bits); + else + memcpy(static_cast(bits.bits), + static_cast(bitset.bits.bits), + (bitset.number_of_bits + 7) >> 3); + + number_of_bits = bitset.number_of_bits; + } + return *this; +} + +/* ========================================================================= */ +inline bool Bitset::operator[](const unsigned long int index) const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the state of a bit in a bit set. + * + * Argument: index -- Index of the bit to test. + * + * Returns: Truth value according to the state of the bit. + * + * ------------------------------------------------------------------------- */ +{ + if (index >= number_of_bits) + throw BitIndexException(); + + return bits[index]; +} + +/* ========================================================================= */ +inline bool Bitset::test(const unsigned long int index) const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the state of a bit in a bit set. + * + * Argument: index -- Index of bit to test. + * + * Returns: Truth value according to the state of the bit. + * + * ------------------------------------------------------------------------- */ +{ + return operator[](index); +} + +/* ========================================================================= */ +inline bool Bitset::operator==(const Bitset& bitset) const +/* ---------------------------------------------------------------------------- + * + * Description: Compares two bitsets for equality. + * + * Argument: bitset -- Target of the comparison. + * + * Returns: `true' if the bit sets have the same capacity and contents. + * + * ------------------------------------------------------------------------- */ +{ + return (number_of_bits == bitset.number_of_bits + && bits.equal(bitset.bits, number_of_bits)); +} + +/* ========================================================================= */ +inline bool Bitset::equal(const Bitset& bitset) const +/* ---------------------------------------------------------------------------- + * + * Description: Compares two bitsets for equality. + * + * Argument: bitset -- Target of the comparison. + * + * Returns: `true' if the bit sets have the same capacity and contents. + * + * ------------------------------------------------------------------------- */ +{ + return operator==(bitset); +} + +/* ========================================================================= */ +inline bool Bitset::operator<=(const Bitset& bitset) const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether `this' bitset is a subset of another Bitset. + * + * Argument: bitset -- Target of the comparison. + * + * Returns: `true' if the capacity of `this' bitset is at most equal to + * the capacity of the target bitset and all bits set in `this' + * Bitset are also set in the other set. + * + * ------------------------------------------------------------------------- */ +{ + return (number_of_bits <= bitset.number_of_bits + && bits.subset(bitset.bits, number_of_bits)); +} + +/* ========================================================================= */ +inline bool Bitset::subset(const Bitset& bitset) const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether `this' bitset is a subset of another Bitset. + * + * Argument: bitset -- Target of the comparison. + * + * Returns: `true' if the capacity of `this' bitset is at most equal to + * the capacity of the target bitset and all bits set in `this' + * Bitset are also set in the other set. + * + * ------------------------------------------------------------------------- */ +{ + return operator<=(bitset); +} + +/* ========================================================================= */ +inline unsigned long int Bitset::capacity() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the capacity of the bit set. + * + * Arguments: None. + * + * Returns: Capacity of the bit set. + * + * ------------------------------------------------------------------------- */ +{ + return number_of_bits; +} + +/* ========================================================================= */ +inline unsigned long int Bitset::count() const +/* ---------------------------------------------------------------------------- + * + * Description: Computes the number of 1-bits in the Bitset. + * + * Arguments: None. + * + * Returns: Number of 1-bits in the Bitset. + * + * ------------------------------------------------------------------------- */ +{ + return bits.count(number_of_bits); +} + +/* ========================================================================= */ +inline unsigned long int Bitset::hammingDistance(const Bitset& bitset) const +/* ---------------------------------------------------------------------------- + * + * Description: Computes the Hamming distance between two Bitsets (or between + * maximal prefixes of both Bitsets if the sets are of + * different capacity). + * + * Argument: bitset -- A reference to a constant Bitset. + * + * Returns: The Hamming distance between the bitsets (or their maximal + * prefixes of equal length). + * + * ------------------------------------------------------------------------- */ +{ + return bits.hammingDistance(bitset.bits, + number_of_bits <= bitset.number_of_bits + ? number_of_bits + : bitset.number_of_bits); +} + +/* ========================================================================= */ +inline void Bitset::set() +/* ---------------------------------------------------------------------------- + * + * Description: Fills the bit set with 1-bits. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + bits.set(number_of_bits); +} + +/* ========================================================================= */ +inline void Bitset::setBit(const unsigned long int index) +/* ---------------------------------------------------------------------------- + * + * Description: Sets a single bit in the bit set. + * + * Argument: index -- Index of the bit to be set. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (index >= number_of_bits) + throw BitIndexException(); + + bits.setBit(index); +} + +/* ========================================================================= */ +inline void Bitset::clear() +/* ---------------------------------------------------------------------------- + * + * Description: Fills the bit set with 0-bits. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + bits.clear(number_of_bits); +} + +/* ========================================================================= */ +inline void Bitset::clearBit(const unsigned long int index) +/* ---------------------------------------------------------------------------- + * + * Description: Clears a single bit in the bit set. + * + * Argument: index -- Index of the bit to be cleared. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (index >= number_of_bits) + throw BitIndexException(); + + bits.clearBit(index); +} + +/* ========================================================================= */ +inline void Bitset::flip() +/* ---------------------------------------------------------------------------- + * + * Description: Flips all bits in the bit set. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + bits.flip(number_of_bits); +} + +/* ========================================================================= */ +inline void Bitset::flipBit(const unsigned long int index) +/* ---------------------------------------------------------------------------- + * + * Description: Switches the state of a single bit in the bit set. + * + * Arguments: index -- Index of the bit. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (index >= number_of_bits) + throw BitIndexException(); + + bits.flipBit(index); +} + +/* ========================================================================= */ +inline unsigned long int Bitset::find() const +/* ---------------------------------------------------------------------------- + * + * Description: Finds the index of the first 1-bit in the bit set. + * + * Arguments: None. + * + * Returns: Index of the first 1-bit in the bit set (or + * `this->number_of_bits' if the bit set is empty). + * + * ------------------------------------------------------------------------- */ +{ + return bits.find(number_of_bits); +} + +/* ========================================================================= */ +inline void Bitset::print(ostream& stream) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes the Bitset into a stream. + * + * Argument: stream -- A reference to an output stream. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + bits.print(number_of_bits, stream); +} + +/* ========================================================================= */ +inline string Bitset::toString() const +/* ---------------------------------------------------------------------------- + * + * Description: Converts the Bitset into a string. + * + * Arguments: None. + * + * Returns: A string representation of the Bitset. + * + * ------------------------------------------------------------------------- */ +{ + return bits.toString(number_of_bits); +} + + + +/****************************************************************************** + * + * Inline function definitions for class Bitset::BitIndexException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline Bitset::BitIndexException::BitIndexException() : + Exception("Index out of range.") +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Bitset::BitIndexException. Creates a + * new exception object. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Bitset::BitIndexException::~BitIndexException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Bitset::BitIndexException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Bitset::BitIndexException& Bitset::BitIndexException::operator= + (const Bitset::BitIndexException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class Bitset::BitIndexException. + * + * Arguments: e -- A reference to another Bitset::BitIndexException. + * + * Returns: A reference to the assigned exception object. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + +#endif /* !BITSET_H */ diff --git a/lbtt/src/BuchiAutomaton.cc b/lbtt/src/BuchiAutomaton.cc new file mode 100644 index 000000000..7be826c8e --- /dev/null +++ b/lbtt/src/BuchiAutomaton.cc @@ -0,0 +1,1084 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include +#include "BuchiAutomaton.h" +#include "StringUtil.h" + +namespace Graph +{ + +/****************************************************************************** + * + * Function definitions for class BuchiAutomaton. + * + *****************************************************************************/ + +/* ========================================================================= */ +BuchiAutomaton::BuchiAutomaton + (const size_type initial_number_of_states, const size_type initstate, + const unsigned long int number_of_accept_sets) : + initial_state(initstate), number_of_acceptance_sets(number_of_accept_sets) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class BuchiAutomaton. Initializes an + * automaton with a given number of states and a given + * initial state. + * + * Arguments: initial_number_of_states -- Initial size of the automaton + * (can be grown later). + * initstate -- Index of the automaton's + * initial state. + * number_of_accept_sets -- Number of acceptance sets in + * the automaton. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (initstate >= initial_number_of_states + && !(initial_number_of_states == 0 && initstate == 0)) + throw NodeIndexException(); + + nodes.reserve(initial_number_of_states); + + for (size_type i = 0; i < initial_number_of_states; ++i) + nodes.push_back(new BuchiState(number_of_accept_sets)); +} + +/* ========================================================================= */ +BuchiAutomaton::BuchiAutomaton(const BuchiAutomaton& automaton) : + Graph(), + initial_state(automaton.initial_state), + number_of_acceptance_sets(automaton.number_of_acceptance_sets) +/* ---------------------------------------------------------------------------- + * + * Description: Copy constructor for class BuchiAutomaton. Creates a copy of + * a BuchiAutomaton object. + * + * Argument: automaton -- Automaton to be copied. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + expand(automaton.size()); + + for (size_type state = 0; state < size(); ++state) + { + for (GraphEdgeContainer::const_iterator transition + = automaton[state].edges().begin(); + transition != automaton[state].edges().end(); + ++transition) + connect(state, + static_cast(*transition)->targetNode(), + static_cast(*transition)->guard()); + + operator[](state).acceptanceSets().copy(automaton[state].acceptanceSets(), + number_of_acceptance_sets); + } +} + +/* ========================================================================= */ +BuchiAutomaton& BuchiAutomaton::operator=(const BuchiAutomaton& automaton) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class BuchiAutomaton. Assigns the + * contents of a BuchiAutomaton to another one. + * + * Argument: automaton -- A constant reference to a BuchiAutomaton whose + * contents are to be copied. + * + * Returns: A reference to the BuchiAutomaton assigned to. + * + * ------------------------------------------------------------------------- */ +{ + if (&automaton != this) + { + initial_state = automaton.initial_state; + number_of_acceptance_sets = automaton.number_of_acceptance_sets; + + clear(); + expand(automaton.size()); + + for (size_type state = 0; state < size(); ++state) + { + for (GraphEdgeContainer::const_iterator transition + = automaton[state].edges().begin(); + transition != automaton[state].edges().end(); + ++transition) + connect(state, + static_cast(*transition)->targetNode(), + static_cast(*transition)->guard()); + + operator[](state).acceptanceSets().copy + (automaton[state].acceptanceSets(), number_of_acceptance_sets); + } + } + + return *this; +} + +/* ========================================================================= */ +void BuchiAutomaton::clear() +/* ---------------------------------------------------------------------------- + * + * Description: Makes the automaton empty. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Graph::clear(); + initial_state = 0; + number_of_acceptance_sets = 0; +} + +/* ========================================================================= */ +BuchiAutomaton::size_type BuchiAutomaton::expand(size_type node_count) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a given number of states to a BuchiAutomaton. + * + * Argument: node_count -- Number of states to be inserted. + * + * Returns: The index of the last inserted state. + * + * ------------------------------------------------------------------------- */ +{ + nodes.reserve(nodes.size() + node_count); + + BuchiState* new_buchi_state; + + while (node_count > 0) + { + new_buchi_state = new BuchiState(number_of_acceptance_sets); + try + { + nodes.push_back(new_buchi_state); + } + catch (...) + { + delete new_buchi_state; + throw; + } + node_count--; + } + + return nodes.size() - 1; +} + +/* ========================================================================= */ +BuchiAutomaton* BuchiAutomaton::regularize() const +/* ---------------------------------------------------------------------------- + * + * Description: Converts a generalized Büchi automaton (i.e., an automaton + * with any number of accepting state sets) into an automaton + * with only one set of accepting states. + * + * Arguments: None. + * + * Returns: A pointer to an equivalent BuchiAutomaton with exactly one + * set of accepting states. + * + * ------------------------------------------------------------------------- */ +{ + /* + * If `this' automaton already has exactly one set of accepting states, + * return a copy of `this' automaton. + */ + + if (number_of_acceptance_sets == 1) + return new BuchiAutomaton(*this); + + /* + * Otherwise construct the result using a depth-first search in `this' + * automaton. + */ + + typedef pair ExpandedState; + + BuchiAutomaton* result_automaton = new BuchiAutomaton(0, 0, 1); + + if (empty()) + return result_automaton; + + stack > + states_to_process; + + map, ALLOC(size_type) > + state_mapping; + + const GraphEdgeContainer* transitions; + + size_type result_source_state, result_target_state; + map, ALLOC(size_type) > + ::const_iterator check_state; + + ExpandedState state = make_pair(initial_state, 0); + + states_to_process.push(state); + state_mapping[state] = result_automaton->expand(); + + while (!states_to_process.empty()) + { + state = states_to_process.top(); + states_to_process.pop(); + + result_source_state = state_mapping[state]; + transitions = &operator[](state.first).edges(); + + if (number_of_acceptance_sets == 0 + || operator[](state.first).acceptanceSets().test(state.second)) + { + if (state.second == 0) + (*result_automaton)[result_source_state].acceptanceSets().setBit(0); + + if (number_of_acceptance_sets > 0) + { + ++state.second; + state.second %= number_of_acceptance_sets; + } + } + + for (GraphEdgeContainer::const_iterator transition = transitions->begin(); + transition != transitions->end(); + ++transition) + { + state.first = (*transition)->targetNode(); + + check_state = state_mapping.find(state); + + if (check_state == state_mapping.end()) + { + result_target_state = result_automaton->expand(); + state_mapping[state] = result_target_state; + states_to_process.push(state); + } + else + result_target_state = check_state->second; + + result_automaton->connect(result_source_state, result_target_state, + static_cast + (*transition)->guard()); + } + } + + return result_automaton; +} + +/* ========================================================================= */ +void BuchiAutomaton::read(istream& input_stream) +/* ---------------------------------------------------------------------------- + * + * Description: Reads an automaton description (which may represent a + * generalized Büchi automaton) from a stream and stores it + * into the automaton object, converting it to a regular + * Büchi automaton if necessary. + * + * Argument: input_stream -- A reference to an input stream. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + size_type number_of_states, current_state, neighbor_state; + unsigned long int acceptance_set; + Exceptional_istream einput_stream(&input_stream, ios::failbit | ios::badbit); + + clear(); + + try + { + /* + * Read in the number of states in the generalized Büchi automaton. + */ + + einput_stream >> number_of_states; + + /* + * If the automaton is empty, do nothing. + */ + + if (number_of_states == 0) + return; + + einput_stream >> number_of_acceptance_sets; + + /* + * Allocate space for the regular Büchi automaton that will be constructed + * from the generalized Büchi automaton. + */ + + nodes.reserve(number_of_states); + for (size_type i = 0; i < number_of_states; ++i) + nodes.push_back(new BuchiState(number_of_acceptance_sets)); + + /* + * The automaton state numbers will be mapped from input file identifiers + * to the interval [0...(number of states - 1)]. + */ + + map, ALLOC(size_type) > + state_number_map; + pair state_mapping(0, 0); + + pair, ALLOC(size_type) > + ::const_iterator, + bool> + state_finder; + + /* + * Also the acceptance set numbers will be mapped to the interval + * [0...(number of acceptance sets - 1)]. + */ + + map, ALLOC(unsigned long int) > + acceptance_set_map; + pair acceptance_set_mapping(0, 0); + + pair, + ALLOC(unsigned long int) >::const_iterator, + bool> + acceptance_set_finder; + + /* + * A bit array is used to keep information about states which have been + * processed. (This is used to verify that each state is defined exactly + * once in the input.) + */ + + BitArray processed_states(number_of_states); + processed_states.clear(number_of_states); + unsigned long int number_of_processed_states = 0; + + bool initial_state_fixed = false; /* will be set to true after */ + /* processing the initial state */ + /* of the automaton */ + + int is_initial; + + ::Ltl::LtlFormula* guard; + + for (size_type i = 0; i < number_of_states; ++i) + { + /* + * Begin processing a new state by reading a state id from the stream. + */ + + einput_stream >> state_mapping.first; + + /* + * Try to insert a new state mapping into the state identifier map. + * If an insertion actually occurs (there is no element in the map with + * key equal to `state_mapping.first'), increment the state counter. In + * any case, set the value of `current_state' to the map value. + * (Note: It is an error to redefine a state that has already been + * processed.) + */ + + state_finder = state_number_map.insert(state_mapping); + + if (!state_finder.second) /* no insertion occurred */ + { + current_state = (state_finder.first)->second; + if (processed_states[current_state]) + throw AutomatonParseException("state redefinition encountered"); + } + else + { + if (state_mapping.second >= number_of_states) + throw AutomatonParseException("number of different state " + "identifiers does not match the size" + " of the Büchi automaton"); + + current_state = state_mapping.second; + state_mapping.second++; + } + + /* + * Check whether the current state is an initial state. (There must be + * exactly one initial state.) + */ + + einput_stream >> is_initial; + + if (is_initial != 0) + { + if (!initial_state_fixed) + { + initial_state = current_state; + initial_state_fixed = true; + } + else + throw AutomatonParseException("multiple initial state definitions"); + } + + /* + * Determine which acceptance sets the current state belongs to. + * The numbers of the acceptance sets are mapped to the proper + * interval, again by introducing mappings for acceptance set numbers + * whenever necessary. + */ + + operator[](current_state).acceptanceSets().clear + (number_of_acceptance_sets); + + while (1) + { + einput_stream >> acceptance_set_mapping.first; + + if (acceptance_set_mapping.first == -1) + break; + + acceptance_set_finder = + acceptance_set_map.insert(acceptance_set_mapping); + + if (!acceptance_set_finder.second) + acceptance_set = (acceptance_set_finder.first)->second; + else + { + if (acceptance_set_mapping.second >= number_of_acceptance_sets) + throw AutomatonParseException("number of acceptance sets " + "does not match automaton state " + "definitions"); + + acceptance_set = acceptance_set_mapping.second; + ++acceptance_set_mapping.second; + } + + operator[](current_state).acceptanceSets().setBit(acceptance_set); + } + + /* + * Process the transitions from the state to other states. Read a + * target state id and add a mapping for it in the translation table if + * necessary. Then, read the propositional formula guarding the + * transition and connect the current state to the target using the + * guard formula. + */ + + while (1) + { + einput_stream >> state_mapping.first; + + if (state_mapping.first == -1) + break; + + state_finder = state_number_map.insert(state_mapping); + if (!state_finder.second) + neighbor_state = (state_finder.first)->second; + else + { + if (state_mapping.second >= number_of_states) + throw AutomatonParseException("number of different state " + "identifiers does not match the size" + " of the Büchi automaton"); + + neighbor_state = state_mapping.second; + state_mapping.second++; + } + + try + { + guard = ::Ltl::LtlFormula::read(input_stream); + } + catch (const ::Ltl::LtlFormula::ParseErrorException& e) + { + throw AutomatonParseException(e.what()); + } + + if (!guard->propositional()) + { + ::Ltl::LtlFormula::destruct(guard); + throw AutomatonParseException("illegal operators in guard formula"); + } + + connect(current_state, neighbor_state, guard); + } + + processed_states.setBit(current_state); + ++number_of_processed_states; + } + + if (!initial_state_fixed) + throw AutomatonParseException("no initial state specified"); + + if (number_of_processed_states != number_of_states) + throw AutomatonParseException("incomplete automaton definition"); + } + catch (const IOException& e) + { + clear(); + if (input_stream.eof()) + throw AutomatonParseException("unexpected end of input"); + else + throw; + } + catch (...) + { + clear(); + throw; + } +} + +/* ========================================================================= */ +BuchiAutomaton* BuchiAutomaton::intersect + (const BuchiAutomaton& a1, const BuchiAutomaton& a2, + map, ALLOC(StateIdPair) >* + intersection_state_mapping) +/* ---------------------------------------------------------------------------- + * + * Description: Computes the intersection of two Büchi automata and returns + * a pointer to the intersection of the two automata. + * + * Arguments: a1, a2 -- References to two constant + * Büchi automata. + * intersection_state_mapping -- An (optional) pointer to a + * map which can be used to find + * out the state identifiers of + * the original automata to + * which a particular state in + * the intersection corresponds. + * + * Returns: A newly allocated BuchiAutomaton representing the + * intersection of the two automata. + * + * ------------------------------------------------------------------------- */ +{ + if (intersection_state_mapping != 0) + intersection_state_mapping->clear(); + + /* + * If either of the original automata is empty, the intersection is also + * empty. + */ + + if (a1.empty() || a2.empty()) + return new BuchiAutomaton(0, 0, 0); + + BuchiAutomaton* automaton; + + /* + * Determine the number of acceptance sets in the intersection. + */ + + const bool a1_has_no_acceptance_sets = (a1.number_of_acceptance_sets == 0); + const bool a2_has_no_acceptance_sets = (a2.number_of_acceptance_sets == 0); + + unsigned long int number_of_intersection_acceptance_sets; + + if (a1_has_no_acceptance_sets && a2_has_no_acceptance_sets) + number_of_intersection_acceptance_sets = 0; + else + number_of_intersection_acceptance_sets = a1.number_of_acceptance_sets + + a2.number_of_acceptance_sets; + + automaton = new BuchiAutomaton(1, 0, number_of_intersection_acceptance_sets); + + /* + * A stack for processing pairs of states of the original automata. + */ + + stack > + unprocessed_states; + + /* + * `state_mapping' maps pairs of states of the original automata to the + * states of the new automaton. + */ + + map, ALLOC(size_type) > + state_mapping; + + size_type first_free_id = 1; /* First free identifier for a + * new state in the intersection + * automaton. + */ + + const StateIdPair* state_pair; /* Pointer to pair of two state + * identifiers of the original + * automata. + */ + + size_type intersect_state; /* `Current' state in the + * intersection automaton. + */ + + bool intersect_state_valid; /* True if the current state has + * been determined by using the + * mapping. + */ + + BitArray* intersect_acceptance_sets; /* Pointers for accessing the */ + const BitArray* acceptance_sets; /* acceptance sets of the new + * and the original automata. + */ + + const GraphEdgeContainer* transitions1; /* Pointers for accessing the */ + const GraphEdgeContainer* transitions2; /* transitions of the two + * original automata. + */ + + ::Ltl::LtlFormula* guard1; /* Pointers for accessing the */ + ::Ltl::LtlFormula* guard2; /* guard formulas of the */ + ::Ltl::LtlFormula* new_guard; /* transitions of the + * automata. + */ + + /* + * Insert the initial state into the state mapping. + */ + + state_mapping.insert(make_pair(make_pair(a1.initial_state, a2.initial_state), + 0)); + unprocessed_states.push(&(state_mapping.begin()->first)); + + /* + * Adjust the acceptance sets of the initial state of the intersection. + */ + + intersect_acceptance_sets = &((*automaton)[0].acceptanceSets()); + + if (!a1_has_no_acceptance_sets) + { + acceptance_sets = &(a1[a1.initial_state].acceptanceSets()); + + for (unsigned long int accept_set = 0; + accept_set < a1.number_of_acceptance_sets; + accept_set++) + { + if (acceptance_sets->test(accept_set)) + intersect_acceptance_sets->setBit(accept_set); + } + } + + if (!a2_has_no_acceptance_sets) + { + acceptance_sets = &(a2[a2.initial_state].acceptanceSets()); + + for (unsigned long int accept_set = 0; + accept_set < a2.number_of_acceptance_sets; + accept_set++) + { + if (acceptance_sets->test(accept_set)) + intersect_acceptance_sets->setBit(a1.number_of_acceptance_sets + + accept_set); + } + } + + /* + * Pop pairs of states of the two original automata until all states have + * been processed. + */ + + try + { + while (!unprocessed_states.empty()) + { + if (::user_break) + throw UserBreakException(); + + intersect_state_valid = false; + state_pair = unprocessed_states.top(); + unprocessed_states.pop(); + + /* + * Loop through the transitions of the two original automata. If the + * conjunction of the guard formulae of any two transitions is + * satisfiable, insert a new transition into the intersection automaton. + * Create new states in the intersection automaton if necessary. + */ + + transitions1 = &a1[state_pair->first].edges(); + transitions2 = &a2[state_pair->second].edges(); + + for (GraphEdgeContainer::const_iterator tr1 = transitions1->begin(); + tr1 != transitions1->end(); + ++tr1) + { + guard1 = &(static_cast(*tr1)->guard()); + + for (GraphEdgeContainer::const_iterator tr2 = transitions2->begin(); + tr2 != transitions2->end(); + ++tr2) + { + guard2 = &(static_cast(*tr2)->guard()); + + new_guard = &Ltl::And::construct(*guard1, *guard2); + + if (new_guard->satisfiable()) + { + /* + * Determine the `current' state of the intersection automaton. + */ + + if (!intersect_state_valid) + { + intersect_state = state_mapping[*state_pair]; + intersect_state_valid = true; + } + + /* + * Test whether the state pair pointed to by the two transitions + * is already in the intersection. + */ + + pair, + ALLOC(size_type) >::iterator, bool> + check_state; + + check_state + = state_mapping.insert(make_pair(make_pair((*tr1)->targetNode(), + (*tr2)->targetNode()), + first_free_id)); + + if (check_state.second) /* insertion occurred? */ + { + automaton->expand(); + + /* + * Determine the acceptance sets to which the new state in the + * intersection automaton belongs. + */ + + intersect_acceptance_sets + = &((*automaton)[first_free_id].acceptanceSets()); + + if (!a1_has_no_acceptance_sets) + { + acceptance_sets = &(a1[check_state.first->first.first]. + acceptanceSets()); + + for (unsigned long int accept_set = 0; + accept_set < a1.number_of_acceptance_sets; + accept_set++) + { + if (acceptance_sets->test(accept_set)) + intersect_acceptance_sets->setBit(accept_set); + } + } + + if (!a2_has_no_acceptance_sets) + { + acceptance_sets = &(a2[check_state.first->first.second]. + acceptanceSets()); + + for (unsigned long int accept_set = 0; + accept_set < a2.number_of_acceptance_sets; + accept_set++) + { + if (acceptance_sets->test(accept_set)) + intersect_acceptance_sets->setBit + (a1.number_of_acceptance_sets + accept_set); + } + } + + /* + * Connect the `current' state of the intersection automaton to + * the new state. + */ + + automaton->connect(intersect_state, first_free_id, new_guard); + first_free_id++; + unprocessed_states.push(&(check_state.first->first)); + } + else + automaton->connect(intersect_state, check_state.first->second, + new_guard); + } + else + ::Ltl::LtlFormula::destruct(new_guard); + } + } + } + + if (intersection_state_mapping != 0) + { + for (map, ALLOC(size_type) > + ::const_iterator mapping = state_mapping.begin(); + mapping != state_mapping.end(); + ++mapping) + intersection_state_mapping->insert(make_pair(mapping->second, + mapping->first)); + } + } + catch (...) + { + delete automaton; + throw; + } + + return automaton; +} + +/* ========================================================================= */ +void BuchiAutomaton::print + (ostream& stream, const int indent, const GraphOutputFormat fmt) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a BuchiAutomaton to a stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * fmt -- Determines the format of the output. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + if (fmt == DOT) + estream << string(indent, ' ') + "digraph G {\n"; + + if (nodes.empty()) + { + if (fmt == NORMAL) + estream << string(indent, ' ') + "The Büchi automaton is empty.\n"; + } + else + { + if (fmt == NORMAL) + { + pair statistics = stats(); + pair reachable_part_statistics + = subgraphStats(initial_state); + + estream << string(indent, ' ') + "The Büchi automaton consists of\n" + + string(indent + 4, ' ') + << statistics.first + << " states and\n" + string(indent + 4, ' ') + << statistics.second + << " transitions.\n" + string(indent, ' ') + + "The automaton has " + << number_of_acceptance_sets + << " sets of accepting states.\n" + string(indent, ' ') + + "The reachable part of the automaton contains\n" + + string(indent + 4, ' ') + << reachable_part_statistics.first + << " states and\n" + string(indent + 4, ' ') + << reachable_part_statistics.second + << " transitions.\n" + string(indent, ' ') + "Initial state: " + << initial_state + << '\n'; + } + else if (fmt == DOT) + { + estream << string(indent, ' ') + " init [style=invis];\n" + + string(indent, ' ') + " init->n" + << initial_state + << ";\n"; + } + + size_type s = nodes.size(); + for (size_type state = 0; state < s; ++state) + { + estream << string(indent, ' '); + if (fmt == NORMAL) + { + estream << "State " << state << ":\n"; + operator[](state).print(stream, indent + 4, NORMAL, + number_of_acceptance_sets); + } + else if (fmt == DOT) + { + GraphEdgeContainer::const_iterator transition; + bool first_printed = false; + + estream << " n" + << state + << string(" [shape=circle,label=\"") + << state + << "\\n{"; + + for (unsigned long int accept_set = 0; + accept_set < number_of_acceptance_sets; + accept_set++) + { + if (operator[](state).acceptanceSets().test(accept_set)) + { + if (first_printed) + estream << ','; + else + first_printed = true; + + estream << accept_set; + } + } + + estream << "}\",fontsize=12];\n"; + + for (transition = nodes[state]->edges().begin(); + transition != nodes[state]->edges().end(); + ++transition) + { + estream << string(indent + 2, ' ') + 'n' << state; + (*transition)->print(stream, indent, fmt); + estream << ";\n"; + } + } + } + } + + if (fmt == DOT) + estream << string(indent, ' ') + "}\n"; + + estream.flush(); +} + + + +/****************************************************************************** + * + * Function definitions for class BuchiAutomaton::BuchiTransition. + * + *****************************************************************************/ + +/* ========================================================================= */ +void BuchiAutomaton::BuchiTransition::print + (ostream& stream, const int indent, const GraphOutputFormat fmt) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a transition between two states of + * a Büchi automaton. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * fmt -- Determines the format of output. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + if (fmt == NORMAL) + { + estream << string(indent, ' ') + "Transition to state " + << targetNode() + << " [ guard: " << *guard_formula << " ]\n"; + } + else if (fmt == DOT) + { + string formula(StringUtil::toString(*guard_formula)); + + estream << " -> n" << targetNode() << " [label=\""; + for (unsigned long int i = 0; i < formula.length(); ++i) + { + if (formula[i] == '/') + { + estream << "&&"; + i++; + } + else if (formula[i] == '\\') + { + estream << "||"; + i++; + } + else + estream << formula[i]; + } + estream << "\",fontsize=10,fontname=\"Courier-Bold\"]"; + } + + estream.flush(); +} + + + +/****************************************************************************** + * + * Function definitions for class BuchiAutomaton::BuchiState. + * + *****************************************************************************/ + +/* ========================================================================= */ +void BuchiAutomaton::BuchiState::print + (ostream& stream, const int indent, const GraphOutputFormat fmt, + const unsigned long int number_of_acceptance_sets) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a state of a Büchi automaton. + * + * Arguments: stream -- A reference to an output + * stream. + * indent -- Number of spaces to leave to + * the left of output. + * fmt -- Determines the output format + * of the state. + * number_of_acceptance_sets -- Number of acceptance sets in + * the automaton in which the + * state belongs. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------ */ +{ + if (fmt == DOT) + return; + + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + estream << string(indent, ' ') + "Member of acceptance sets {"; + + bool first_printed = false; + + for (unsigned long int accept_set = 0; + accept_set < number_of_acceptance_sets; + ++accept_set) + { + if (acceptance_sets[accept_set]) + { + if (first_printed) + estream << ", "; + else + first_printed = true; + + estream << accept_set; + } + } + + estream << "}\n"; + + if (!edges().empty()) + { + GraphEdgeContainer::const_iterator edge; + + for (edge = edges().begin(); edge != edges().end(); ++edge) + (*edge)->print(stream, indent); + + } else + estream << string(indent, ' ') + "No transitions to other states.\n"; + + estream.flush(); +} + +} diff --git a/lbtt/src/BuchiAutomaton.h b/lbtt/src/BuchiAutomaton.h new file mode 100644 index 000000000..7b58867b3 --- /dev/null +++ b/lbtt/src/BuchiAutomaton.h @@ -0,0 +1,861 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef BUCHIAUTOMATON_H +#define BUCHIAUTOMATON_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include "Alloc.h" +#include "BitArray.h" +#include "EdgeContainer.h" +#include "Exception.h" +#include "Graph.h" +#include "LtlFormula.h" + +using namespace std; + +namespace Graph +{ + +/****************************************************************************** + * + * A class for representing Büchi automata with a single set of accepting + * states. + * + *****************************************************************************/ + +class BuchiAutomaton : public Graph +{ +public: + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class BuchiTransition; /* A class for representing + * the transitions between + * the states of the + * automaton. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class BuchiState : /* A class for */ + public Graph::Node /* representing the */ + { /* states of the + * automaton. + */ + public: + explicit BuchiState /* Constructor. */ + (const unsigned long int + num_of_acceptance_sets); + + ~BuchiState(); /* Destructor. */ + + /* `edges' inherited from Graph::Node */ + + BitArray& acceptanceSets(); /* Tell the acceptance */ + const BitArray& acceptanceSets() const; /* status of the state. */ + + void print /* Writes information */ + (ostream& stream, /* about the state to a */ + const int indent, /* stream. */ + const GraphOutputFormat fmt) const; + + void print /* Writes information */ + (ostream& stream, /* about the state to a */ + const int indent, /* stream. */ + const GraphOutputFormat fmt, + const unsigned long int + number_of_acceptance_sets) + const; + + private: + BuchiState(const BuchiState&); /* Prevent copying and */ + BuchiState& operator=(const BuchiState&); /* assignment of + * BuchiState objects. + */ + + BitArray acceptance_sets; /* Acceptance status of the + * state. + */ + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + explicit BuchiAutomaton /* Constructor. */ + (const size_type initial_number_of_states = 0, + const size_type initstate = 0, + const unsigned long int + number_of_accept_sets = 0); + + BuchiAutomaton(const BuchiAutomaton& automaton); /* Copy constructor. */ + + ~BuchiAutomaton(); /* Destructor. */ + + BuchiAutomaton& /* Assignment operator. */ + operator=(const BuchiAutomaton& automaton); + + BuchiState& operator[] /* Indexing operator. No */ + (const size_type index) const; /* range check is + * performed on the + * argument. + */ + + BuchiState& node(const size_type index) const; /* Synonym for the indexing + * operator. This function + * also checks the range of + * the argument. + */ + + /* `size' inherited from Graph */ + + /* `empty' inherited from Graph */ + + void clear(); /* Makes the automaton + * empty. + */ + + size_type expand(size_type node_count = 1); /* Inserts states to the + * automaton. + */ + + void connect /* Connects two states */ + (const size_type father, /* of the automaton with */ + const size_type child); /* an unguarded + * transition. + */ + + void connect /* Connects two states */ + (const size_type father, const size_type child, /* of the automaton with */ + ::Ltl::LtlFormula& guard); /* a transition guarded */ + void connect /* by a propositional */ + (const size_type father, const size_type child, /* formula. */ + ::Ltl::LtlFormula* guard); + + /* `disconnect' inherited from Graph */ + + /* `connected' inherited from Graph */ + + /* `stats' inherited from Graph */ + + /* `subgraphStats' inherited from Graph */ + + size_type initialState() const; /* Get or set the */ + size_type& initialState(); /* initial state of the * + * automaton. + */ + + unsigned long int numberOfAcceptanceSets() const; /* Returns the number of + * acceptance sets in the + * automaton. + */ + + BuchiAutomaton* regularize() const; /* Converts a generalized + * automaton to an + * automaton with one set + * of accepting states. + */ + + void read(istream& input_stream); /* Reads the automaton + * from a stream. + */ + + void print /* Writes information */ + (ostream& stream = cout, /* about the automaton */ + const int indent = 0, /* to a stream in */ + const GraphOutputFormat fmt = NORMAL) const; /* various formats + * (determined by the + * `fmt' argument). + */ + + static BuchiAutomaton* intersect /* Computes the */ + (const BuchiAutomaton& a1, /* intersection of two */ + const BuchiAutomaton& a2, /* Büchi automata. */ + map, ALLOC(StateIdPair) >* + intersection_state_mapping = 0); + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class AutomatonParseException; /* Class for reporting + * parse errors when + * reading an automaton + * description from a + * stream. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +private: + size_type initial_state; /* Identifier of the + * initial state of the + * automaton. + */ + + unsigned long int number_of_acceptance_sets; /* Number of acceptance + * sets in the automaton. + */ +}; + + + +/****************************************************************************** + * + * A class for representing the transitions of a Büchi automaton. + * + *****************************************************************************/ + +class BuchiAutomaton::BuchiTransition : public Graph::Edge +{ +public: + BuchiTransition /* Constructor. */ + (const size_type target, + ::Ltl::LtlFormula* formula); + + ~BuchiTransition(); /* Destructor. */ + +private: + +public: + + /* `targetNode' inherited from Graph::Edge */ + + bool enabled /* These functions test */ + (const BitArray& truth_assignment, /* whether the */ + const unsigned long int assignment_size) /* transition is */ + const; /* enabled in a given */ + /* truth assignment for */ + bool enabled /* the atomic */ + (const Bitset& truth_assignment) const; /* propositions. */ + + ::Ltl::LtlFormula& guard() const; /* Returns the + * propositional formula + * guarding the transition. + */ + + void print /* Writes information */ + (ostream& stream = cout, /* about the transition */ + const int indent = 0, /* to a stream in */ + const GraphOutputFormat fmt = NORMAL) const; /* various formats + * (determined by the + * `fmt' argument). + */ + +private: + BuchiTransition(const BuchiTransition&); /* Prevent copying and */ + BuchiTransition& operator= /* assignment of */ + (const BuchiTransition&); /* BuchiTransition + * objects. + */ + + bool operator== /* Equality test. Used */ + (const Graph::Edge& /* for sorting */ + transition) const; /* transitions in an STL + * container. + */ + + bool operator< /* `Less than' relation. */ + (const Graph::Edge& /* Used for sorting */ + transition) const; /* transitions in an STL + * container. + */ + + ::Ltl::LtlFormula* guard_formula; /* The propositional + * formula guarding the + * transition. + */ +}; + + + +/****************************************************************************** + * + * A class for reporting parse errors when reading an automaton description + * from a stream. + * + *****************************************************************************/ + +class BuchiAutomaton::AutomatonParseException : public Exception +{ +public: + AutomatonParseException /* Constructor. */ + (const string& msg = "parse error"); + + /* default copy constructor */ + + ~AutomatonParseException() throw(); /* Destructor. */ + + AutomatonParseException& /* Assignment operator. */ + operator=(const AutomatonParseException& e); + + /* `what' inherited from class Exception */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class BuchiAutomaton. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline BuchiAutomaton::~BuchiAutomaton() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class BuchiAutomaton. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline BuchiAutomaton::BuchiState& BuchiAutomaton::operator[] + (const size_type index) const +/* ---------------------------------------------------------------------------- + * + * Description: Indexing operator for class BuchiAutomaton. This function can + * be used to refer to the individual states of the automaton. + * No range check will be performed on the argument. + * + * Argument: index -- Index of a state. + * + * Returns: A reference to a state of the automaton. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast(*nodes[index]); +} + +/* ========================================================================= */ +inline BuchiAutomaton::BuchiState& BuchiAutomaton::node + (const size_type index) const +/* ---------------------------------------------------------------------------- + * + * Description: Function for referring to a single state of a BuchiAutomaton. + * This function will perform a range check on the argument. + * + * Argument: index -- Index of a state. + * + * Returns: A reference to a state of the automaton. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast(Graph::node(index)); +} + +/* ========================================================================= */ +inline void BuchiAutomaton::connect + (const size_type father, const size_type child) +/* ---------------------------------------------------------------------------- + * + * Description: Connects two states of a BuchiAutomaton to each other with an + * unguarded transition (actually, a transition with a guard + * that is always true). + * + * Arguments: father -- Source state identifier. + * child -- Target state identifier. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + connect(father, child, &(::Ltl::True::construct())); +} + +/* ========================================================================= */ +inline void BuchiAutomaton::connect + (const size_type father, const size_type child, ::Ltl::LtlFormula& guard) +/* ---------------------------------------------------------------------------- + * + * Description: Connects two states of a BuchiAutomaton to each other, using + * a LtlFormula (which is actually a propositional formula) to + * guard the transition between the states. + * + * Arguments: father -- Source state. + * child -- Target state. + * guard -- A reference to an LtlFormula (a propositional + * formula) guarding the transition. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + connect(father, child, guard.clone()); +} + +/* ========================================================================= */ +inline void BuchiAutomaton::connect + (const size_type father, const size_type child, ::Ltl::LtlFormula* guard) +/* ---------------------------------------------------------------------------- + * + * Description: Connects two states of a BuchiAutomaton to each other, using + * a LtlFormula (which is actually a propositional formula) to + * guard the transition between the states. + * + * Arguments: father -- Source state. + * child -- Target state. + * guard -- A pointer to an LtlFormula (a propositional + * formula) guarding the transition. The transition + * will "own" the guard formula. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + BuchiTransition* new_buchi_transition = new BuchiTransition(child, guard); + + try + { + nodes[father]->outgoing_edges.insert(new_buchi_transition); + } + catch (...) + { + delete new_buchi_transition; + throw; + } +} + +/* ========================================================================= */ +inline BuchiAutomaton::size_type BuchiAutomaton::initialState() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the initial state of the BuchiAutomaton by value. + * + * Arguments: None. + * + * Returns: Index of the initial state of the automaton. + * + * ------------------------------------------------------------------------- */ +{ + return initial_state; +} + +/* ========================================================================= */ +inline BuchiAutomaton::size_type& BuchiAutomaton::initialState() +/* ---------------------------------------------------------------------------- + * + * Description: Returns the initial state of the BuchiAutomaton by reference. + * This function can therefore be used to change the initial + * state. + * + * Arguments: None. + * + * Returns: A reference to the value of the initial state. + * + * ------------------------------------------------------------------------- */ +{ + return initial_state; +} + +/* ========================================================================= */ +inline unsigned long int BuchiAutomaton::numberOfAcceptanceSets() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the number of acceptance sets in the automaton. + * + * Arguments: None. + * + * Returns: The number of acceptance sets in the automaton. + * + * ------------------------------------------------------------------------- */ +{ + return number_of_acceptance_sets; +} + +/* ========================================================================= */ +inline istream& operator>>(istream& stream, BuchiAutomaton& automaton) +/* ---------------------------------------------------------------------------- + * + * Description: Defines an alternative method for reading an automaton from + * a stream by using the >> operator. + * + * Arguments: stream -- A reference to an input stream. + * automaton -- A reference to the BuchiAutomaton. + * + * Returns: A reference to the input stream. + * + * ------------------------------------------------------------------------- */ +{ + automaton.read(stream); + return stream; +} + + + +/****************************************************************************** + * + * Inline function definitions for class BuchiAutomaton::BuchiTransition. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline BuchiAutomaton::BuchiTransition::BuchiTransition + (const size_type target, ::Ltl::LtlFormula* formula) : + Edge(target), guard_formula(formula) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class BuchiAutomaton::BuchiTransition. + * Initializes a new transition to a BuchiState, guarded by an + * LtlFormula (which is actually a propositional formula). + * + * Arguments: target -- Identifier of the target state of the automaton. + * formula -- A pointer to a propositional formula guarding + * the transition. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline BuchiAutomaton::BuchiTransition::BuchiTransition + (const BuchiTransition& transition) : + Edge(transition), guard_formula(transition.guard_formula->clone()) +/* ---------------------------------------------------------------------------- + * + * Description: Copy constructor for class BuchiAutomaton::BuchiTransition. + * Creates a copy of a BuchiTransition object. + * + * Arguments: transition -- BuchiTransition to be copied. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline BuchiAutomaton::BuchiTransition::~BuchiTransition() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class BuchiAutomaton::BuchiTransition. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + ::Ltl::LtlFormula::destruct(guard_formula); +} + +/* ========================================================================= */ +inline bool BuchiAutomaton::BuchiTransition::operator== + (const Graph::Edge& transition) const +/* ---------------------------------------------------------------------------- + * + * Description: Equality relation for comparing two BuchiTransitions. Two + * transitions are `equal' if and only if their target nodes + * have the same identifier and if their guard formulae are + * identical according to the `less' + * relation. + * + * Argument: transition -- A reference to a constant Edge. + * + * Returns: Truth value according to the relationship between the two + * transitions. + * + * ------------------------------------------------------------------------- */ +{ + /* + * This function is called only when comparing two edges stored in the + * `outgoing_edges' GraphEdgeContainer of some state in a BuchiAutomaton. + * Since (pointers to) BuchiTransitions are never mixed with other types in + * that container, it is always safe to static_cast `transition' to a + * reference to a BuchiTransition. + */ + + return (Edge::operator==(transition) + && !(guard_formula < static_cast(transition) + .guard_formula) + && !(static_cast(transition).guard_formula + < guard_formula)); +} + +/* ========================================================================= */ +inline bool BuchiAutomaton::BuchiTransition::operator< + (const Graph::Edge& transition) const +/* ---------------------------------------------------------------------------- + * + * Description: `Less than' relation for comparing two BuchiTransitions. A + * BuchiTransition is `less than' another if and only if the + * identifier of its target node is less than that of the other + * or the target nodes agree but the guard formula of the + * first transition is `less than' the other according to the + * `less' relation. + * + * Argument: transition -- A reference to a constant Edge. + * + * Returns: Truth value according to the relationship between the two + * transitions. + * + * ------------------------------------------------------------------------- */ +{ + /* + * This function is called only when comparing two edges stored in the + * `outgoing_edges' GraphEdgeContainer of some state in a BuchiAutomaton. + * Since (pointers to) BuchiTransitions are never mixed with other types in + * that container, it is always safe to static_cast `transition' to a + * reference to a BuchiTransition. + */ + + return (Edge::operator<(transition) + || (Edge::operator==(transition) + && guard_formula + < static_cast(transition) + .guard_formula)); +} + +/* ========================================================================= */ +inline bool BuchiAutomaton::BuchiTransition::enabled + (const BitArray& truth_assignment, const unsigned long int assignment_size) + const +/* ---------------------------------------------------------------------------- + * + * Description: Determines whether the transition is enabled in a given + * truth assignment for propositional variables. + * + * Arguments: truth_assignment -- A reference to a constant BitArray + * representing an assignment of truth + * values to propositional variables. + * assignment_size -- Number of propositions in the + * truth assignment. + * + * Returns: A truth value telling whether the transition is enabled in + * the truth assignment. + * + * ------------------------------------------------------------------------- */ +{ + return guard_formula->evaluate(truth_assignment, assignment_size); +} + +/* ========================================================================= */ +inline bool BuchiAutomaton::BuchiTransition::enabled + (const Bitset& truth_assignment) const +/* ---------------------------------------------------------------------------- + * + * Description: See above. + * + * Arguments: truth_assignment -- A reference to a constant Bitset + * representing an assignment of truth + * values to propositional variables. + * + * Returns: A truth value telling whether the transition is enabled in + * the truth assignment. + * + * ------------------------------------------------------------------------- */ +{ + return enabled(truth_assignment, truth_assignment.capacity()); +} + +/* ========================================================================= */ +inline ::Ltl::LtlFormula& BuchiAutomaton::BuchiTransition::guard() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the propositional formula guarding the transition + * (a LtlFormula object). + * + * Arguments: None. + * + * Returns: A reference to the constant propositional formula guarding + * the transition. + * + * ------------------------------------------------------------------------- */ +{ + return *guard_formula; +} + + + +/****************************************************************************** + * + * Inline function definitions for class BuchiAutomaton::BuchiState. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline BuchiAutomaton::BuchiState::BuchiState + (const unsigned long int num_of_acceptance_sets) : + Node(), acceptance_sets(num_of_acceptance_sets) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class BuchiAutomaton::BuchiState. Initializes + * a new state for the automaton. + * + * Argument: num_of_acceptance_sets -- Number of acceptance sets in the + * automaton to which the state + * belongs. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + acceptance_sets.clear(num_of_acceptance_sets); +} + +/* ========================================================================= */ +inline BuchiAutomaton::BuchiState::~BuchiState() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class BuchiAutomaton::BuchiState. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline BitArray& BuchiAutomaton::BuchiState::acceptanceSets() +/* ---------------------------------------------------------------------------- + * + * Description: Returns the acceptance status of the BuchiState, i.e. the + * BitArray indicating the acceptance sets to which the state + * belongs. + * + * Arguments: None. + * + * Returns: A reference to a BitArray object telling the acceptance sets + * to which the state belongs. + * + * ------------------------------------------------------------------------- */ +{ + return acceptance_sets; +} + +/* ========================================================================= */ +inline const BitArray& BuchiAutomaton::BuchiState::acceptanceSets() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the acceptance status of the BuchiState, i.e. the + * BitArray indicating the acceptance sets to which the state + * belongs. + * + * Arguments: None. + * + * Returns: A reference to a constant BitArray object telling the + * acceptance sets to which the state belongs. + * + * ------------------------------------------------------------------------- */ +{ + return acceptance_sets; +} + +/* ========================================================================= */ +inline void BuchiAutomaton::BuchiState::print + (ostream& stream, const int indent, const GraphOutputFormat fmt) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a state of a Büchi automaton, + * assuming that the (unspecified) maximum number of acceptance + * sets in the automaton is 0. [Note: This function is used + * to override the `print' function defined in the base class + * `Graph::Node'.] + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * fmt -- Determines the output format of the state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------ */ +{ + this->print(stream, indent, fmt, 0); +} + + + +/****************************************************************************** + * + * Inline function definitions for class + * BuchiAutomaton::AutomatonParseException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline BuchiAutomaton::AutomatonParseException::AutomatonParseException + (const string& msg) : + Exception(msg) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class + * BuchiAutomaton::AutomatonParseException. Initializes a new + * exception object with an error message. + * + * Argument: msg -- A reference to a constant string containing the + * error message. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline BuchiAutomaton::AutomatonParseException::~AutomatonParseException() + throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class BuchiAutomaton::AutomatonParseException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline BuchiAutomaton::AutomatonParseException& +BuchiAutomaton::AutomatonParseException::operator= + (const AutomatonParseException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class + * BuchiAutomaton::AutomatonParseException. Assigns the contents + * of another object of the same type to the exception object. + * + * Argument: e -- A reference to a constant exception object of the + * same type. + * + * Returns: A reference to the exception object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + +} + +#endif /* !BUCHIAUTOMATON_H */ diff --git a/lbtt/src/Config-lex.ll b/lbtt/src/Config-lex.ll new file mode 100644 index 000000000..e09e51308 --- /dev/null +++ b/lbtt/src/Config-lex.ll @@ -0,0 +1,258 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +%{ +#include +#include +#include +#include +#include +#include +#include "Configuration.h" +#include "Config-parse.h" + +extern int config_file_line_number; + +%} + +%option case-insensitive +%option never-interactive +%option noyywrap + +%% + +[ \t]* { /* Skip whitespace. */ } +"#"[^\n]* { /* Skip comments. */ } + +"\n" { /* Skip newlines, but update the line number. */ + config_file_line_number++; + } + +"{" { return CFG_LBRACE; } +"}" { return CFG_RBRACE; } +"=" { return CFG_EQUALS; } + +algorithm { return CFG_ALGORITHM; } +enabled { return CFG_ENABLED; } +name { return CFG_NAME; } +parameters { return CFG_PARAMETERS; } +path { return CFG_PROGRAMPATH; } + +globaloptions { return CFG_GLOBALOPTIONS; } +comparisoncheck { return CFG_COMPARISONTEST; } +comparisontest { return CFG_COMPARISONTEST; } +consistencycheck { return CFG_CONSISTENCYTEST; } +consistencytest { return CFG_CONSISTENCYTEST; } +interactive { return CFG_INTERACTIVE; } +intersectioncheck { return CFG_INTERSECTIONTEST; } +intersectiontest { return CFG_INTERSECTIONTEST; } +modelcheck { return CFG_MODELCHECK; } +rounds { return CFG_ROUNDS; } +verbosity { return CFG_VERBOSITY; } + +statespaceoptions { return CFG_STATESPACEOPTIONS; } +edgeprobability { return CFG_EDGEPROBABILITY; } +propositions { return CFG_PROPOSITIONS; } +size { return CFG_SIZE; } +truthprobability { return CFG_TRUTHPROBABILITY; } +changeinterval { return CFG_CHANGEINTERVAL; } +randomseed { return CFG_RANDOMSEED; } + +formulaoptions { return CFG_FORMULAOPTIONS; } +abbreviatedoperators { return CFG_ABBREVIATEDOPERATORS; } +andpriority { return CFG_ANDPRIORITY; } +beforepriority { return CFG_BEFOREPRIORITY; } +defaultoperatorpriority { return CFG_DEFAULTOPERATORPRIORITY; } +equivalencepriority { return CFG_EQUIVALENCEPRIORITY; } +falsepriority { return CFG_FALSEPRIORITY; } +finallypriority { return CFG_FINALLYPRIORITY; } +generatemode { return CFG_GENERATEMODE; } +globallypriority { return CFG_GLOBALLYPRIORITY; } +implicationpriority { return CFG_IMPLICATIONPRIORITY; } +nextpriority { return CFG_NEXTPRIORITY; } +notpriority { return CFG_NOTPRIORITY; } +orpriority { return CFG_ORPRIORITY; } +outputmode { return CFG_OUTPUTMODE; } +propositionpriority { return CFG_PROPOSITIONPRIORITY; } +releasepriority { return CFG_RELEASEPRIORITY; } +strongreleasepriority { return CFG_STRONGRELEASEPRIORITY; } +truepriority { return CFG_TRUEPRIORITY; } +untilpriority { return CFG_UNTILPRIORITY; } +weakuntilpriority { return CFG_WEAKUNTILPRIORITY; } +xorpriority { return CFG_XORPRIORITY; } + +true|yes { + yylval.truth_value = true; + return CFG_TRUTH_VALUE; + } + +false|no { + yylval.truth_value = false; + return CFG_TRUTH_VALUE; + } + +always { + yylval.interactivity_value = + Configuration::ALWAYS; + return CFG_INTERACTIVITY_VALUE; + } + +never { + yylval.interactivity_value = + Configuration::NEVER; + return CFG_INTERACTIVITY_VALUE; + } + +onerror { + yylval.interactivity_value = + Configuration::ONERROR; + return CFG_INTERACTIVITY_VALUE; + } + +normal { + yylval.formula_mode_value = + Configuration::NORMAL; + return CFG_FORMULA_MODE_VALUE; + } + +nnf { + yylval.formula_mode_value = Configuration::NNF; + return CFG_FORMULA_MODE_VALUE; + } + +local { + yylval.product_type_value = Configuration::LOCAL; + return CFG_PRODUCT_TYPE_VALUE; + } + +global { + yylval.product_type_value = + Configuration::GLOBAL; + return CFG_PRODUCT_TYPE_VALUE; + } + +randomgraph { + yylval.statespace_mode_value + = Configuration::RANDOMGRAPH; + return CFG_STATESPACE_MODE_VALUE; + } + +randomconnectedgraph { + yylval.statespace_mode_value + = Configuration::RANDOMCONNECTEDGRAPH; + return CFG_STATESPACE_MODE_VALUE; + } + +randompath { + yylval.statespace_mode_value + = Configuration::RANDOMPATH; + return CFG_STATESPACE_MODE_VALUE; + } + +enumeratedpath { + yylval.statespace_mode_value + = Configuration::ENUMERATEDPATH; + return CFG_STATESPACE_MODE_VALUE; + } + + +"-"?[0-9]+"...""-"?[0-9]+ { + char* dot_ptr; + yylval.integer_interval.min + = strtol(yytext, &dot_ptr, 10); + + if (yylval.integer_interval.min == LONG_MIN + || yylval.integer_interval.min == LONG_MAX) + throw Configuration::ConfigurationException + (config_file_line_number, + "integer out of range"); + + dot_ptr += 3; + yylval.integer_interval.max + = strtol(dot_ptr, 0, 10); + + if (yylval.integer_interval.max == LONG_MIN + || yylval.integer_interval.max == LONG_MAX) + throw Configuration::ConfigurationException + (config_file_line_number, + "integer out of range"); + + return CFG_INTEGER_INTERVAL; + } + +"-"?[0-9]+ { + yylval.integer = strtol(yytext, 0, 10); + if (yylval.integer == LONG_MIN + || yylval.integer == LONG_MAX) + throw Configuration::ConfigurationException + (config_file_line_number, + "integer out of range"); + return CFG_INTEGER; + } + +"-"?[0-9]*"."[0-9]+ { + yylval.real = strtod(yytext, 0); + + if (yylval.real == HUGE_VAL + || yylval.real == -HUGE_VAL) + throw Configuration::ConfigurationException + (config_file_line_number, + "real number out of range"); + return CFG_REAL; + } + +\"([^\n\"\\]*(\\[^\n])?)*\" { + unsigned long int len = strlen(yytext); + bool escape = false; + yylval.str = new string; + for (unsigned long int i = 1; i < len - 1; i++) + { + if (!escape && yytext[i] == '\\') + escape = true; + else + { + escape = false; + (*yylval.str) += yytext[i]; + } + } + return CFG_STRING_CONSTANT; + } + +. { + return CFG_UNKNOWN; + } + + +%% + +/* ========================================================================= */ +int getCharacter() +/* ---------------------------------------------------------------------------- + * + * Description: Reads the next character from the lexer input stream. + * + * Arguments: None. + * + * Returns: The next character in the lexer input stream or EOF if there + * are no more characters to read. + * + * ------------------------------------------------------------------------- */ +{ + return yyinput(); +} diff --git a/lbtt/src/Config-parse.yy b/lbtt/src/Config-parse.yy new file mode 100644 index 000000000..07fba1c17 --- /dev/null +++ b/lbtt/src/Config-parse.yy @@ -0,0 +1,1381 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +%{ + +#include +#include +#include +#include "Configuration.h" +#include "StringUtil.h" + + + +/****************************************************************************** + * + * Variables for parsing the configuration file. + * + *****************************************************************************/ + +static Configuration::AlgorithmInformation /* Stores all the */ + algorithm_information; /* information in a + * single `Algorithm' + * block in the + * configuration file. + */ + +static int algorithm_begin_line; /* Input file line number + * denoting the beginning + * of the most recently + * encountered `Algorithm' + * block. + */ + +static int expected_token; /* Type of a token to be + * encountered next when + * reading the + * configuration file. + */ + +static int current_block_type; /* Type of the current + * configuration block. + */ + +static int current_option_type; /* Type of the current + * option name. + */ + +static Configuration* parser_cfg; /* Pointer to a + * Configuration data + * structure in which + * the configuration will + * be stored. + */ + +int config_file_line_number; /* Number of the current + * line in the + * configuration + * file. + */ + + + +/****************************************************************************** + * + * Declarations for external functions and variables (provided by the lexer) + * used when parsing the configuration file. + * + *****************************************************************************/ + +#ifdef YYTEXT_POINTER +extern char* yytext; /* Current token in the */ +#else /* input (provided by */ +extern char yytext[]; /* the lexer). */ +#endif /* YYTEXT_POINTER */ + +extern void yyrestart(FILE*); /* Changes the input stream + * for the lexer (provided + * by the lexer). + */ + +extern int yylex(); /* Reads the next token + * from the input (this + * function is provided by + * the lexer). + */ + +extern int getCharacter(); /* Returns the next + * character in the lexer + * input stream. + */ + + + +/****************************************************************************** + * + * Function for reporting parse errors. + * + *****************************************************************************/ + +/* ========================================================================= */ +void yyerror(char* error_message) +/* ---------------------------------------------------------------------------- + * + * Description: Function for reporting parse errors. + * + * Arguments: error_message -- An error message. + * + * Returns: Nothing. Instead, throws a + * Configuration::ConfigurationException initialized with the + * given error message. + * + * ------------------------------------------------------------------------- */ +{ + string unknown_token(yytext); + int c; + + do + { + c = getCharacter(); + if (c != EOF && c != ' ' && c != '\t' && c != '\n') + unknown_token += static_cast(c); + } + while (c != EOF && c != ' ' && c != '\t' && c != '\n'); + + string msg; + + switch (expected_token) + { + case CFG_BLOCK_ID : + msg = string("unrecognized block identifier (`") + unknown_token + "')"; + break; + case CFG_OPTION_ID : + msg = string("unrecognized option identifier (`") + unknown_token + "')"; + break; + case CFG_LBRACE : + msg = "`{' expected after block identifier"; + break; + case CFG_EQUALS : + msg = "`=' expected after option identifier"; + break; + case CFG_TRUTH_VALUE : + msg = "truth value expected as option argument"; + break; + case CFG_INTERACTIVITY_VALUE : + msg = "interactivity mode expected as option argument"; + break; + case CFG_FORMULA_MODE_VALUE : + msg = "formula generation mode expected as option argument"; + break; + case CFG_STATESPACE_MODE_VALUE : + msg = "state space generation mode expected as option argument"; + break; + case CFG_PRODUCT_TYPE_VALUE : + msg = "model checking mode expected as option argument"; + break; + case CFG_INTEGER : + msg = "nonnegative integer expected as option argument"; + break; + case CFG_INTEGER_INTERVAL : + msg = "nonnegative integer or an integer interval expected as option" + " argument"; + break; + case CFG_REAL : + msg = "nonnegative real number expected as option argument"; + break; + case CFG_STRING_CONSTANT : + msg = "string constant expected as option argument"; + break; + default : + msg = error_message; + break; + } + + throw Configuration::ConfigurationException(config_file_line_number, msg); +} + + + +/****************************************************************************** + * + * Functions for performing various bound checks for the values of different + * options in the configuration file. + * + *****************************************************************************/ + +/* ========================================================================= */ +void checkIntegerRange + (long int value, const struct Configuration::IntegerRange& range, + bool show_line_number_if_error = true) +/* ---------------------------------------------------------------------------- + * + * Description: Checks that a value given to a configuration is within the + * acceptable range. + * + * Arguments: value -- Integer value for a + * configuration option. + * range -- A reference to a constant + * struct + * Configuration::IntegerRange. + * show_line_number_if_error -- If the value is not within the + * specified range, this + * parameter determines whether a + * configuration file line number + * is shown together with the + * error message. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (value < range.min || value > range.max) + throw Configuration::ConfigurationException + ((show_line_number_if_error ? config_file_line_number : -1), + range.error_message); +} + +/* ========================================================================= */ +void checkProbability(double value, bool show_line_number_if_error = true) +/* ---------------------------------------------------------------------------- + * + * Description: Checks whether a probability value specified in the program + * configuration is between 0.0 and 1.0 inclusive. + * + * Argument: value -- A value supposed to denote a + * probability. + * show_line_number_if_error -- If the value is not within the + * specified range, this + * parameter determines whether a + * configuration file line number + * is shown together with the + * error message. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (value < 0.0 || value > 1.0) + throw Configuration::ConfigurationException + ((show_line_number_if_error ? config_file_line_number : -1), + "probability must be between 0.0 and 1.0 inclusive"); +} + +/* ========================================================================= */ +static inline bool isLocked(int option) +/* ---------------------------------------------------------------------------- + * + * Description: Checks whether the value of a configuration option can be + * initialized from the configuration file. (This should not be + * done if the option was present on the program command line.) + * + * Argument: option -- The command line option. + * + * Returns: `true' if the value of the option has been overridden in the + * command line. + * + * ------------------------------------------------------------------------- */ +{ + return (parser_cfg->locked_options.find(make_pair(current_block_type, + option)) + != parser_cfg->locked_options.end()); +} + +%} + + +/****************************************************************************** + * + * Declarations for terminal and nonterminal symbols used in the grammar rules + * below. + * + *****************************************************************************/ + +/* Data types for configuration file option values. */ + +%union +{ + bool truth_value; + + long int integer; + + Configuration::InteractionMode interactivity_value; + + Configuration::FormulaMode formula_mode_value; + + Configuration::StateSpaceMode statespace_mode_value; + + Configuration::ProductMode product_type_value; + + double real; + + string *str; + + struct + { + long int min; + long int max; + } integer_interval; +} + +/* Keywords. */ + +%token CFG_ALGORITHM CFG_ENABLED CFG_NAME CFG_PARAMETERS CFG_PROGRAMPATH + +%token CFG_GLOBALOPTIONS CFG_COMPARISONTEST CFG_CONSISTENCYTEST + CFG_INTERACTIVE CFG_INTERSECTIONTEST CFG_MODELCHECK CFG_ROUNDS + CFG_VERBOSITY + +%token CFG_STATESPACEOPTIONS CFG_EDGEPROBABILITY CFG_PROPOSITIONS CFG_SIZE + CFG_TRUTHPROBABILITY CFG_CHANGEINTERVAL CFG_RANDOMSEED + +%token CFG_FORMULAOPTIONS CFG_ABBREVIATEDOPERATORS CFG_ANDPRIORITY + CFG_BEFOREPRIORITY CFG_DEFAULTOPERATORPRIORITY CFG_EQUIVALENCEPRIORITY + CFG_FALSEPRIORITY CFG_FINALLYPRIORITY CFG_GENERATEMODE + CFG_GLOBALLYPRIORITY CFG_IMPLICATIONPRIORITY CFG_NEXTPRIORITY + CFG_NOTPRIORITY CFG_ORPRIORITY CFG_OUTPUTMODE CFG_PROPOSITIONPRIORITY + CFG_RELEASEPRIORITY CFG_STRONGRELEASEPRIORITY CFG_TRUEPRIORITY + CFG_UNTILPRIORITY CFG_WEAKUNTILPRIORITY CFG_XORPRIORITY + +/* Boolean constants. */ + +%token CFG_TRUTH_VALUE + +/* Interactivity mode constants. */ + +%token CFG_INTERACTIVITY_VALUE + +/* Formula output/generation mode constants. */ + +%token CFG_FORMULA_MODE_VALUE + +/* Statespace generation mode constants. */ + +%token CFG_STATESPACE_MODE_VALUE + +/* Constants controlling the product space computation. */ + +%token CFG_PRODUCT_TYPE_VALUE + +/* Numbers. */ + +%token CFG_INTEGER +%token CFG_REAL + +/* Intervals of integers. */ + +%token CFG_INTEGER_INTERVAL + +/* String constants. */ + +%token CFG_STRING_CONSTANT + +/* Punctuation symbols. */ + +%token CFG_LBRACE CFG_RBRACE CFG_EQUALS +%token CFG_BLOCK_ID, CFG_OPTION_ID + +/* The `unknown' token. */ + +%token CFG_UNKNOWN + + + +/****************************************************************************** + * + * Grammar rule definitions. + * + *****************************************************************************/ + +%% + +configuration_file: configuration_blocks + ; + +configuration_blocks: /* empty */ + | configuration_blocks + { + expected_token = CFG_BLOCK_ID; + } + configuration_block + ; + +configuration_block: algorithm_option_block + | global_option_block + | statespace_option_block + | formula_option_block + ; + +algorithm_option_block: CFG_ALGORITHM + { + current_block_type = CFG_ALGORITHM; + + algorithm_begin_line = config_file_line_number; + + algorithm_information.name = 0; + algorithm_information.path_to_program = 0; + algorithm_information.extra_parameters = 0; + algorithm_information.enabled = true; + + expected_token = CFG_LBRACE; + } + CFG_LBRACE algorithm_options CFG_RBRACE + { + if (algorithm_information.name == 0) + { + algorithm_information.name = + new string("Algorithm "); + algorithm_information.name->append + (::StringUtil::toString + (parser_cfg->algorithms.size())); + } + parser_cfg->algorithms.push_back + (algorithm_information); + if (algorithm_information.path_to_program == 0) + { + throw Configuration::ConfigurationException + (::StringUtil::toString + (algorithm_begin_line) + + "--" + + ::StringUtil::toString + (config_file_line_number), + "missing path to executable (`" + + *(algorithm_information.name) + + "')"); + } + } + ; + +algorithm_options: /* empty */ + | algorithm_options + { + expected_token = CFG_OPTION_ID; + } + algorithm_option + ; + +algorithm_option: CFG_ENABLED + { + current_option_type = CFG_ENABLED; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_TRUTH_VALUE; + } + CFG_TRUTH_VALUE + { + algorithm_information.enabled = $5; + } + + | CFG_NAME + { + current_option_type = CFG_NAME; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_STRING_CONSTANT; + } + CFG_STRING_CONSTANT + { + algorithm_information.name = $5; + } + + | CFG_PARAMETERS + { + current_option_type = CFG_PARAMETERS; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_STRING_CONSTANT; + } + CFG_STRING_CONSTANT + { + algorithm_information.extra_parameters = $5; + } + + | CFG_PROGRAMPATH + { + current_option_type = CFG_PROGRAMPATH; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_STRING_CONSTANT; + } + CFG_STRING_CONSTANT + { + algorithm_information.path_to_program = $5; + } + ; + +global_option_block: CFG_GLOBALOPTIONS + { + current_block_type = CFG_GLOBALOPTIONS; + expected_token = CFG_LBRACE; + } + CFG_LBRACE global_options CFG_RBRACE + ; + +global_options: /* empty */ + | global_options + { + expected_token = CFG_OPTION_ID; + } + global_option + ; + +global_option: CFG_COMPARISONTEST + { + current_option_type = CFG_COMPARISONTEST; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_TRUTH_VALUE; + } + CFG_TRUTH_VALUE + { + if (!isLocked(CFG_COMPARISONTEST)) + parser_cfg->global_options.do_comp_test = $5; + } + + | CFG_CONSISTENCYTEST + { + current_option_type = CFG_CONSISTENCYTEST; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_TRUTH_VALUE; + } + CFG_TRUTH_VALUE + { + if (!isLocked(CFG_CONSISTENCYTEST)) + parser_cfg->global_options.do_cons_test = $5; + } + + | CFG_INTERACTIVE + { + current_option_type = CFG_INTERACTIVE; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTERACTIVITY_VALUE; + } + CFG_INTERACTIVITY_VALUE + { + if (!isLocked(CFG_INTERACTIVE)) + parser_cfg->global_options.interactive = $5; + } + + | CFG_INTERSECTIONTEST + { + current_option_type = CFG_INTERSECTIONTEST; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_TRUTH_VALUE; + } + CFG_TRUTH_VALUE + { + if (!isLocked(CFG_INTERSECTIONTEST)) + parser_cfg->global_options.do_intr_test = $5; + } + + | CFG_MODELCHECK + { + current_option_type = CFG_MODELCHECK; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_PRODUCT_TYPE_VALUE; + } + CFG_PRODUCT_TYPE_VALUE + { + if (!isLocked(CFG_MODELCHECK)) + parser_cfg->global_options.product_mode = $5; + } + + | CFG_ROUNDS + { + current_option_type = CFG_ROUNDS; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_ROUNDS)) + { + checkIntegerRange + ($5, Configuration::ROUND_COUNT_RANGE, + true); + parser_cfg->global_options.number_of_rounds + = $5; + } + } + + | CFG_VERBOSITY + { + current_option_type = CFG_VERBOSITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_VERBOSITY)) + { + checkIntegerRange + ($5, Configuration::VERBOSITY_RANGE, true); + parser_cfg->global_options.verbosity = $5; + } + } + ; + +statespace_option_block: CFG_STATESPACEOPTIONS + { + current_block_type = CFG_STATESPACEOPTIONS; + expected_token = CFG_LBRACE; + } + CFG_LBRACE statespace_options CFG_RBRACE + ; + +statespace_options: /* empty */ + | statespace_options + { + expected_token = CFG_OPTION_ID; + } + statespace_option + ; + +statespace_option: CFG_CHANGEINTERVAL + { + current_option_type = CFG_CHANGEINTERVAL; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_CHANGEINTERVAL)) + { + checkIntegerRange + ($5, Configuration::GENERATION_RANGE, true); + parser_cfg->global_options. + statespace_change_interval = $5; + } + } + + | CFG_EDGEPROBABILITY + { + current_option_type = CFG_EDGEPROBABILITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_REAL; + } + CFG_REAL + { + if (!isLocked(CFG_EDGEPROBABILITY)) + { + checkProbability($5); + parser_cfg->statespace_generator. + edge_probability = $5; + } + } + + | CFG_GENERATEMODE + { + current_option_type = CFG_GENERATEMODE; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_STATESPACE_MODE_VALUE; + } + CFG_STATESPACE_MODE_VALUE + { + if (!isLocked(CFG_GENERATEMODE)) + parser_cfg->global_options. + statespace_generation_mode = $5; + } + + | CFG_PROPOSITIONS + { + current_option_type = CFG_PROPOSITIONS; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_PROPOSITIONS)) + { + checkIntegerRange + ($5, Configuration::PROPOSITION_COUNT_RANGE, + true); + parser_cfg->statespace_generator. + atoms_per_state = $5; + } + } + + | CFG_RANDOMSEED + { + current_option_type = CFG_RANDOMSEED; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_RANDOMSEED)) + parser_cfg->global_options. + statespace_random_seed = $5; + } + + | CFG_SIZE + { + current_option_type = CFG_SIZE; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER_INTERVAL; + } + statespace_size + + | CFG_TRUTHPROBABILITY + { + current_option_type = CFG_TRUTHPROBABILITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_REAL; + } + CFG_REAL + { + if (!isLocked(CFG_TRUTHPROBABILITY)) + { + checkProbability($5); + parser_cfg->statespace_generator. + truth_probability = $5; + } + } + ; + +formula_option_block: CFG_FORMULAOPTIONS + { + current_block_type = CFG_FORMULAOPTIONS; + expected_token = CFG_LBRACE; + } + CFG_LBRACE formula_options CFG_RBRACE + ; + +formula_options: /* empty */ + | formula_options + { + expected_token = CFG_OPTION_ID; + } + formula_option + ; + +formula_option: CFG_ABBREVIATEDOPERATORS + { + current_option_type = CFG_ABBREVIATEDOPERATORS; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_TRUTH_VALUE; + } + CFG_TRUTH_VALUE + { + if (!isLocked(CFG_ABBREVIATEDOPERATORS)) + parser_cfg->formula_options. + allow_abbreviated_operators = $5; + } + + | CFG_ANDPRIORITY + { + current_option_type = CFG_ANDPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_ANDPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_CONJUNCTION] = $5; + } + } + + | CFG_BEFOREPRIORITY + { + current_option_type = CFG_BEFOREPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_BEFOREPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_BEFORE] = $5; + } + } + + | CFG_CHANGEINTERVAL + { + current_option_type = CFG_CHANGEINTERVAL; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_CHANGEINTERVAL)) + { + checkIntegerRange + ($5, Configuration::GENERATION_RANGE, true); + parser_cfg->global_options. + formula_change_interval = $5; + } + } + + | CFG_DEFAULTOPERATORPRIORITY + { + current_option_type + = CFG_DEFAULTOPERATORPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_DEFAULTOPERATORPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options. + default_operator_priority = $5; + } + } + + | CFG_EQUIVALENCEPRIORITY + { + current_option_type = CFG_EQUIVALENCEPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_EQUIVALENCEPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_EQUIVALENCE] = $5; + } + } + + | CFG_FALSEPRIORITY + { + current_option_type = CFG_FALSEPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_FALSEPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_FALSE] = $5; + } + } + + | CFG_FINALLYPRIORITY + { + current_option_type = CFG_FINALLYPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_FINALLYPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_FINALLY] = $5; + } + } + + | CFG_GENERATEMODE + { + current_option_type = CFG_GENERATEMODE; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_FORMULA_MODE_VALUE; + } + CFG_FORMULA_MODE_VALUE + { + if (!isLocked(CFG_GENERATEMODE)) + parser_cfg->formula_options.generate_mode + = $5; + } + + | CFG_GLOBALLYPRIORITY + { + current_option_type = CFG_GLOBALLYPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_GLOBALLYPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_GLOBALLY] = $5; + } + } + + | CFG_IMPLICATIONPRIORITY + { + current_option_type = CFG_IMPLICATIONPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_IMPLICATIONPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_IMPLICATION] = $5; + } + } + + | CFG_NEXTPRIORITY + { + current_option_type = CFG_NEXTPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_NEXTPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_NEXT] = $5; + } + } + + | CFG_NOTPRIORITY + { + current_option_type = CFG_NOTPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_NOTPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_NEGATION] = $5; + } + } + + | CFG_ORPRIORITY + { + current_option_type = CFG_ORPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_ORPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_DISJUNCTION] = $5; + } + } + + | CFG_OUTPUTMODE + { + current_option_type = CFG_OUTPUTMODE; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_FORMULA_MODE_VALUE; + } + CFG_FORMULA_MODE_VALUE + { + if (!isLocked(CFG_OUTPUTMODE)) + parser_cfg->formula_options.output_mode = $5; + } + + | CFG_PROPOSITIONPRIORITY + { + current_option_type = CFG_PROPOSITIONPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_PROPOSITIONPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_ATOM] = $5; + } + } + + | CFG_PROPOSITIONS + { + current_option_type = CFG_PROPOSITIONS; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_PROPOSITIONS)) + { + checkIntegerRange + ($5, Configuration::PROPOSITION_COUNT_RANGE, + true); + parser_cfg->formula_options. + formula_generator. + number_of_available_variables = $5; + } + } + + | CFG_RANDOMSEED + { + current_option_type = CFG_RANDOMSEED; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_RANDOMSEED)) + parser_cfg->global_options.formula_random_seed + = $5; + } + + | CFG_RELEASEPRIORITY + { + current_option_type = CFG_RELEASEPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_RELEASEPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_V] = $5; + } + } + + | CFG_SIZE + { + current_option_type = CFG_SIZE; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER_INTERVAL; + } + formula_size + + | CFG_STRONGRELEASEPRIORITY + { + current_option_type = CFG_STRONGRELEASEPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_STRONGRELEASEPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_STRONG_RELEASE] = $5; + } + } + + | CFG_TRUEPRIORITY + { + current_option_type = CFG_TRUEPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_TRUEPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_TRUE] = $5; + } + } + + | CFG_UNTILPRIORITY + { + current_option_type = CFG_UNTILPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_UNTILPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_UNTIL] = $5; + } + } + + | CFG_WEAKUNTILPRIORITY + { + current_option_type = CFG_WEAKUNTILPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_WEAKUNTILPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_WEAK_UNTIL] = $5; + } + } + + | CFG_XORPRIORITY + { + current_option_type = CFG_XORPRIORITY; + expected_token = CFG_EQUALS; + } + CFG_EQUALS + { + expected_token = CFG_INTEGER; + } + CFG_INTEGER + { + if (!isLocked(CFG_XORPRIORITY)) + { + checkIntegerRange + ($5, Configuration::PRIORITY_RANGE, true); + parser_cfg->formula_options.symbol_priority + [::Ltl::LTL_XOR] = $5; + } + } + ; + +formula_size: CFG_INTEGER + { + if (!isLocked(CFG_SIZE)) + { + checkIntegerRange + ($1, Configuration::FORMULA_SIZE_RANGE, + true); + parser_cfg->formula_options.formula_generator. + size = $1; + parser_cfg->formula_options.formula_generator. + max_size = $1; + } + } + + | CFG_INTEGER_INTERVAL + { + if (!isLocked(CFG_SIZE)) + { + checkIntegerRange + ($1.min, Configuration::FORMULA_SIZE_RANGE, + true); + + Configuration::IntegerRange max_len_range + (Configuration::FORMULA_MAX_SIZE_RANGE); + + max_len_range.min = $1.min; + + checkIntegerRange($1.max, max_len_range, + true); + + parser_cfg->formula_options.formula_generator. + size = $1.min; + parser_cfg->formula_options.formula_generator. + max_size = $1.max; + } + } + ; + +statespace_size: CFG_INTEGER + { + if (!isLocked(CFG_SIZE)) + { + checkIntegerRange + ($1, Configuration::STATESPACE_SIZE_RANGE, + true); + parser_cfg->statespace_generator.min_size + = $1; + parser_cfg->statespace_generator.max_size + = $1; + } + } + + | CFG_INTEGER_INTERVAL + { + if (!isLocked(CFG_SIZE)) + { + checkIntegerRange + ($1.min, + Configuration::STATESPACE_SIZE_RANGE, + true); + + Configuration::IntegerRange max_size_range + (Configuration::STATESPACE_MAX_SIZE_RANGE); + + checkIntegerRange($1.max, max_size_range, + true); + + parser_cfg->statespace_generator.min_size + = $1.min; + parser_cfg->statespace_generator.max_size + = $1.max; + } + } + ; + +%% + + + +/****************************************************************************** + * + * Main interface to the parser. + * + *****************************************************************************/ + +/* ========================================================================= */ +int parseConfiguration(FILE* stream, Configuration& configuration) +/* ---------------------------------------------------------------------------- + * + * Description: Main interface to the configuration file parser. Parses the + * configuration file and stores the results into the given + * Configuration object. + * + * Arguments: stream -- A pointer to a file from which the + * configuration should be read. The file is + * assumed to be open for reading. + * configuration -- A reference to a Configuration object in + * which the configuration should be stored. + * + * Returns: + * + * ------------------------------------------------------------------------- */ +{ + yyrestart(stream); + parser_cfg = &configuration; + config_file_line_number = 1; + return yyparse(); +} diff --git a/lbtt/src/Configuration.cc b/lbtt/src/Configuration.cc new file mode 100644 index 000000000..8392ca33f --- /dev/null +++ b/lbtt/src/Configuration.cc @@ -0,0 +1,1991 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include "Configuration.h" +#ifdef HAVE_GETOPT_LONG +#include +#define OPTIONSTRUCT struct option +#else +#include "gnu-getopt.h" +#define optarg gnu_optarg +#define optind gnu_optind +#define opterr gnu_opterr +#define OPTIONSTRUCT struct gnu_option +#define getopt_long gnu_getopt_long +#endif /* HAVE_GETOPT_LONG */ + +/****************************************************************************** + * + * Declarations for functions and variables provided by the parser. + * + *****************************************************************************/ + +#include "Config-parse.h" /* Include declarations for + * the tokens that may be + * present in the + * configuration file. + */ + +extern int parseConfiguration /* Parser interface. */ + (FILE*, Configuration&); + +extern void checkIntegerRange /* Range checking */ + (long int, /* functions. */ + const struct Configuration::IntegerRange&, + bool); + +extern void checkProbability(double, bool); + + + +/****************************************************************************** + * + * Definitions for ranges of certain integer-valued configuration options. + * + *****************************************************************************/ + +const struct Configuration::IntegerRange Configuration::VERBOSITY_RANGE + = {0, 5, "verbosity must be between 0 and 5 (inclusive)"}; + +const struct Configuration::IntegerRange Configuration::ROUND_COUNT_RANGE + = {1, LONG_MAX, "number of rounds must be positive"}; + +const struct Configuration::IntegerRange Configuration::GENERATION_RANGE + = {0, LONG_MAX, "length of interval must be nonnegative"}; + +const struct Configuration::IntegerRange Configuration::PRIORITY_RANGE + = {0, INT_MAX / 14, "priority value out of range"}; + +const struct Configuration::IntegerRange + Configuration::PROPOSITION_COUNT_RANGE + = {0, LONG_MAX, "number of propositions must be nonnegative"}; + +const struct Configuration::IntegerRange Configuration::FORMULA_SIZE_RANGE + = {1, LONG_MAX, "formula size must be always positive"}; + +const struct Configuration::IntegerRange + Configuration::FORMULA_MAX_SIZE_RANGE + = {1, LONG_MAX, "minimum formula size exceeds the maximum formula size"}; + +const struct Configuration::IntegerRange Configuration::STATESPACE_SIZE_RANGE + = {1, LONG_MAX, "state space size must be always positive"}; + +const struct Configuration::IntegerRange + Configuration::STATESPACE_MAX_SIZE_RANGE + = {1, LONG_MAX, "minimum state space size exceeds the maximum state space " + "size"}; + + + +/****************************************************************************** + * + * Function definitions for class Configuration. + * + *****************************************************************************/ + +/* ========================================================================= */ +Configuration::Configuration() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Configuration. Creates a new data + * structure for storing program configuration and initializes + * the configuration parameters to their default values. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + reset(); +} + +/* ========================================================================= */ +Configuration::~Configuration() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Configuration. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + for (vector + ::const_iterator it = algorithms.begin(); + it != algorithms.end(); ++it) + { + delete it->name; + delete it->path_to_program; + delete it->extra_parameters; + } +} + +/* ========================================================================= */ +void Configuration::read(int argc, char* argv[]) +/* ---------------------------------------------------------------------------- + * + * Description: Parses the command line parameters passed to the program and + * reads the configuration file. + * + * Arguments: argc -- (Number of command line parameters) + 1. + * argv -- Array of C-style strings storing the parameters. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + using namespace ::StringUtil; + + reset(); + + /* + * Command line option declarations. + */ + + static OPTIONSTRUCT command_line_options[] = + { + {"comparisontest", no_argument, 0, OPT_COMPARISONTEST}, + {"comparisoncheck", no_argument, 0, OPT_COMPARISONTEST}, + {"configfile", required_argument, 0, OPT_CONFIGFILE}, + {"consistencytest", no_argument, 0, OPT_CONSISTENCYTEST}, + {"consistencycheck", no_argument, 0, OPT_CONSISTENCYTEST}, + {"disable", required_argument, 0, OPT_DISABLE}, + {"enable", required_argument, 0, OPT_ENABLE}, + {"formulachangeinterval", required_argument, 0, OPT_FORMULACHANGEINTERVAL}, + {"formulafile", required_argument, 0, OPT_FORMULAFILE}, + {"formularandomseed", required_argument, 0, OPT_FORMULARANDOMSEED}, + {"globalmodelcheck", no_argument, 0, OPT_GLOBALPRODUCT}, + {"help", no_argument, 0, OPT_HELP}, + {"interactive", required_argument, 0, OPT_INTERACTIVE}, + {"intersectiontest", no_argument, 0, OPT_INTERSECTIONTEST}, + {"intersectioncheck", no_argument, 0, OPT_INTERSECTIONTEST}, + {"localmodelcheck", no_argument, 0, OPT_LOCALPRODUCT}, + {"logfile", required_argument, 0, OPT_LOGFILE}, + {"modelcheck", required_argument, 0, OPT_MODELCHECK}, + {"nocomparisontest", no_argument, 0, OPT_NOCOMPARISONTEST}, + {"nocomparisoncheck", no_argument, 0, OPT_NOCOMPARISONTEST}, + {"noconsistencytest", no_argument, 0, OPT_NOCONSISTENCYTEST}, + {"noconsistencycheck", no_argument, 0, OPT_NOCONSISTENCYTEST}, + {"nointersectiontest", no_argument, 0, OPT_NOINTERSECTIONTEST}, + {"nointersectioncheck", no_argument, 0, OPT_NOINTERSECTIONTEST}, + {"nopause", no_argument, 0, OPT_NOPAUSE}, + {"pause", no_argument, 0, OPT_PAUSE}, + {"pauseonerror", no_argument, 0, OPT_PAUSEONERROR}, + {"profile", no_argument, 0, OPT_PROFILE}, + {"quiet", no_argument, 0, OPT_QUIET}, + {"rounds", required_argument, 0, OPT_ROUNDS}, + {"showconfig", no_argument, 0, OPT_SHOWCONFIG}, + {"showoperatordistribution", no_argument, 0, OPT_SHOWOPERATORDISTRIBUTION}, + {"silent", no_argument, 0, OPT_QUIET}, + {"skip", required_argument, 0, OPT_SKIP}, + {"statespacechangeinterval", required_argument, 0, + OPT_STATESPACECHANGEINTERVAL}, + {"statespacerandomseed", required_argument, 0, OPT_STATESPACERANDOMSEED}, + {"verbosity", required_argument, 0, OPT_VERBOSITY}, + {"version", no_argument, 0, OPT_VERSION}, + + {"abbreviatedoperators", no_argument, 0, OPT_ABBREVIATEDOPERATORS}, + {"andpriority", required_argument, 0, OPT_ANDPRIORITY}, + {"beforepriority", required_argument, 0, OPT_BEFOREPRIORITY}, + {"defaultoperatorpriority", required_argument, 0, + OPT_DEFAULTOPERATORPRIORITY}, + {"equivalencepriority", required_argument, 0, OPT_EQUIVALENCEPRIORITY}, + {"falsepriority", required_argument, 0, OPT_FALSEPRIORITY}, + {"finallypriority", required_argument, 0, OPT_FINALLYPRIORITY}, + {"formulageneratemode", required_argument, 0, OPT_FORMULAGENERATEMODE}, + {"formulaoutputmode", required_argument, 0, OPT_FORMULAOUTPUTMODE}, + {"formulapropositions", required_argument, 0, OPT_FORMULAPROPOSITIONS}, + {"formulasize", required_argument, 0, OPT_FORMULASIZE}, + {"generatennf", no_argument, 0, OPT_GENERATENNF}, + {"globallypriority", required_argument, 0, OPT_GLOBALLYPRIORITY}, + {"implicationpriority", required_argument, 0, OPT_IMPLICATIONPRIORITY}, + {"nextpriority", required_argument, 0, OPT_NEXTPRIORITY}, + {"noabbreviatedoperators", no_argument, 0, OPT_NOABBREVIATEDOPERATORS}, + {"nogeneratennf", no_argument, 0, OPT_NOGENERATENNF}, + {"nooutputnnf", no_argument, 0, OPT_NOOUTPUTNNF}, + {"notpriority", required_argument, 0, OPT_NOTPRIORITY}, + {"orpriority", required_argument, 0, OPT_ORPRIORITY}, + {"outputnnf", no_argument, 0, OPT_OUTPUTNNF}, + {"propositionpriority", required_argument, 0, OPT_PROPOSITIONPRIORITY}, + {"releasepriority", required_argument, 0, OPT_RELEASEPRIORITY}, + {"strongreleasepriority", required_argument, 0, OPT_STRONGRELEASEPRIORITY}, + {"truepriority", required_argument, 0, OPT_TRUEPRIORITY}, + {"untilpriority", required_argument, 0, OPT_UNTILPRIORITY}, + {"weakuntilpriority", required_argument, 0, OPT_WEAKUNTILPRIORITY}, + {"xorpriority", required_argument, 0, OPT_XORPRIORITY}, + + {"edgeprobability", required_argument, 0, OPT_EDGEPROBABILITY}, + {"enumeratedpath", no_argument, 0, OPT_ENUMERATEDPATH}, + {"randomconnectedgraph", no_argument, 0, OPT_RANDOMCONNECTEDGRAPH}, + {"randomgraph", no_argument, 0, OPT_RANDOMGRAPH}, + {"randompath", no_argument, 0, OPT_RANDOMPATH}, + {"statespacegeneratemode", required_argument, 0, + OPT_STATESPACEGENERATEMODE}, + {"statespacepropositions", required_argument, 0, + OPT_STATESPACEPROPOSITIONS}, + {"statespacesize", required_argument, 0, OPT_STATESPACESIZE}, + {"truthprobability", required_argument, 0, OPT_TRUTHPROBABILITY}, + + {0, 0, 0, 0} + }; + + opterr = 1; + + int opttype; + int option_index; + bool error = false, print_config = false, + print_operator_distribution = false; + + string enabled_or_disabled_algorithms[2]; + + locked_options.clear(); + + /* + * Read the command line parameters. + */ + + do + { + option_index = 0; + opttype = getopt_long(argc, argv, "h", command_line_options, + &option_index); + + switch (opttype) + { + case OPT_COMPARISONTEST : + case OPT_NOCOMPARISONTEST : + global_options.do_comp_test = (opttype == OPT_COMPARISONTEST); + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, + CFG_COMPARISONTEST)); + break; + + case OPT_CONFIGFILE : + global_options.cfg_filename = optarg; + break; + + case OPT_CONSISTENCYTEST : + case OPT_NOCONSISTENCYTEST : + global_options.do_cons_test = (opttype == OPT_CONSISTENCYTEST); + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, + CFG_CONSISTENCYTEST)); + break; + + case OPT_DISABLE : + case OPT_ENABLE : + { + int i = (opttype == OPT_DISABLE ? 1 : 0); + + if (!enabled_or_disabled_algorithms[i].empty()) + enabled_or_disabled_algorithms[i] += ','; + enabled_or_disabled_algorithms[i] += optarg; + } + break; + + case OPT_FORMULACHANGEINTERVAL : + case OPT_STATESPACECHANGEINTERVAL : + { + long int interval_length + = parseCommandLineInteger + (command_line_options[option_index].name, optarg); + checkIntegerRange(interval_length, GENERATION_RANGE, false); + + if (opttype == OPT_FORMULACHANGEINTERVAL) + { + global_options.formula_change_interval = interval_length; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_CHANGEINTERVAL)); + } + else + { + global_options.statespace_change_interval = interval_length; + locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, + CFG_CHANGEINTERVAL)); + } + } + + break; + + case OPT_FORMULAFILE : + global_options.formula_input_filename = optarg; + break; + + case OPT_FORMULARANDOMSEED : + global_options.formula_random_seed + = parseCommandLineInteger + (command_line_options[option_index].name, optarg); + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_RANDOMSEED)); + break; + + case OPT_GLOBALPRODUCT : + global_options.product_mode = GLOBAL; + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_MODELCHECK)); + break; + + case OPT_HELP : + showCommandLineHelp(argv[0]); + exit(0); + + case OPT_INTERACTIVE : + if (strcmp(optarg, "always") == 0) + global_options.interactive = ALWAYS; + else if (strcmp(optarg, "onerror") == 0) + global_options.interactive = ONERROR; + else if (strcmp(optarg, "never") == 0) + global_options.interactive = NEVER; + else + error = true; + + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_INTERACTIVE)); + break; + + case OPT_INTERSECTIONTEST : + case OPT_NOINTERSECTIONTEST : + global_options.do_intr_test = (opttype == OPT_INTERSECTIONTEST); + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, + CFG_INTERSECTIONTEST)); + break; + + case OPT_LOCALPRODUCT : + global_options.product_mode = LOCAL; + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_MODELCHECK)); + break; + + case OPT_LOGFILE : + global_options.transcript_filename = optarg; + break; + + case OPT_MODELCHECK : + if (strcmp(optarg, "global") == 0) + global_options.product_mode = GLOBAL; + else if (strcmp(optarg, "local") == 0) + global_options.product_mode = LOCAL; + else + error = true; + + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_MODELCHECK)); + break; + + case OPT_PAUSE : + case OPT_NOPAUSE : + global_options.interactive = (opttype == OPT_PAUSE ? ALWAYS : NEVER); + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_INTERACTIVE)); + break; + + case OPT_PAUSEONERROR : + global_options.interactive = ONERROR; + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_INTERACTIVE)); + break; + + case OPT_PROFILE : + global_options.do_comp_test + = global_options.do_cons_test + = global_options.do_intr_test + = false; + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, + CFG_COMPARISONTEST)); + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, + CFG_CONSISTENCYTEST)); + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, + CFG_INTERSECTIONTEST)); + break; + + case OPT_QUIET : + global_options.verbosity = 0; + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_VERBOSITY)); + global_options.interactive = NEVER; + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_INTERACTIVE)); + break; + + case OPT_ROUNDS : + { + long int number_of_rounds + = parseCommandLineInteger + (command_line_options[option_index].name, optarg); + checkIntegerRange(number_of_rounds, ROUND_COUNT_RANGE, false); + global_options.number_of_rounds = number_of_rounds; + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_ROUNDS)); + } + + break; + + case OPT_SHOWCONFIG : + print_config = true; + break; + + case OPT_SHOWOPERATORDISTRIBUTION : + print_operator_distribution = true; + break; + + case OPT_SKIP : + { + long int rounds_to_skip + = parseCommandLineInteger + (command_line_options[option_index].name, optarg); + checkIntegerRange(rounds_to_skip, ROUND_COUNT_RANGE, false); + global_options.init_skip = rounds_to_skip; + } + + break; + + case OPT_STATESPACERANDOMSEED : + global_options.statespace_random_seed + = parseCommandLineInteger + (command_line_options[option_index].name, optarg); + locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, + CFG_RANDOMSEED)); + break; + + case OPT_VERBOSITY : + { + long int verbosity + = parseCommandLineInteger + (command_line_options[option_index].name, optarg); + checkIntegerRange(verbosity, VERBOSITY_RANGE, false); + global_options.verbosity = verbosity; + locked_options.insert(make_pair(CFG_GLOBALOPTIONS, CFG_VERBOSITY)); + } + + break; + + case OPT_VERSION : + cout << "lbtt " PACKAGE_VERSION "\n" + "lbtt is free software; you may change and " + "redistribute it under the terms of\n" + "the GNU General Public License. lbtt comes with " + "NO WARRANTY. See the file\n" + "COPYING for details.\n"; + exit(0); + break; + + case OPT_ABBREVIATEDOPERATORS : + case OPT_NOABBREVIATEDOPERATORS : + formula_options.allow_abbreviated_operators + = (opttype == OPT_ABBREVIATEDOPERATORS); + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_ABBREVIATEDOPERATORS)); + break; + + case OPT_ANDPRIORITY : + case OPT_BEFOREPRIORITY : + case OPT_DEFAULTOPERATORPRIORITY : + case OPT_EQUIVALENCEPRIORITY : + case OPT_FALSEPRIORITY : + case OPT_FINALLYPRIORITY : + case OPT_GLOBALLYPRIORITY : + case OPT_IMPLICATIONPRIORITY : + case OPT_NEXTPRIORITY : + case OPT_NOTPRIORITY : + case OPT_ORPRIORITY : + case OPT_PROPOSITIONPRIORITY : + case OPT_RELEASEPRIORITY : + case OPT_STRONGRELEASEPRIORITY : + case OPT_TRUEPRIORITY : + case OPT_UNTILPRIORITY : + case OPT_WEAKUNTILPRIORITY : + case OPT_XORPRIORITY : + { + long int priority + = parseCommandLineInteger + (command_line_options[option_index].name, optarg); + + checkIntegerRange(priority, PRIORITY_RANGE, false); + + int symbol = -1; + + switch (opttype) + { + case OPT_ANDPRIORITY : + symbol = ::Ltl::LTL_CONJUNCTION; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_ANDPRIORITY)); + break; + + case OPT_BEFOREPRIORITY : + symbol = ::Ltl::LTL_BEFORE; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_BEFOREPRIORITY)); + break; + + case OPT_DEFAULTOPERATORPRIORITY : + formula_options.default_operator_priority = priority; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_DEFAULTOPERATORPRIORITY)); + break; + + case OPT_EQUIVALENCEPRIORITY : + symbol = ::Ltl::LTL_EQUIVALENCE; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_EQUIVALENCEPRIORITY)); + break; + + case OPT_FALSEPRIORITY : + symbol = ::Ltl::LTL_FALSE; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_FALSEPRIORITY)); + break; + + case OPT_FINALLYPRIORITY : + symbol = ::Ltl::LTL_FINALLY; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_FINALLYPRIORITY)); + break; + + case OPT_GLOBALLYPRIORITY : + symbol = ::Ltl::LTL_GLOBALLY; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_GLOBALLYPRIORITY)); + break; + + case OPT_IMPLICATIONPRIORITY : + symbol = ::Ltl::LTL_IMPLICATION; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_IMPLICATIONPRIORITY)); + break; + + case OPT_NEXTPRIORITY : + symbol = ::Ltl::LTL_NEXT; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_NEXTPRIORITY)); + break; + + case OPT_NOTPRIORITY : + symbol = ::Ltl::LTL_NEGATION; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_NOTPRIORITY)); + break; + + case OPT_ORPRIORITY : + symbol = ::Ltl::LTL_DISJUNCTION; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_ORPRIORITY)); + break; + + case OPT_PROPOSITIONPRIORITY : + symbol = ::Ltl::LTL_ATOM; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_PROPOSITIONPRIORITY)); + break; + + case OPT_RELEASEPRIORITY : + symbol = ::Ltl::LTL_V; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_RELEASEPRIORITY)); + break; + + case OPT_STRONGRELEASEPRIORITY : + symbol = ::Ltl::LTL_STRONG_RELEASE; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_STRONGRELEASEPRIORITY)); + break; + + case OPT_TRUEPRIORITY : + symbol = ::Ltl::LTL_TRUE; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_TRUEPRIORITY)); + break; + + case OPT_UNTILPRIORITY : + symbol = ::Ltl::LTL_UNTIL; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_UNTILPRIORITY)); + break; + + case OPT_WEAKUNTILPRIORITY : + symbol = ::Ltl::LTL_WEAK_UNTIL; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_WEAKUNTILPRIORITY)); + break; + + case OPT_XORPRIORITY : + symbol = ::Ltl::LTL_XOR; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_XORPRIORITY)); + + default : + break; + } + + if (symbol != -1) + formula_options.symbol_priority[symbol] = priority; + } + + break; + + case OPT_FORMULAGENERATEMODE : + if (strcmp(optarg, "nnf") == 0) + formula_options.generate_mode = NNF; + else if (strcmp(optarg, "normal") == 0) + formula_options.generate_mode = NORMAL; + else + error = true; + + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_GENERATEMODE)); + break; + + case OPT_FORMULAOUTPUTMODE : + if (strcmp(optarg, "nnf") == 0) + formula_options.output_mode = NNF; + else if (strcmp(optarg, "normal") == 0) + formula_options.output_mode = NORMAL; + else + error = true; + + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_OUTPUTMODE)); + break; + + case OPT_FORMULAPROPOSITIONS : + case OPT_STATESPACEPROPOSITIONS : + { + long int num_propositions + = parseCommandLineInteger + (command_line_options[option_index].name, optarg); + + checkIntegerRange(num_propositions, PROPOSITION_COUNT_RANGE, false); + + if (opttype == OPT_STATESPACEPROPOSITIONS) + { + locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, + CFG_PROPOSITIONS)); + statespace_generator.atoms_per_state = num_propositions; + } + else + { + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, + CFG_PROPOSITIONS)); + formula_options.formula_generator.number_of_available_variables + = num_propositions; + } + } + + break; + + case OPT_FORMULASIZE : + case OPT_STATESPACESIZE : + { + IntegerRange min_size_range, max_size_range; + + if (opttype == OPT_FORMULASIZE) + { + min_size_range = FORMULA_SIZE_RANGE; + max_size_range = FORMULA_MAX_SIZE_RANGE; + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_SIZE)); + } + else + { + min_size_range = STATESPACE_SIZE_RANGE; + max_size_range = STATESPACE_MAX_SIZE_RANGE; + locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, CFG_SIZE)); + } + + string value(optarg); + string::size_type index = value.find("..."); + + if (index == string::npos) + { + long int size + = parseCommandLineInteger + (command_line_options[option_index].name, value); + + checkIntegerRange(size, min_size_range, false); + + if (opttype == OPT_FORMULASIZE) + formula_options.formula_generator.size + = formula_options.formula_generator.max_size + = size; + else + statespace_generator.min_size + = statespace_generator.max_size + = size; + } + else + { + string option_name(command_line_options[option_index].name); + + long int min = parseCommandLineInteger(option_name + " (min)", + value.substr(0, index)); + long int max = parseCommandLineInteger(option_name + " (max)", + value.substr(index + 3)); + + checkIntegerRange(min, min_size_range, false); + max_size_range.min = min; + checkIntegerRange(max, max_size_range, false); + + if (opttype == OPT_FORMULASIZE) + { + formula_options.formula_generator.size = min; + formula_options.formula_generator.max_size = max; + } + else + { + statespace_generator.min_size = min; + statespace_generator.max_size = max; + } + } + } + + break; + + case OPT_GENERATENNF : + case OPT_NOGENERATENNF : + formula_options.generate_mode + = (opttype == OPT_GENERATENNF ? NNF : NORMAL); + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_GENERATEMODE)); + break; + + case OPT_OUTPUTNNF : + case OPT_NOOUTPUTNNF : + formula_options.output_mode + = (opttype == OPT_OUTPUTNNF ? NNF : NORMAL); + locked_options.insert(make_pair(CFG_FORMULAOPTIONS, CFG_OUTPUTMODE)); + break; + + case OPT_EDGEPROBABILITY : + case OPT_TRUTHPROBABILITY : + { + char* endptr; + double probability = strtod(optarg, &endptr); + + if (*endptr != '\0') + probability = -1.0; + + checkProbability(probability, false); + + if (opttype == OPT_EDGEPROBABILITY) + { + statespace_generator.edge_probability = probability; + locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, + CFG_EDGEPROBABILITY)); + } + else + { + statespace_generator.truth_probability = probability; + locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, + CFG_TRUTHPROBABILITY)); + } + } + + break; + + case OPT_ENUMERATEDPATH : + global_options.statespace_generation_mode = ENUMERATEDPATH; + locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, + CFG_GENERATEMODE)); + break; + + case OPT_RANDOMCONNECTEDGRAPH : + global_options.statespace_generation_mode = RANDOMCONNECTEDGRAPH; + locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, + CFG_GENERATEMODE)); + break; + + case OPT_RANDOMGRAPH : + global_options.statespace_generation_mode = RANDOMGRAPH; + locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, + CFG_GENERATEMODE)); + break; + + case OPT_RANDOMPATH : + global_options.statespace_generation_mode = RANDOMPATH; + locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, + CFG_GENERATEMODE)); + break; + + case OPT_STATESPACEGENERATEMODE : + if (strcmp(optarg, "randomconnectedgraph") == 0) + global_options.statespace_generation_mode = RANDOMCONNECTEDGRAPH; + else if (strcmp(optarg, "randomgraph") == 0) + global_options.statespace_generation_mode = RANDOMGRAPH; + else if (strcmp(optarg, "randompath") == 0) + global_options.statespace_generation_mode = RANDOMPATH; + else if (strcmp(optarg, "enumeratedpath") == 0) + global_options.statespace_generation_mode = ENUMERATEDPATH; + else + error = true; + + locked_options.insert(make_pair(CFG_STATESPACEOPTIONS, + CFG_GENERATEMODE)); + break; + + case '?' : + case ':' : + exit(-1); + } + + if (error) + throw ConfigurationException + ("", string("unrecognized argument (`") + optarg + + "') for option `--" + + command_line_options[option_index].name + "'"); + } + while (opttype != -1); + + if (optind != argc) + throw ConfigurationException + ("", string("unrecognized command line option `") + + argv[optind] + "'"); + + /* + * Read the configuration file. + */ + + FILE* configuration_file = fopen(global_options.cfg_filename.c_str(), "r"); + if (configuration_file == NULL) + throw ConfigurationException + ("", "error opening configuration file `" + + global_options.cfg_filename + "'"); + + try + { + parseConfiguration(configuration_file, *this); + fclose(configuration_file); + } + catch (const ConfigurationException&) + { + fclose(configuration_file); + throw; + } + + /* + * Use the information gathered from command line options to enable or + * disable some of the implementations. + */ + + set, ALLOC(unsigned long int) > + algorithm_id_set; + + for (int i = 0; i < 2; i++) + { + if (enabled_or_disabled_algorithms[i].empty()) + continue; + + try + { + parseInterval(enabled_or_disabled_algorithms[i], algorithm_id_set, 0, + algorithms.size() - 1); + + if (algorithm_id_set.empty()) + throw Exception(); + } + catch (const Exception&) + { + throw ConfigurationException("", + string("invalid argument (`") + + enabled_or_disabled_algorithms[i] + + "') for option `--" + + (i == 0 ? "en" : "dis") + + "able'"); + } + + for (set, + ALLOC(unsigned long int) >::const_iterator + id = algorithm_id_set.begin(); + id != algorithm_id_set.end(); + ++id) + { + if (*id >= algorithms.size()) + throw ConfigurationException + ("", + string("invalid implementation identifier (") + toString(*id) + + ") in the argument for `--" + + (i == 0 ? "en" : "dis") + + "able'"); + + algorithms[*id].enabled = (i == 0); + } + } + + /* + * Check that the values for configuration options are within acceptable + * limits. Initialize also the values of unspecified options to their + * default values. + */ + + /* + * Check that the number of rounds to run is greater than the number of + * rounds to skip. + */ + + if (global_options.number_of_rounds <= global_options.init_skip) + throw ConfigurationException + ("", "the argument for `--skip' must be less than the total " + "number of test rounds"); + + /* + * Check that there is at least one algorithm available for use. + */ + + if (algorithms.empty()) + throw ConfigurationException + ("", "no implementations defined in the configuration file"); + + /* + * The case where the number of available variables for propositional + * formulae is zero is equivalent to the case where propositional atoms are + * disallowed in the formulae altogether. + */ + + if (formula_options.formula_generator.number_of_available_variables == 0) + formula_options.symbol_priority[::Ltl::LTL_ATOM] = 0; + + /* + * Verify that at least one propositional symbol class (a Boolean constant + * or a propositional atom) has a nonzero priority. + */ + + if (formula_options.symbol_priority[::Ltl::LTL_ATOM] == 0 + && formula_options.symbol_priority[::Ltl::LTL_TRUE] == 0 + && formula_options.symbol_priority[::Ltl::LTL_FALSE] == 0) + throw ConfigurationException("", "at least one atomic symbol must have " + "nonzero priority"); + + /* + * If the operators ->, <->, xor, <>, [], W and M are disallowed, set their + * priorities to zero. + */ + + if (!formula_options.allow_abbreviated_operators) + { + formula_options.symbol_priority[::Ltl::LTL_IMPLICATION] = 0; + formula_options.symbol_priority[::Ltl::LTL_EQUIVALENCE] = 0; + formula_options.symbol_priority[::Ltl::LTL_XOR] = 0; + formula_options.symbol_priority[::Ltl::LTL_FINALLY] = 0; + formula_options.symbol_priority[::Ltl::LTL_GLOBALLY] = 0; + formula_options.symbol_priority[::Ltl::LTL_WEAK_UNTIL] = 0; + formula_options.symbol_priority[::Ltl::LTL_STRONG_RELEASE] = 0; + formula_options.symbol_priority[::Ltl::LTL_BEFORE] = 0; + } + + /* + * Check that at least one unary operator has a nonzero priority. + * Initialize the priority of the operators whose priority has not yet been + * specified to the default priority. + */ + + bool unary_operator_allowed = false; + + for (map, ALLOC(int) >::iterator + it = formula_options.symbol_priority.begin(); + it != formula_options.symbol_priority.end(); ++it) + { + if (it->second == -1) + it->second = formula_options.default_operator_priority; + + if (it->second > 0 && !unary_operator_allowed) + unary_operator_allowed = + (it->first == ::Ltl::LTL_NEGATION || it->first == ::Ltl::LTL_NEXT + || it->first == ::Ltl::LTL_FINALLY + || it->first == ::Ltl::LTL_GLOBALLY); + } + + if (!unary_operator_allowed) + throw ConfigurationException("", "at least one unary operator must have " + "a nonzero priority"); + + /* + * Initialize the random formula generator with priorities for the LTL + * formula symbols. + */ + + int total_short_unary_priority = 0; + int total_long_unary_priority = 0; + int total_binary_priority = 0; + + for (map, ALLOC(int) >::const_iterator + it = formula_options.symbol_priority.begin(); + it != formula_options.symbol_priority.end(); ++it) + { + if (it->second != 0) + { + switch (it->first) + { + case ::Ltl::LTL_ATOM : + case ::Ltl::LTL_TRUE : + case ::Ltl::LTL_FALSE : + formula_options.formula_generator.useSymbol(it->first, it->second); + break; + + case ::Ltl::LTL_NEGATION : + formula_options.formula_generator.useShortOperator + (it->first, it->second); + total_short_unary_priority += it->second; + if (formula_options.generate_mode != NNF) + { + formula_options.formula_generator.useLongOperator + (it->first, it->second); + total_long_unary_priority += it->second; + } + break; + + case ::Ltl::LTL_NEXT : + case ::Ltl::LTL_FINALLY : + case ::Ltl::LTL_GLOBALLY : + formula_options.formula_generator.useShortOperator + (it->first, it->second); + total_short_unary_priority += it->second; + formula_options.formula_generator.useLongOperator + (it->first, it->second); + total_long_unary_priority += it->second; + break; + + case ::Ltl::LTL_CONJUNCTION : + case ::Ltl::LTL_DISJUNCTION : + case ::Ltl::LTL_IMPLICATION : + case ::Ltl::LTL_EQUIVALENCE : + case ::Ltl::LTL_XOR : + case ::Ltl::LTL_UNTIL : + case ::Ltl::LTL_V : + case ::Ltl::LTL_WEAK_UNTIL : + case ::Ltl::LTL_STRONG_RELEASE : + case ::Ltl::LTL_BEFORE : + formula_options.formula_generator.useLongOperator + (it->first, it->second); + total_binary_priority += it->second; + break; + } + } + } + + if (print_operator_distribution + && global_options.formula_input_filename.empty()) + { + /* + * Compute the probability distribution for the operators used in random + * LTL formula generation. + */ + + ProbabilityMap result_cache; + + for (unsigned long int k = 1; + k <= formula_options.formula_generator.max_size; + k++) + { + for (map, ALLOC(int) >::const_iterator + op = formula_options.symbol_priority.begin(); + op != formula_options.symbol_priority.end(); + ++op) + { + if (op->second > 0) + { + switch (op->first) + { + case ::Ltl::LTL_ATOM : + case ::Ltl::LTL_TRUE : + case ::Ltl::LTL_FALSE : + break; + + default : + { + double probability = 0.0; + for (unsigned long int s + = formula_options.formula_generator.size; + s <= formula_options.formula_generator.max_size; + s++) + { + if (k >= s) + continue; + probability += operatorProbability + (op->first, k, s, + total_short_unary_priority, + total_long_unary_priority, + total_binary_priority, + result_cache); + } + probability /= static_cast + (formula_options.formula_generator.max_size + - formula_options.formula_generator.size + + 1); + + formula_options.symbol_distribution[op->first] + += k * probability; + + break; + } + } + } + } + } + } + + if (print_config) + { + print(cout); + exit(0); + } +} + +/* ========================================================================= */ +void Configuration::print(ostream& stream, int indent) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about the program configuration into a + * stream. + * + * Argument: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + using namespace ::StringUtil; + + Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); + + estream << string(indent, ' ') + "Program configuration:\n" + + string(indent, ' ') + string(22, '-') + "\n\n" + + string(indent + 2, ' ') + + toString(global_options.number_of_rounds) + + " test round" + + (global_options.number_of_rounds > 1 ? "s" : ""); + + if (global_options.init_skip > 0) + estream << " (of which the first " + + (global_options.init_skip > 1 + ? toString(global_options.init_skip) + " rounds " + : string("")) + + "will be skipped)"; + + estream << ".\n" + string(indent + 2, ' '); + + if (global_options.interactive == ALWAYS) + estream << "Pausing after every test round.\n"; + else if (global_options.interactive == ONERROR) + estream << "Testing will be interrupted in case of an error.\n"; + else + estream << "Running in batch mode.\n"; + + estream << string(indent + 2, ' ') + + "Using " + + (global_options.product_mode == GLOBAL + ? "global" : "local") + + " model checking for tests.\n"; + + if (!global_options.transcript_filename.empty()) + estream << string(indent + 2, ' ') + "Writing error log to `" + + global_options.transcript_filename + "'.\n"; + + estream << '\n' + string(indent + 2, ' ') + "Implementations:\n"; + + vector::size_type + algorithm_number = 0; + + for (vector + ::const_iterator a = algorithms.begin(); + a != algorithms.end(); + ++a) + { + estream << string(indent + 4, ' ') + algorithmString(algorithm_number); + + if (!a->enabled) + estream << " (disabled)"; + + estream << '\n'; + + algorithm_number++; + } + + estream << '\n' + string(indent + 2, ' '); + + if (global_options.do_comp_test || global_options.do_cons_test + || global_options.do_intr_test) + { + estream << "Enabled tests:\n"; + if (global_options.do_comp_test) + estream << string(indent + 4, ' ') + + "Model checking result cross-comparison test\n"; + if (global_options.do_cons_test) + estream << string(indent + 4, ' ') + + "Model checking result consistency check\n"; + if (global_options.do_intr_test) + estream << string(indent + 4, ' ') + + "Büchi automata intersection emptiness check\n"; + } + else + estream << "All automata correctness tests disabled.\n"; + + estream << '\n' + string(indent + 2, ' ') + "Random state spaces:\n" + + string(indent + 4, ' '); + + switch (global_options.statespace_generation_mode) + { + case RANDOMGRAPH : + estream << "Random graphs "; + break; + case RANDOMCONNECTEDGRAPH : + estream << "Random connected graphs "; + break; + case RANDOMPATH : + estream << "Random paths "; + break; + case ENUMERATEDPATH : + estream << "Enumerated paths "; + break; + default : + break; + } + + estream << '(' + toString(statespace_generator.min_size); + + if (statespace_generator.max_size != statespace_generator.min_size + && global_options.statespace_generation_mode != ENUMERATEDPATH) + estream << "..." + toString(statespace_generator.max_size); + + estream << string(" state") + + (statespace_generator.max_size == 1 ? "" : "s") + ", " + + toString(statespace_generator.atoms_per_state) + + " atomic proposition" + + (statespace_generator.atoms_per_state == 1 ? "" : "s") + + ")\n" + string(indent + 4, ' '); + + if (global_options.statespace_change_interval == 0 + || global_options.statespace_change_interval + >= global_options.number_of_rounds) + estream << "Using a fixed state space.\n" + string(indent + 4, ' '); + else + { + estream << "New state space will be generated after every "; + if (global_options.statespace_change_interval > 1) + { + estream << global_options.statespace_change_interval; + + if (global_options.statespace_change_interval % 100 < 10 + || global_options.statespace_change_interval % 100 >= 20) + { + switch (global_options.statespace_change_interval % 10) + { + case 1 : estream << "st"; break; + case 2 : estream << "nd"; break; + case 3 : estream << "rd"; break; + default : estream << "th"; break; + } + } + else + estream << "th"; + + estream << ' '; + } + + estream << "round.\n" + string(indent + 4, ' '); + } + + if (global_options.statespace_generation_mode != ENUMERATEDPATH) + { + estream << "Random seed: " + + toString(global_options.statespace_random_seed) + + '\n' + string(indent + 4, ' '); + + if (global_options.statespace_generation_mode & GRAPH) + estream << "Random edge probability: " + + toString(statespace_generator.edge_probability) + + '\n' + string(indent + 4, ' '); + + estream << "Propositional truth probability: " + + toString(statespace_generator.truth_probability) + + "\n"; + } + + estream << "\n" + string(indent + 2, ' '); + + if (global_options.formula_input_filename.empty()) + { + estream << "Random LTL formulas:\n" + string(indent + 4, ' ') + + toString(formula_options.formula_generator.size); + + if (formula_options.formula_generator.max_size + != formula_options.formula_generator.size) + estream << "..." + + toString(formula_options.formula_generator.max_size); + + estream << string(" parse tree node") + + (formula_options.formula_generator.max_size == 1 ? "" : "s") + + ", " + + toString(formula_options.formula_generator. + number_of_available_variables) + + " atomic proposition" + + (formula_options.formula_generator. + number_of_available_variables == 1 ? "" : "s"); + } + else + estream << "Reading LTL formulas from `" + + global_options.formula_input_filename + + "'."; + + estream << '\n' + string(indent + 4, ' '); + + if (global_options.formula_change_interval == 0 + || global_options.formula_change_interval + >= global_options.number_of_rounds) + estream << "Using a fixed LTL formula."; + else + { + estream << "New LTL formula will be " + << (global_options.formula_input_filename.empty() + ? "generate" + : "rea") + << "d after every "; + if (global_options.formula_change_interval > 1) + { + estream << global_options.formula_change_interval; + + if (global_options.formula_change_interval % 100 < 10 + || global_options.formula_change_interval % 100 >= 20) + { + switch (global_options.formula_change_interval % 10) + { + case 1 : estream << "st"; break; + case 2 : estream << "nd"; break; + case 3 : estream << "rd"; break; + default : estream << "th"; break; + } + } + else + estream << "th"; + + estream << ' '; + } + + estream << "round."; + } + + estream << '\n'; + + if (global_options.formula_input_filename.empty() + && formula_options.generate_mode == NNF) + estream << string(indent + 4, ' ') + + "Formulas will be generated into negation normal form.\n"; + else if (formula_options.output_mode == NNF) + estream << string(indent + 4, ' ') + + "Formulas will be converted into negation normal form.\n"; + + if (global_options.formula_input_filename.empty()) + { + estream << string(indent + 4, ' ') + "Random seed: " + + toString(global_options.formula_random_seed) + + '\n' + string(indent + 4, ' ') + + "Atomic symbols in use (priority):\n" + + string(indent + 6, ' '); + + bool first_printed = false; + + for (map, ALLOC(int) >::const_iterator + op = formula_options.symbol_priority.begin(); + op != formula_options.symbol_priority.end(); + ++op) + { + if ((op->first != ::Ltl::LTL_ATOM && op->first != ::Ltl::LTL_TRUE + && op->first != ::Ltl::LTL_FALSE) + || op->second == 0) + continue; + + if (first_printed) + estream << "; "; + + first_printed = true; + + switch (op->first) + { + case ::Ltl::LTL_ATOM : + estream << "propositions"; + break; + case ::Ltl::LTL_TRUE : case ::Ltl::LTL_FALSE : + estream << ::Ltl::infixSymbol(op->first); + break; + default : + break; + } + + estream << " (" << op->second << ')'; + } + + estream << '\n' + << string(indent + 4, ' ') + + "Operators used for random LTL formula generation:"; + + string operator_name_string; + string operator_priority_string; + string operator_distribution_string; + int number_of_operators_printed = 0; + int max_operators_per_line + = (formula_options.symbol_distribution.empty() ? 7 : 6); + + for (map, ALLOC(int) >::const_iterator op + = formula_options.symbol_priority.begin(); + op != formula_options.symbol_priority.end(); + ++op) + { + if (op->first == ::Ltl::LTL_ATOM || op->first == ::Ltl::LTL_TRUE + || op->first == ::Ltl::LTL_FALSE || op->second == 0) + continue; + + if (number_of_operators_printed % max_operators_per_line == 0) + { + operator_name_string = string(indent + 6, ' ') + "operator "; + operator_priority_string = string(indent + 6, ' ') + "priority "; + + if (!formula_options.symbol_distribution.empty()) + { + operator_name_string = string(11, ' ') + operator_name_string; + operator_priority_string = string(11, ' ') + + operator_priority_string; + operator_distribution_string + = string(indent + 6, ' ') + "occurrences/formula "; + } + } + + string symbol_string = ::Ltl::infixSymbol(op->first); + operator_name_string += symbol_string; + + string priority_string = ::StringUtil::toString(op->second); + operator_priority_string += priority_string; + + string distribution_string; + if (!formula_options.symbol_distribution.empty()) + { + distribution_string + = ::StringUtil::toString(formula_options.symbol_distribution. + find(op->first)->second, 3); + + operator_distribution_string += distribution_string; + } + + if (number_of_operators_printed % max_operators_per_line + == max_operators_per_line - 1) + { + estream << '\n' + operator_name_string + '\n' + + operator_priority_string + '\n'; + + if (!formula_options.symbol_distribution.empty()) + estream << operator_distribution_string + '\n'; + } + else + { + operator_name_string += string(9 - symbol_string.length(), ' '); + operator_priority_string += string(9 - priority_string.length(), ' '); + if (!formula_options.symbol_distribution.empty()) + operator_distribution_string + += string(9 - distribution_string.length(), ' '); + } + + number_of_operators_printed++; + } + + if (number_of_operators_printed % max_operators_per_line != 0) + { + estream << '\n' + operator_name_string + '\n' + operator_priority_string + + '\n'; + + if (!formula_options.symbol_distribution.empty()) + estream << operator_distribution_string + '\n'; + } + } + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +string Configuration::algorithmString + (vector::size_type + algorithm_id) const +/* ---------------------------------------------------------------------------- + * + * Description: Constructs a string with an algorithm identifer and the name + * of the algorithm in the form ": `'". + * + * Arguments: algorithm_id -- Numeric identifier for the algorithm. + * + * Returns: A string with the algorithm's id and name. + * + * ------------------------------------------------------------------------- */ +{ + using namespace ::StringUtil; + + return toString(algorithm_id) + ": `" + *(algorithms[algorithm_id].name) + + '\''; +} + +/* ========================================================================= */ +void Configuration::showCommandLineHelp(const char* program_name) +/* ---------------------------------------------------------------------------- + * + * Description: Prints the list of command line options. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + cout << string("Usage: ") + program_name + + " [OPTION]...\n\nGeneral options:\n" + " --[no]comparisontest Enable or disable the model " + "checking result\n" + " cross-comparison test\n" + " --configfile=FILE Read configuration from FILE\n" + " --[no]consistencytest Enable or disable the model " + "checking result\n" + " consistency test\n" + " --disable=IMPLEMENTATION-ID[,IMPLEMENTATION-ID...]\n" + " Exclude implementation(s) from " + "tests\n" + " --enable=IMPLEMENTATION-ID[,IMPLEMENTATION-ID,...]\n" + " Include implementation(s) into " + "tests\n" + " --formulafile=FILE Read LTL formulas from FILE\n" + " --globalmodelcheck Use global model checking in " + "tests\n" + " (equivalent to " + "`--modelcheck=global')\n" + " -h, --help Show this help and exit\n" + " --interactive=MODE Set the interactivity mode " + "(`always', `onerror', \n" + " `never')\n" + " --[no]intersectiontest Enable or disable the Büchi " + "automata\n" + " intersection emptiness test\n" + " --localmodelcheck Use local model checking in tests" + "\n" + " (equivalent to " + "`--modelcheck=local')\n" + " --logfile=FILE Write error log to FILE\n" + " --modelcheck=MODE Set model checking mode " + "(`global' or `local')\n" + " --nopause Do not pause between test rounds " + "(equivalent to\n" + " `--interactive=never')\n" + " --pause Pause unconditionally after every " + "test round\n" + " (equivalent to " + "`--interactive=always')\n" + " --pauseonerror Pause between test rounds only in " + "case of an\n" + " error (equivalent to " + "`--interactive=onerror')\n" + " --profile Disable all automata correctness " + "tests\n" + " --quiet, --silent Run all tests silently without " + "interruption\n" + " --rounds=NUMBER-OF-ROUNDS Set number of test rounds (1-)\n" + " --showconfig Display current configuration and " + "exit\n" + " --showoperatordistribution Display probability distribution " + "for LTL formula\n" + " operators\n" + " --skip=NUMBER-OF-ROUNDS Set number of test rounds to skip " + "before\n" + " starting tests\n" + " --verbosity=INTEGER Set the verbosity of output (0-5)\n" + " --version Display program version and exit" + "\n\n" + "LTL formula generation options:\n" + " --[no]abbreviatedoperators Allow or disallow operators ->, " + "<->, xor, <>,\n" + " [], u, w in the generated " + "formulas\n" + " --andpriority=INTEGER Set priority for the /\\ operator\n" + " --beforepriority=INTEGER Set priority for the Before " + "operator\n" + " --defaultoperatorpriority=INTEGER\n" + " Set default priority for operators" + "\n" + " --equivalencepriority=INTEGER\n" + " Set priority for the <-> operator\n" + " --falsepriority=INTEGER Set priority for the constant " + "`false'\n" + " --finallypriority=INTEGER Set priority for the <> operator\n" + " --formulachangeinterval=NUMBER-OF-ROUNDS\n" + " Set formula generation interval in " + "test rounds\n" + " (0-)\n" + " --formulageneratemode=MODE Set formula generation mode " + "(`normal', `nnf')\n" + " --formulaoutputmode=MODE Set formula output mode (`normal', " + "`nnf')\n" + " --formulapropositions=NUMBER-OF-PROPOSITIONS\n" + " Set maximum number of atomic " + "propositions in\n" + " generated formulas (0-)\n" + " --formularandomseed=INTEGER Set random seed for the formula " + "generation\n" + " algorithm\n" + " --formulasize=SIZE,\n" + " --formulasize=MIN-SIZE...MAX-SIZE\n" + " Set size of random LTL formulas " + "(1-)\n" + " --[no]generatennf Force or prevent generating LTL " + "formulas in\n" + " negation normal form\n" + " --globallypriority=INTEGER Set priority for the [] operator\n" + " --implicationpriority=INTEGER\n" + " Set priority for the -> operator\n" + " --nextpriority=INTEGER Set priority for the Next operator" + "\n" + " --notpriority=INTEGER Set priority for the negation " + "operator\n" + " --orpriority=INTEGER Set priority for the \\/ operator\n" + " --[no]outputnnf Enable or disable formula " + "translation to\n" + " negation normal form before " + "invoking the\n" + " translators\n" + " --propositionpriority=INTEGER\n" + " Set priority for atomic " + "propositions\n" + " --releasepriority=INTEGER Set priority for the (Weak) Release" + " operator\n" + " --strongreleasepriority=INTEGER\n" + " Set priority for the Strong " + "Release operator\n" + " --truepriority=INTEGER Set priority for the constant " + "`true'\n" + " --untilpriority=INTEGER Set priority for the (Strong) Until" + " operator\n" + " --weakuntilpriority=INTEGER\n" + " Set priority for the Weak Until " + "operator\n" + " --xorpriority=INTEGER Set priority for the xor " + "operator\n\n" + "State space generation options:\n" + " --edgeprobability=PROBABILITY\n" + " Set random edge probability for " + "state spaces\n" + " (0.0--1.0)\n" + " --enumeratedpath Enumerate all paths of a given " + "size and a given\n" + " number of propositions per state " + "(equivalent to\n" + " `--statespacegeneratemode=" + "enumeratedpath')\n" + " --randomconnectedgraph Generate connected graphs as state " + "spaces\n" + " (equivalent to\n" + " `--statespacegeneratemode=" + "randomconnectedgraph')\n" + " --randomgraph Generate random graphs as state " + "spaces\n" + " (equivalent to\n" + " `--statespacegeneratemode=" + "randomgraph')\n" + " --randompath Generate random paths as state " + "spaces\n" + " (equivalent to\n" + " `--statespacegeneratemode=" + "randompath')\n" + " --statespacechangeinterval=NUMBER-OF-ROUNDS\n" + " Set state space generation " + "interval in test\n" + " rounds (0-)\n" + " --statespacegeneratemode=MODE\n" + " Set state space generation mode\n" + " (`randomconnectedgraph', " + "`randomgraph',\n" + " `randompath', `enumeratedpath')\n" + " --statespacepropositions=NUMBER-OF-PROPOSITIONS\n" + " Set number of propositions per " + "state (0-)\n" + " --statespacerandomseed=INTEGER\n" + " Set random seed for the state " + "space generation\n" + " algorithm\n" + " --statespacesize=SIZE,\n" + " --statespacesize=MIN-SIZE...MAX-SIZE\n" + " Set size of generated state spaces " + "(1-)\n" + " --truthprobability=PROBABILITY\n" + " Set truth probability of " + "propositions (0.0--1.0)\n\n" + "Report bugs to .\n"; +} + +/* ========================================================================= */ +void Configuration::reset() +/* ---------------------------------------------------------------------------- + * + * Description: Resets the program configuration to the default + * configuration. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + global_options.verbosity = 3; + global_options.interactive = ALWAYS; + global_options.number_of_rounds = 10; + global_options.init_skip = 0; + global_options.statespace_change_interval = 1; + global_options.statespace_generation_mode = RANDOMCONNECTEDGRAPH; + global_options.formula_change_interval = 1; + global_options.product_mode = GLOBAL; + global_options.cfg_filename = "config"; + global_options.transcript_filename = ""; + global_options.formula_input_filename = ""; + global_options.do_comp_test = true; + global_options.do_cons_test = true; + global_options.do_intr_test = true; + global_options.statespace_random_seed = 1; + global_options.formula_random_seed = 1; + + formula_options.default_operator_priority = 0; + formula_options.symbol_priority.clear(); + formula_options.symbol_priority[::Ltl::LTL_ATOM] = 90; + formula_options.symbol_priority[::Ltl::LTL_TRUE] = 5; + formula_options.symbol_priority[::Ltl::LTL_FALSE] = 5; + formula_options.symbol_priority[::Ltl::LTL_CONJUNCTION] = -1; + formula_options.symbol_priority[::Ltl::LTL_DISJUNCTION] = -1; + formula_options.symbol_priority[::Ltl::LTL_UNTIL] = -1; + formula_options.symbol_priority[::Ltl::LTL_V] = -1; + formula_options.symbol_priority[::Ltl::LTL_WEAK_UNTIL] = -1; + formula_options.symbol_priority[::Ltl::LTL_STRONG_RELEASE] = -1; + formula_options.symbol_priority[::Ltl::LTL_BEFORE] = -1; + formula_options.symbol_priority[::Ltl::LTL_IMPLICATION] = -1; + formula_options.symbol_priority[::Ltl::LTL_EQUIVALENCE] = -1; + formula_options.symbol_priority[::Ltl::LTL_XOR] = -1; + formula_options.symbol_priority[::Ltl::LTL_NEGATION] = -1; + formula_options.symbol_priority[::Ltl::LTL_NEXT] = -1; + formula_options.symbol_priority[::Ltl::LTL_FINALLY] = -1; + formula_options.symbol_priority[::Ltl::LTL_GLOBALLY] = -1; + + formula_options.symbol_distribution.clear(); + + formula_options.allow_abbreviated_operators = true; + formula_options.output_mode = NORMAL; + formula_options.generate_mode = NORMAL; + + formula_options.formula_generator.reset(); + + statespace_generator.reset(); + + locked_options.clear(); +} + +/* ========================================================================= */ +long int Configuration::parseCommandLineInteger + (const string& option, const string& value) const +/* ---------------------------------------------------------------------------- + * + * Description: Converts an integer (given as a string) into a long int. Used + * when processing command line parameters. + * + * Arguments: option -- A reference to a constant string giving a name of + * a command line option. + * value -- A reference to a string which is supposed to + * contain an integer. + * + * Returns: The value of the integer. + * + * ------------------------------------------------------------------------- */ +{ + char* endptr; + long int val = strtol(value.c_str(), &endptr, 10); + + if (*endptr != '\0' || value.empty()) + throw ConfigurationException + ("", "the argument for `--" + option + "' must be a nonnegative " + "integer"); + + if (val == LONG_MIN || val == LONG_MAX) + throw ConfigurationException + ("", "the argument for `--" + option + "' is out of range"); + + return val; +} + +/* ========================================================================= */ +double Configuration::operatorProbability + (const int op, const int k, const int n, + const int total_short_unary_priority, const int total_long_unary_priority, + const int total_binary_priority, + ProbabilityMap& result_cache) const +/* ---------------------------------------------------------------------------- + * + * Description: Computes the probability with which a randomly generated + * formula of size `n' will contain exactly `k' occurrences of + * the operator `op'. + * + * Arguments: op -- Operator type identifier. + * k -- Number of occurrences of `op' + * in a formula. + * n -- Formula size. + * total_short_unary_priority -- Combined priority of all + * unary operators allowed in + * formulae of size 2. + * total_long_unary_priority -- Combined priority of all + * unary operators allowed in + * formulae of size greater than + * 2. + * total_binary_priority -- Combined priority of all + * binary operators (allowed in + * formulae of size greater than + * 2). + * result_cache -- Data structure for storing + * intermediate results. + * + * Returns: The probability with which a randomly generated formula of + * size `n' will contain exactly `k' occurrences of the operator + * `op'. + * + * ------------------------------------------------------------------------- */ +{ + double result; + + int arity; + int priority = formula_options.symbol_priority.find(op)->second; + + ProbabilityMap::const_iterator check_op(result_cache.find(op)); + if (check_op != result_cache.end()) + { + ProbabilityMapElement::const_iterator check_p + (check_op->second.find(make_pair(k, n))); + if (check_p != check_op->second.end()) + return check_p->second; + } + + switch (op) + { + case ::Ltl::LTL_NEGATION : + case ::Ltl::LTL_NEXT : + case ::Ltl::LTL_FINALLY : + case ::Ltl::LTL_GLOBALLY : + arity = 1; + break; + + default : + arity = 2; + break; + } + + if (k == 0) + { + result = 1.0; + for (int kp = 1; kp < n; kp++) + result -= operatorProbability(op, kp, n, + total_short_unary_priority, + total_long_unary_priority, + total_binary_priority, + result_cache); + } + else if (n == 1 || k >= n) + result = 0.0; + else if (k == 1 && n == 2) + { + if (arity == 1 && total_short_unary_priority > 0) + result = static_cast(priority) + / static_cast(total_short_unary_priority); + else + result = 0.0; + } + else + { + int p1, p2; + + if (arity == 1) + { + p1 = total_long_unary_priority; + if (op != ::Ltl::LTL_NEGATION || formula_options.generate_mode != NNF) + p1 -= priority; + p2 = total_binary_priority; + } + else + { + p1 = total_long_unary_priority; + p2 = total_binary_priority - priority; + } + + result = 0.0; + for (int m = 1; m <= n - 2; m++) + for (int i = 0; i <= k; i++) + { + if (i >= m || k - i >= n - m - 1) + continue; + result += operatorProbability(op, i, m, + total_short_unary_priority, + total_long_unary_priority, + total_binary_priority, + result_cache) + * operatorProbability(op, k - i, n - m - 1, + total_short_unary_priority, + total_long_unary_priority, + total_binary_priority, + result_cache); + } + + result *= static_cast(p2); + + if (arity == 1) + { + result /= static_cast(n - 2); + + if (op != ::Ltl::LTL_NEGATION || formula_options.generate_mode != NNF) + result += static_cast(priority) + * operatorProbability(op, k - 1, n - 1, + total_short_unary_priority, + total_long_unary_priority, + total_binary_priority, + result_cache); + } + else + { + double r = 0.0; + for (int m = 1; m <= n - 2; m++) + for (int i = 0; i <= k - 1; i++) + { + if (i >= m || k - 1 - i >= n - m - 1) + continue; + r += operatorProbability(op, i, m, + total_short_unary_priority, + total_long_unary_priority, + total_binary_priority, + result_cache) + * operatorProbability(op, k - 1 - i, n - m - 1, + total_short_unary_priority, + total_long_unary_priority, + total_binary_priority, + result_cache); + } + result += static_cast(priority) * r; + result /= static_cast(n - 2); + } + + result += static_cast(p1) + * operatorProbability(op, k, n - 1, + total_short_unary_priority, + total_long_unary_priority, + total_binary_priority, + result_cache); + + result /= static_cast(total_long_unary_priority + + total_binary_priority); + } + + result_cache[op][make_pair(k, n)] = result; + return result; +} diff --git a/lbtt/src/Configuration.h b/lbtt/src/Configuration.h new file mode 100644 index 000000000..99ad8b49a --- /dev/null +++ b/lbtt/src/Configuration.h @@ -0,0 +1,627 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include +#include +#include +#include "Alloc.h" +#include "Exception.h" +#include "FormulaRandomizer.h" +#include "StateSpaceRandomizer.h" +#include "StringUtil.h" + +using namespace std; + + + +/****************************************************************************** + * + * A class for storing program configuration information. + * + *****************************************************************************/ + +class Configuration +{ +public: + Configuration(); /* Constructor. */ + + ~Configuration(); /* Destructor. */ + + void read(int argc, char* argv[]); /* Reads the program + * configuration. + */ + + void print /* Writes the current */ + (ostream& stream = cout, int indent = 0) const; /* configuration (in a + * textual form) to a + * stream. + */ + + struct AlgorithmInformation; /* See below. */ + + string algorithmString /* Formats the the id */ + (vector::size_type/* the name of the */ + algorithm_id) const; /* algorithm into a + * string. + */ + + static void showCommandLineHelp /* Prints the list of */ + (const char* program_name); /* command line options. */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + enum InteractionMode {NEVER, ALWAYS, ONERROR}; /* Enumeration constants + * affecting the behaviour + * of the program as + * regards user control. + */ + + enum FormulaMode {NORMAL, NNF}; /* Enumeration constants + * affecting the generation + * and output of random + * formulae. + */ + + enum StateSpaceMode {RANDOMGRAPH = 1, /* Enumeration constants */ + RANDOMCONNECTEDGRAPH = 2, /* affecting the */ + GRAPH = 3, /* generation of random */ + RANDOMPATH = 4, /* state spaces. */ + ENUMERATEDPATH = 8, + PATH = 12}; + + enum ProductMode {LOCAL, GLOBAL}; /* Enumeration constants + * for controlling the + * scope of synchronous + * products. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + struct AlgorithmInformation /* A structure for storing + * information about a + * particular algorithm + * (name, path to + * executable, command-line + * parameters). + */ + { + string* name; /* Name of the algorithm. + */ + + string* path_to_program; /* Path to the executable + * required for running + * the algorithm. + */ + + string* extra_parameters; /* Additional command-line + * parameters required for + * running the executable. + */ + + bool enabled; /* Determines whether the + * algorithm is enabled + * (whether it will be used + * in the tests or not). + */ + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + struct GlobalConfiguration /* A structure for storing + * all the information + * affecting the general + * behaviour of the + * program. + */ + { + int verbosity; /* Determines the verbosity + * of program output (0-5, + * the bigger the value, + * the more information + * will be shown). + */ + + InteractionMode interactive; /* Controls the behaviour + * of the program as + * regards the ability of + * the user to enter + * commands between test + * rounds. Possible values + * and their meanings are: + * + * NEVER: + * Run all tests without + * interruption. + * ALWAYS: + * Pause after each test + * round to wait for + * user commands. + * ONERROR: + * Try to run the tests + * without interruption. + * However, in case of + * an error, pause and + * wait for user + * commands. + */ + + unsigned long int number_of_rounds; /* Number of test rounds. + */ + + unsigned long int init_skip; /* Number of rounds to skip + * before starting testing. + */ + + unsigned long int statespace_change_interval; /* Determines the frequency + * (in rounds) of how often + * a new state space is + * generated. + */ + + StateSpaceMode statespace_generation_mode; /* Random state space + * generation mode. + * Available options are: + * + * RANDOMGRAPH: + * Generate random + * connected graphs as + * state spaces. + * RANDOMPATH: + * Generate paths as + * state spaces, choose + * the loop and the + * truth assignments for + * atomic propositions + * randomly. + * ENUMERATEDPATH: + * Generate paths as + * state spaces by + * enumerating all + * possible paths of a + * given length. + */ + + unsigned long int formula_change_interval; /* Determines the frequency + * (in rounds) of how often + * a new formula is + * generated. + */ + + ProductMode product_mode; /* Determines the scope of + * the synchronous products + * computed by the program. + * Possible values and + * their meanings are: + * + * LOCAL: + * The synchronous + * products are computed + * only with respect to + * the initial state of + * the system. This will + * save memory but makes + * the algorithm cross- + * comparisons less + * powerful, possibly + * at the cost of + * chances for finding + * inconsistencies in the + * results. + * GLOBAL: + * The synchronous + * products are computed + * with respect to each + * system state (i.e. + * the formula is model + * checked in each system + * state separately). + * This will usually + * require more memory + * than the other + * alternative. + */ + + string cfg_filename; /* Name for the + * configuration file. + */ + + string transcript_filename; /* Name for the error log + * file. + */ + + string formula_input_filename; /* Name for the file from + * which to read LTL + * formulae. + */ + + bool do_comp_test; /* Is the model checking + * result cross-comparison + * test enabled? + */ + + bool do_cons_test; /* Is the model checking + * result consistency check + * enabled? + */ + + bool do_intr_test; /* Is the automata + * intersection emptiness + * check enabled? + */ + + unsigned int statespace_random_seed; /* Random seeds for the */ + unsigned int formula_random_seed; /* state space and + * formula generation + * algorithms. + */ + }; + + struct FormulaConfiguration /* A structure for storing + * specific information + * affecting the generation + * of random formulae. + */ + { + int default_operator_priority; /* Default priority for all + * LTL formula symbols. + */ + + map, ALLOC(int) > /* Priorities for LTL */ + symbol_priority; /* formula symbols. */ + + map, ALLOC(double) > /* Expected numbers of */ + symbol_distribution; /* occurrence for the + * different formula + * operators. + */ + + bool allow_abbreviated_operators; /* Determines whether the + * operators ->, <->, xor, + * <>, [], W and M should + * be allowed when + * generating random + * formulae (these are + * `abbreviated' operators + * since they could be + * written in an equivalent + * form by using another + * operators). + */ + + Configuration::FormulaMode output_mode; /* Determines whether the + * generated formulae are + * to be converted to + * negation normal form + * before passing them to + * the different + * algorithms. Possible + * values are: + * + * NORMAL: + * No conversion. + * NNF: + * Do the conversion + * (this may affect the + * size of the formulae!) + */ + + Configuration::FormulaMode generate_mode; /* Determines whether the + * formulae are to be + * generated in negation + * normal form (strict + * size requirement for + * formulae). Possible + * values are: + * + * NORMAL: + * Allow more flexibility + * in the generation of + * formulae. + * NNF: + * Force generation into + * negation normal form. + */ + + ::Ltl::FormulaRandomizer formula_generator; /* Interface to the random + * LTL formula generation + * algorithm. + */ + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + vector algorithms; /* information about the + * algorithms used in + * the tests. + */ + + GlobalConfiguration global_options; /* General configuration + * information. + */ + + FormulaConfiguration formula_options; /* Configuration + * information for + * generating random + * formulae. + */ + + Graph::StateSpaceRandomizer /* Interface to the */ + statespace_generator; /* random state space + * generation + * algorithms. + */ + + typedef pair IntPair; + + set, ALLOC(IntPair) > /* Configuration options */ + locked_options; /* the values of which + * should not be + * initialized from the + * configuration file. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class ConfigurationException : public Exception /* A class for reporting + * errors when reading + * the configuration file. + */ + { + public: + ConfigurationException /* Constructors. */ + (const string& info = "", + const string& msg = ""); + + ConfigurationException + (int line_number = -1, + const string& msg = ""); + + ~ConfigurationException() throw(); /* Destructor. */ + + /* default copy constructor */ + + ConfigurationException& operator= /* Assignment operator. */ + (const ConfigurationException& e); + + /* `what' inherited from class Exception */ + + /* `changeMessage' inherited from class Exception */ + + string line_info; /* Error context + * information. + */ + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + struct IntegerRange /* Data structure for + * representing integer- + * valued ranges of certain + * program configuration + * options. + */ + { + long int min; /* Lower bound. */ + + long int max; /* Upper bound. */ + + char* error_message; /* Error message to be + * displayed if the value + * is not within the + * specified range. + */ + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + /* + * Ranges for certain integer-valued configuration options. + */ + + static const struct IntegerRange + VERBOSITY_RANGE, ROUND_COUNT_RANGE, GENERATION_RANGE, PRIORITY_RANGE, + PROPOSITION_COUNT_RANGE, FORMULA_SIZE_RANGE, FORMULA_MAX_SIZE_RANGE, + STATESPACE_SIZE_RANGE, STATESPACE_MAX_SIZE_RANGE; + +private: + enum CommandLineOptionType /* Command line options. */ + {OPT_COMPARISONTEST = 10000, OPT_CONFIGFILE, + OPT_CONSISTENCYTEST, OPT_DISABLE, OPT_ENABLE, + OPT_FORMULACHANGEINTERVAL, OPT_FORMULAFILE, + OPT_FORMULARANDOMSEED, OPT_HELP = 'h', + OPT_GLOBALPRODUCT = 20000, OPT_INTERACTIVE, + OPT_INTERSECTIONTEST, OPT_LOGFILE, + OPT_MODELCHECK, OPT_NOCOMPARISONTEST, + OPT_NOCONSISTENCYTEST, OPT_NOINTERSECTIONTEST, + OPT_NOPAUSE, OPT_PAUSE, OPT_PAUSEONERROR, + OPT_PROFILE, OPT_QUIET, OPT_ROUNDS, + OPT_SHOWCONFIG, OPT_SHOWOPERATORDISTRIBUTION, + OPT_SKIP, OPT_STATESPACECHANGEINTERVAL, + OPT_STATESPACERANDOMSEED, OPT_VERBOSITY, + OPT_VERSION, + + OPT_LOCALPRODUCT, + + OPT_ABBREVIATEDOPERATORS, OPT_ANDPRIORITY, + OPT_BEFOREPRIORITY, + OPT_DEFAULTOPERATORPRIORITY, + OPT_EQUIVALENCEPRIORITY, OPT_FALSEPRIORITY, + OPT_FINALLYPRIORITY,OPT_FORMULAGENERATEMODE, + OPT_FORMULAOUTPUTMODE, + OPT_FORMULAPROPOSITIONS, OPT_FORMULASIZE, + OPT_GENERATENNF, OPT_GLOBALLYPRIORITY, + OPT_IMPLICATIONPRIORITY, OPT_NEXTPRIORITY, + OPT_NOABBREVIATEDOPERATORS, + OPT_NOGENERATENNF, OPT_NOOUTPUTNNF, + OPT_NOTPRIORITY, OPT_ORPRIORITY, + OPT_OUTPUTNNF, OPT_PROPOSITIONPRIORITY, + OPT_RELEASEPRIORITY, + OPT_STRONGRELEASEPRIORITY, OPT_TRUEPRIORITY, + OPT_UNTILPRIORITY, OPT_WEAKUNTILPRIORITY, + OPT_XORPRIORITY, + + OPT_EDGEPROBABILITY, + OPT_ENUMERATEDPATH, OPT_RANDOMCONNECTEDGRAPH, + OPT_RANDOMGRAPH, OPT_RANDOMPATH, + OPT_STATESPACEGENERATEMODE, + OPT_STATESPACEPROPOSITIONS, + OPT_STATESPACESIZE, OPT_TRUTHPROBABILITY}; + + typedef map, double, /* Type definitions for */ + less >, /* the result cache used */ + ALLOC(double) > /* for computing the */ + ProbabilityMapElement; /* probability */ + typedef map, /* formula operators. */ + ALLOC(ProbabilityMapElement) > + ProbabilityMap; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + Configuration(const Configuration& cfg); /* Prevent copying and */ + Configuration& operator= /* assignment of */ + (const Configuration& cfg); /* Configuration + * objects. + */ + + void reset(); /* Initializes the + * configuration data + * to default values. + */ + + long int parseCommandLineInteger /* Converts an integer */ + (const string& option, const string& value) /* to a string with */ + const; /* some additional + * validity checks. + */ + + double operatorProbability /* Computes the */ + (const int op, const int k, const int n, /* probability with */ + const int total_short_unary_priority, /* which the operator */ + const int total_long_unary_priority, /* `op' will occur */ + const int total_binary_priority, /* exactly `k' times in */ + ProbabilityMap& result_cache) const; /* a randomly generated + * formula of size `n'. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class Configuration::ConfigurationException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline Configuration::ConfigurationException::ConfigurationException + (const string& info, const string& msg) : + Exception(msg), line_info(info) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Configuration::ConfigurationException. + * Creates a new exception object, initializing it with an + * error message and optional context information. + * + * Arguments: info -- Configuration file line number information (for + * telling about the context of the error). + * msg -- A reference to a constant string containing the + * error message. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Configuration::ConfigurationException::ConfigurationException + (int line_number, const string& msg) : + Exception(msg), line_info(line_number == -1 + ? string("") + : ::StringUtil::toString(line_number)) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Configuration::ConfigurationException. + * Creates a new exception object, initializing it with an + * error message and configuration file line number. + * + * Arguments: line_number -- Number of the line with an error (if -1, + * no context info is assumed to be present). + * msg -- A reference to a constant string containing + * the error message. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Configuration::ConfigurationException::~ConfigurationException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Configuration::ConfigurationException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Configuration::ConfigurationException& +Configuration::ConfigurationException::operator= + (const ConfigurationException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class + * Configuration::ConfigurationException. Assigns the value of + * another Configuration::ConfigurationException to `this' one. + * + * Arguments: e -- A reference to a constant + * Configuration::ConfigurationException. + * + * Returns: A reference to the object whose value was changed. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + line_info = e.line_info; + return *this; +} + + + +#endif /* !CONFIGURATION_H */ diff --git a/lbtt/src/DispUtil.cc b/lbtt/src/DispUtil.cc new file mode 100644 index 000000000..3c99f0d43 --- /dev/null +++ b/lbtt/src/DispUtil.cc @@ -0,0 +1,183 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include +#include "Alloc.h" +#include "DispUtil.h" + +/****************************************************************************** + * + * Functions for displaying various statistics during testing. + * + *****************************************************************************/ + +namespace DispUtil +{ + +stack > /* output stream. */ + stream_formatting_stack; + +/* ========================================================================= */ +void changeStreamFormatting + (ostream& stream, int width, int precision, ios::fmtflags flags) +/* ---------------------------------------------------------------------------- + * + * Description: Changes the formatting state of an output stream, storing its + * previous state so that it can be restored later. + * + * Arguments: stream -- A reference to an output stream. + * width -- Field width. + * precision -- Floating-point precision. + * flags -- Flags affecting e.g. output justification. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + StreamFormatting formatting; + + formatting.width = stream.width(width); + formatting.precision = stream.precision(precision); + formatting.flags = stream.flags(flags); + + stream_formatting_stack.push(formatting); +} + +/* ========================================================================= */ +void restoreStreamFormatting(ostream& stream) +/* ---------------------------------------------------------------------------- + * + * Description: Restores the formatting state of an output stream whose + * previous state was saved by a call to + * changeStreamFormatting. + * + * Arguments: stream -- A reference to an output stream. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (!stream_formatting_stack.empty()) + { + StreamFormatting formatting = stream_formatting_stack.top(); + stream_formatting_stack.pop(); + stream.width(formatting.width); + stream.precision(formatting.precision); + stream.flags(formatting.flags); + } +} + +/* ========================================================================= */ +void printTextBlock + (ostream& stream, int indent, const string& text, int max_line_len) +/* ---------------------------------------------------------------------------- + * + * Description: Writes a string of text to a stream, breaking the text to + * multiple indented lines of a given maximum length if + * required. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of + * each printed line. + * text -- Text to be written to the stream. '\n':s + * can be used in the text to insert + * additional line breaks to the output. + * max_line_len -- Maximum allowed line length (including the + * amount of indentation). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + string::size_type max_len, current_pos = 1; + const string ind = string(indent, ' '); + + if (indent > max_line_len) + max_len = 0; + else + max_len = max_line_len - indent; + + string word; + string::const_iterator c = text.begin(); + + while (c != text.end() || !word.empty()) + { + if (c == text.end() || *c == ' ' || *c == '\t' || *c == '\n') + { + while (!word.empty()) + { + if (current_pos == 1) + estream << ind; + + string::size_type new_pos = current_pos + word.length(); + if (current_pos > 1) + ++new_pos; + + if (new_pos > max_len) + { + if (current_pos == 1) + { + estream << word; + word = ""; + } + + estream << '\n'; + current_pos = 1; + } + else + { + if (current_pos > 1) + estream << ' '; + estream << word; + word = ""; + current_pos = new_pos; + } + } + + if (c != text.end()) + { + if (*c == '\n') + { + current_pos = 1; + estream << '\n'; + } + ++c; + } + } + else if (c != text.end()) + { + word += *c; + ++c; + } + } + + estream << '\n'; + estream.flush(); +} + +} diff --git a/lbtt/src/DispUtil.h b/lbtt/src/DispUtil.h new file mode 100644 index 000000000..ff0040c63 --- /dev/null +++ b/lbtt/src/DispUtil.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef DISPUTIL_H +#define DISPUTIL_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include "Configuration.h" +#include "SharedTestData.h" +#include "TestRoundInfo.h" + +using namespace std; + +extern Configuration configuration; + +/****************************************************************************** + * + * Prototypes for miscellaneous routines for controlling output stream + * formatting and writing text into a stream. + * + *****************************************************************************/ + +namespace DispUtil +{ + +void changeStreamFormatting /* Changes the state of */ + (ostream& stream, int width, int precision, /* an output stream and */ + ios::fmtflags flags); /* saves its previous + * state. + */ + +void restoreStreamFormatting(ostream& stream); /* Restores a previously + * saved state of an + * output stream. + */ + +void printTextBlock /* Writes an indented */ + (ostream& stream, int indent, const string& text, /* and word-wrapped */ + int max_line_len); /* block of text into + * a stream. + */ + + + +/****************************************************************************** + * + * A data stucture for storing information about the state of an output stream. + * + *****************************************************************************/ + +struct StreamFormatting +{ + int width; /* Field width. */ + + int precision; /* Floating-point + * precision. + */ + + ios::fmtflags flags; /* Various flags affecting + * e.g. the justification + * of output. + */ + + bool printText /* "Verbosity-aware" */ + (const char* text, /* functions for writing */ + const int verbosity_threshold, /* text to standard */ + const int indent); /* output. */ + + bool printText + (const string& text, + const int verbosity_threshold, + const int indent); +}; + + + +/****************************************************************************** + * + * Functions for printing text on standard output. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool printText + (const char* text, const int verbosity_threshold, const int indent = 0) +/* ---------------------------------------------------------------------------- + * + * Description: Writes text on the standard output if the current output + * verbosity level is greater or equal to a given threshold. + * + * Arguments: text -- Text to write. + * verbosity_threshold -- Verbosity level threshold. + * indent -- Number of spaces to print on the + * left of the text. + * + * ------------------------------------------------------------------------- */ +{ + if (configuration.global_options.verbosity >= verbosity_threshold) + { + if (indent > 0) + SharedTestData::round_info.cout << string(indent, ' '); + SharedTestData::round_info.cout << text; + SharedTestData::round_info.cout.flush(); + return true; + } + + return false; +} + +/* ========================================================================= */ +inline bool printText + (const string& text, const int verbosity_threshold, const int indent = 0) +/* ---------------------------------------------------------------------------- + * + * Description: Writes text on the standard output if the current output + * verbosity level is greater or equal to a given threshold. + * + * Arguments: text -- Text to write. + * verbosity_threshold -- Verbosity level threshold. + * indent -- Number of spaces to print on the + * left of the text. + * + * Returns: `true' if anything was written to standard output. + * + * ------------------------------------------------------------------------- */ +{ + if (configuration.global_options.verbosity >= verbosity_threshold) + { + if (indent > 0) + SharedTestData::round_info.cout << string(indent, ' '); + SharedTestData::round_info.cout << text; + SharedTestData::round_info.cout.flush(); + return true; + } + + return false; +} + +} + +#endif /* !DISPUTIL_H */ diff --git a/lbtt/src/EdgeContainer.h b/lbtt/src/EdgeContainer.h new file mode 100644 index 000000000..b0180304d --- /dev/null +++ b/lbtt/src/EdgeContainer.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef EDGECONTAINER_H +#define EDGECONTAINER_H + +#include +#include "Graph.h" + +typedef ::Graph::EdgeVector GraphEdgeContainer; + +#endif /* !EDGECONTAINER_H */ diff --git a/lbtt/src/Exception.h b/lbtt/src/Exception.h new file mode 100644 index 000000000..274ab3be6 --- /dev/null +++ b/lbtt/src/Exception.h @@ -0,0 +1,1170 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef EXCEPTION_H +#define EXCEPTION_H + +#include +#include +#include +#include + +using namespace std; + +/****************************************************************************** + * + * A base class for the exceptions used in the other modules of the program. + * + *****************************************************************************/ + +class Exception : public exception +{ +public: + Exception(const string& message = ""); /* Constructor. */ + + /* default copy constructor */ + + virtual ~Exception() throw(); /* Destructor. */ + + /* default assignment operator */ + + virtual const char* what() const throw(); /* Returns the exception's + * error message. + */ + + virtual void changeMessage /* Changes the error */ + (const string& new_message); /* message. */ + +private: + string error_message; /* Error message. */ +}; + + + +/****************************************************************************** + * + * A class for reporting of a user break. + * + *****************************************************************************/ + +class UserBreakException : public Exception +{ +public: + UserBreakException /* Constructor. */ + (const string& message = "user break"); + + /* default copy constructor */ + + virtual ~UserBreakException() throw(); /* Destructor. */ + + UserBreakException& /* Assignment operator. */ + operator=(const UserBreakException& e); + + /* `what' inherited from class Exception */ + + /* `changeMessage' inherited from class Exception */ +}; + + + +/****************************************************************************** + * + * A base class for I/O exceptions. + * + *****************************************************************************/ + +class IOException : public Exception +{ +public: + IOException(const string& message = ""); /* Constructor. */ + + /* default copy constructor */ + + virtual ~IOException() throw(); /* Destructor. */ + + IOException& operator=(const IOException& e); /* Assignment operator. */ + + /* `what' inherited from class Exception */ + + /* `changeMessage' inherited from class Exception */ +}; + + + +/****************************************************************************** + * + * An exception class for reporting errors when trying to open a file. + * + *****************************************************************************/ + +class FileOpenException : public IOException +{ +public: + FileOpenException(); /* Default constructor. */ + + FileOpenException(const string& filename); /* Constructor which + * relates the exception's + * error message with a + * given file name. + */ + + /* default copy constructor */ + + ~FileOpenException() throw(); /* Destructor. */ + + FileOpenException& operator= /* Assignment operator. */ + (const FileOpenException& e); + + /* `what' inherited from class IOException */ + + /* `changeMessage' inherited from class IOException */ +}; + + + +/****************************************************************************** + * + * An exception class for reporting errors when trying to create a file. + * + *****************************************************************************/ + +class FileCreationException : public IOException +{ +public: + FileCreationException(); /* Default constructor. */ + + FileCreationException(const string& filename); /* Constructor which + * relates the exception's + * error message with a + * given file name. + */ + + /* default copy constructor */ + + ~FileCreationException() throw(); /* Destructor. */ + + FileCreationException& operator= /* Assignment operator. */ + (const FileCreationException& e); + + /* `what' inherited from class IOException */ + + /* `changeMessage' inherited from class IOException */ +}; + + + +/****************************************************************************** + * + * An exception class for reporting errors when reading a file. + * + *****************************************************************************/ + +class FileReadException : public IOException +{ +public: + FileReadException(); /* Default constructor. */ + + FileReadException /* Constructor which */ + (const string& filename, /* relates the */ + const string& details = ""); /* exception's error + * message with a given + * file, including a + * possible explanation + * for the error in the + * message. + */ + + /* default copy constructor */ + + ~FileReadException() throw(); /* Destructor. */ + + FileReadException& operator= /* Assignment operator. */ + (const FileReadException& e); + + /* `what' inherited from class IOException */ + + /* `changeMessage' inherited from class IOException */ +}; + + + +/****************************************************************************** + * + * An exception class for reporting errors when writing to a file. + * + *****************************************************************************/ + +class FileWriteException : public IOException +{ +public: + FileWriteException(); /* Default constructor. */ + + FileWriteException /* Constructor which */ + (const string& filename, /* relates the */ + const string& details = ""); /* exception's error + * message with a given + * file, including a + * possible explanation + * for the error in the + * message. + */ + + /* default copy constructor */ + + ~FileWriteException() throw(); /* Destructor. */ + + FileWriteException& operator= /* Assignment operator. */ + (const FileWriteException& e); + + /* `what' inherited from class IOException */ + + /* `changeMessage' inherited from class IOException */ +}; + + + +/****************************************************************************** + * + * An exception class for reporting errors when trying to execute an external + * program. + * + *****************************************************************************/ + +class ExecFailedException : public IOException +{ +public: + ExecFailedException(); /* Default constructor. */ + + ExecFailedException(const string& filename); /* Constructor which + * relates the exception's + * error message with a + * given file name. + */ + + /* default copy constructor */ + + ~ExecFailedException() throw(); /* Destructor. */ + + ExecFailedException& operator= /* Assignment operator. */ + (const ExecFailedException& e); + + /* `what' inherited from class IOException */ + + /* `changeMessage' inherited from class IOException */ +}; + + + +/****************************************************************************** + * + * A wrapper class for performing `guarded' input from a regular stream using + * the >> operator for reading the stream. If the input operation fails, an + * exception is thrown. + * + * (This class is required to make `guarded' input from regular streams + * possible with (at least) the GNU Standard C++ Library which does not fully + * support the ANSI C++ standard.) + * + *****************************************************************************/ + +class Exceptional_istream +{ +public: + Exceptional_istream /* Constructor. */ + (istream *istr, + ios::iostate mask = ios::goodbit); + + /* default copy constructor */ + + ~Exceptional_istream(); /* Destructor. */ + + /* default assignment operator */ + + template /* Operator for reading */ + Exceptional_istream &operator>>(T &t); /* from the stream. */ + + operator istream&(); /* Casts the exception- + * aware input stream into + * a regular input stream. + */ + +private: + istream* stream; /* A pointer to the + * `regular' input stream + * with which the object is + * associated. + */ + + ios::iostate exception_mask; /* Bit mask which + * determines when to throw + * an exception. + */ +}; + + + +/****************************************************************************** + * + * A wrapper class for performing `guarded' output to a regular stream using + * the << operator for writing to the stream. If the output operation fails, an + * exception is thrown. + * + * (This class is required to make `guarded' output from regular streams + * possible with (at least) the GNU Standard C++ Library which does not fully + * support the ANSI C++ standard.) + * + *****************************************************************************/ + +class Exceptional_ostream +{ +public: + Exceptional_ostream /* Constructor. */ + (ostream* ostr, + ios::iostate mask = ios::goodbit); + + /* default copy constructor */ + + ~Exceptional_ostream(); /* Destructor. */ + + /* default assignment operator */ + + template /* Operator for writing */ + Exceptional_ostream& operator<<(const T& t); /* to the stream. */ + + Exceptional_ostream& flush(); /* Flushes the stream. */ + + operator ostream&(); /* Casts the exception- + * aware output stream into + * a regular output stream. + */ + +private: + ostream* stream; /* A pointer to the + * `regular' output stream + * with which the object is + * associated. + */ + + ios::iostate exception_mask; /* Bit mask which + * determines when to throw + * an exception. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class Exception. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline Exception::Exception(const string& message) : + error_message(message) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Exception. Creates a new exception + * object, initializing it with a given error message. + * + * Argument: message -- An error message. The message defaults to the + * empty string if this argument is not given. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Exception::~Exception() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Exception. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline const char* Exception::what() const throw() +/* ---------------------------------------------------------------------------- + * + * Description: Tells the Exception's error message. + * + * Arguments: None. + * + * Returns: The error message as a C-style string. + * + * ------------------------------------------------------------------------- */ +{ + return error_message.c_str(); +} + +/* ========================================================================= */ +inline void Exception::changeMessage(const string& new_message) +/* ---------------------------------------------------------------------------- + * + * Description: Changes the Exception's error message. + * + * Argument: new_message -- A replacement error message. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + error_message = new_message; +} + + + +/****************************************************************************** + * + * Inline function definitions for class UserBreakException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline UserBreakException::UserBreakException(const string& message) : + Exception(message) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class UserBreakException. Creates a new + * UserBreakException object and initializes it with an error + * message. + * + * Argument: message -- An error message. The message defaults to the + * string `User break.' if no alternative message + * is given. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline UserBreakException::~UserBreakException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class UserBreakException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline UserBreakException& UserBreakException::operator= + (const UserBreakException& e) +/* ---------------------------------------------------------------------------- + * + * Descrption: Assignment operator for class UserBreakException. Assigns the + * value of another UserBreakException to `this' one. + * + * Argument: e -- A reference to a constant UserBreakException. + * + * Returns: A reference to the object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class IOException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline IOException::IOException(const string& message) : + Exception(message) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class IOException. Creates a new IOException + * object and initializes it with an error message. + * + * Argument: message -- An error message. The message defaults to the + * empty string if the argument is not given. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline IOException::~IOException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class IOException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline IOException& IOException::operator=(const IOException& e) +/* ---------------------------------------------------------------------------- + * + * Descrption: Assignment operator for class IOException. Assigns the value + * of another IOException to `this' one. + * + * Argument: e -- A reference to a constant IOException. + * + * Returns: A reference to the object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class FileOpenException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline FileOpenException::FileOpenException() : + IOException("file open error") +/* ---------------------------------------------------------------------------- + * + * Description: Default constructor for class FileOpenException. Creates a + * new FileOpenException object and initializes it with a + * generic error message `File open error.' + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileOpenException::FileOpenException(const string& filename) : + IOException("error opening " + filename) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class FileOpenException. This constructor + * relates the exception's error message with a given file name. + * + * Argument: filename -- A reference to a constant string (the file + * name). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileOpenException::~FileOpenException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class FileOpenException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileOpenException& FileOpenException::operator= + (const FileOpenException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class FileOpenException. Assigns the + * `value' of another FileOpenException to `this' one. + * + * Argument: e -- A reference to a constant FileOpenException object. + * + * Returns: A reference to the object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + IOException::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class FileCreationException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline FileCreationException::FileCreationException() : + IOException("file creation error") +/* ---------------------------------------------------------------------------- + * + * Description: Default constructor for class FileCreationException. Creates + * a new FileCreationException, initializing the error message + * to `File creation error.' + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileCreationException::FileCreationException(const string& filename) : + IOException("unable to create " + filename) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class FileCreationException. This constructor + * relates the exception's error message with a given file name. + * + * Argument: filename -- A reference to a constant string (the file + * name). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileCreationException::~FileCreationException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class FileCreationException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileCreationException& FileCreationException::operator= + (const FileCreationException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class FileCreationException. Assigns + * the `value' of another FileCreationException to `this' one. + * + * Argument: e -- A reference to a constant FileCreationException. + * + * Returns: A reference to the object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + IOException::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class FileReadException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline FileReadException::FileReadException() : + IOException("error reading file") +/* ---------------------------------------------------------------------------- + * + * Description: Default constructor for class FileReadException. Creates a + * new FileReadException object and initializes it with the + * error message `Error reading file.' + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileReadException::FileReadException + (const string& filename, const string& details) : + IOException("error reading " + filename + + string(details.empty() ? "" : " " + details)) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class FileReadException. This constructor + * relates the exception's error message to a given file name. + * The error message can also include an explanation for the + * error. + * + * Arguments: filename -- A reference to a constant string (the file + * name). + * details -- Explanation for the error (defaults to the + * empty string if not specified). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileReadException::~FileReadException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class FileReadException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileReadException& FileReadException::operator= + (const FileReadException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class FileReadException. Assigns the + * `value' of another FileReadException to `this' one. + * + * Argument: e -- A reference to a constant FileReadException. + * + * Returns: A reference to the object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + IOException::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class FileWriteException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline FileWriteException::FileWriteException() : + IOException("error writing to file") +/* ---------------------------------------------------------------------------- + * + * Description: Default constructor for class FileWriteException. Creates a + * new FileWriteException object and initializes it with the + * error message `Error writing to file.' + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileWriteException::FileWriteException + (const string& filename, const string& details) : + IOException("error writing to " + filename + + string(details.empty() ? "" : " " + details)) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class FileWriteException. This constructor + * relates the exception's error message to a given file name. + * The error message can also include an explanation for the + * error. + * + * Arguments: filename -- A reference to a constant string (the file + * name). + * details -- Explanation for the error (defaults to the + * empty string if not specified). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileWriteException::~FileWriteException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class FileWriteException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FileWriteException& FileWriteException::operator= + (const FileWriteException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class FileWriteException. Assigns the + * `value' of another FileWriteException to `this' one. + * + * Argument: e -- A reference to a constant FileWriteException. + * + * Returns: A reference to the object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + IOException::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class ExecFailedException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline ExecFailedException::ExecFailedException() : + IOException("program execution failed") +/* ---------------------------------------------------------------------------- + * + * Description: Default constructor for class ExecFailedException. Creates a + * new ExecFailedException object and initializes it with the + * error message `Program execution failed.' + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline ExecFailedException::ExecFailedException(const string& filename) : + IOException("execution of `" + filename + "' failed") +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class ExecFailedException. This constructor + * relates the exception's error message to a given file name. + * + * Argument: filename -- A reference to a constant string (the file + * name). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline ExecFailedException::~ExecFailedException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class ExecFailedException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline ExecFailedException& ExecFailedException::operator= + (const ExecFailedException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class ExecFailedException. Assigns + * the `value' of another ExecFailedException object to `this' + * one. + * + * Argument: e -- A reference to a constant ExecFailedException. + * + * Returns: A reference to the object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + IOException::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class Exceptional_istream. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline Exceptional_istream::Exceptional_istream + (istream* istr, ios::iostate mask) : + stream(istr), exception_mask(mask) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Exceptional_istream. Creates a new + * object providing `guarded' input from a regular stream + * using the >> operator for the input operations. + * + * Arguments: istr -- A pointer to an object of type istream. + * mask -- A bit mask determining when the Exceptional_istream + * should throw exceptions. The most useful constants + * for the bit mask are + * ios::badbit Throw an exception if the input + * stream (after performing an input + * operation) enters a state in which + * the call to bad() would return + * true. + * ios::failbit Throw an exception if the input + * stream (after performing an input + * operation) enters a state in which + * the call to fail() would return + * true. + * ios::eofbit Throw an exception if the input + * stream (after performing an input + * operation) enters a state in which + * the call to eof() would return + * true. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Exceptional_istream::~Exceptional_istream() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Exceptional_istream. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Exceptional_istream::operator istream&() +/* ---------------------------------------------------------------------------- + * + * Description: Casts the exception-aware input stream into a regular input + * stream. + * + * Arguments: None. + * + * Returns: A reference to the input stream associated with the object. + * + * ------------------------------------------------------------------------- */ +{ + return *stream; +} + + + +/****************************************************************************** + * + * Template function definitions for class Exceptional_istream. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +Exceptional_istream& Exceptional_istream::operator>>(T& t) +/* ---------------------------------------------------------------------------- + * + * Description: Input operator for reading from a stream. If the read + * operation puts the stream into an undesirable state, an + * exception will be thrown. + * + * Arguments: t -- A reference to an object which will store the value + * read. + * + * Returns: A reference to the Exceptional_istream object (to support + * chaining of the >> operators). + * + * ------------------------------------------------------------------------- */ +{ + *stream >> t; + if (stream->rdstate() & exception_mask) + throw IOException("error reading from stream"); + + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class Exceptional_ostream. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline Exceptional_ostream::Exceptional_ostream + (ostream* ostr, ios::iostate mask) : + stream(ostr), exception_mask(mask) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Exceptional_ostream. Creates a new + * object providing `guarded' output into a regular stream + * using the << operator for the output operations. + * + * Arguments: ostr -- A pointer to an object of type ostream. + * mask -- A bit mask determining when the Exceptional_ostream + * should throw exceptions. The most useful constants + * for the bit mask are + * ios::badbit Throw an exception if the output + * stream (after performing an output + * operation) enters a state in which + * the call to bad() would return + * true. + * ios::failbit Throw an exception if the output + * stream (after performing an output + * operation) enters a state in which + * the call to fail() would return + * true. + * ios::eofbit Throw an exception if the output + * stream (after performing an output + * operation) enters a state in which + * the call to eof() would return + * true. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Exceptional_ostream::~Exceptional_ostream() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Exceptional_ostream. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline Exceptional_ostream::operator ostream&() +/* ---------------------------------------------------------------------------- + * + * Description: Casts the exception-aware output stream into a regular output + * stream. + * + * Arguments: None. + * + * Returns: A reference to the input output associated with the object. + * + * ------------------------------------------------------------------------- */ +{ + return *stream; +} + +/* ========================================================================= */ +inline Exceptional_ostream& Exceptional_ostream::flush() +/* ---------------------------------------------------------------------------- + * + * Description: Flushes an Exceptional_ostream. + * + * Arguments: None. + * + * Returns: A reference to the Exceptional_ostream. + * + * ------------------------------------------------------------------------- */ +{ + stream->flush(); + if (stream->rdstate() & exception_mask) + throw IOException("error writing to stream"); + + return *this; +} + + + +/****************************************************************************** + * + * Template function definitions for class Exceptional_ostream. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +Exceptional_ostream& Exceptional_ostream::operator<<(const T& t) +/* ---------------------------------------------------------------------------- + * + * Description: Output operator for writing into a stream. If the write + * operation puts the stream into an undesirable state, an + * exception will be thrown. + * + * Arguments: t -- A reference to a constant object which is to be + * written into the stream. + * + * Returns: A reference to the Exceptional_ostream object (to support + * chaining of the << operators). + * + * ------------------------------------------------------------------------- */ +{ + *stream << t; + if (stream->rdstate() & exception_mask) + throw IOException("error writing to stream"); + + return *this; +} + + + +#endif /* !EXCEPTION_H */ diff --git a/lbtt/src/ExternalTranslator.cc b/lbtt/src/ExternalTranslator.cc new file mode 100644 index 000000000..d73091da7 --- /dev/null +++ b/lbtt/src/ExternalTranslator.cc @@ -0,0 +1,220 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#include "ExternalTranslator.h" + +/****************************************************************************** + * + * Function definitions for class ExternalTranslator. + * + *****************************************************************************/ + +/* ========================================================================= */ +ExternalTranslator::~ExternalTranslator() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class ExternalTranslator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + /* + * Delete all registered temporary files or directories in the reverse order + * of registration. + */ + + while (!temporary_file_objects.empty()) + { + delete temporary_file_objects.top(); + temporary_file_objects.pop(); + } +} + +/* ========================================================================= */ +ExternalTranslator::TempFileObject& +ExternalTranslator::registerTempFileObject + (const string& filename, TempFileObject::Type type) +/* ---------------------------------------------------------------------------- + * + * Description: Registers a temporary file or directory such that it will be + * automatically deleted when the ExternalTranslator object is + * destroyed. + * + * Arguments: filename -- Name of the temporary file or directory. + * type -- Type of the object (TempFileObject::FILE or + * TempFileObject::DIRECTORY). + * + * Returns: A reference to the file object. + * + * ------------------------------------------------------------------------- */ +{ + temporary_file_objects.push(new TempFileObject(filename, type)); + return *temporary_file_objects.top(); +} + +/* ========================================================================= */ +void ExternalTranslator::translate + (const ::Ltl::LtlFormula& formula, const char* filename) +/* ---------------------------------------------------------------------------- + * + * Description: Executes an external program which translates an LTL formula + * into a Büchi automaton and stores the automaton description + * in lbtt format into a file. + * + * Arguments: formula -- LTL formula to be translated. + * filename -- Name of the output file. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + TempFileObject& external_program_input_file = registerTempFileObject(); + + TempFileObject& external_program_output_file = registerTempFileObject(); + + string translated_formula; + translateFormula(formula, translated_formula); + + ofstream input_file; + input_file.open(external_program_input_file.getName().c_str(), + ios::out | ios::trunc); + if (!input_file.good()) + throw FileCreationException(string("`") + + external_program_input_file.getName() + + "'"); + + Exceptional_ostream einput_file(&input_file, ios::failbit | ios::badbit); + + formatInput(einput_file, translated_formula); + + input_file.close(); + + string command_line = string(command_line_arguments[2]) + + commandLine(external_program_input_file.getName(), + external_program_output_file.getName()); + + if (!execSuccess(system(command_line.c_str()))) + throw ExecFailedException(command_line_arguments[2]); + + parseAutomaton(external_program_output_file.getName(), filename); +} + + + +/****************************************************************************** + * + * Function definitions for class ExternalTranslator::TempFileObject. + * + *****************************************************************************/ + +/* ========================================================================= */ +ExternalTranslator::TempFileObject::TempFileObject + (const string& filename, Type t) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class TempFileObject. Creates a temporary + * file or a directory and tests whether it can really be + * written to (if not, a FileCreationException is thrown). + * + * Arguments: filename -- Name of the temporary file or directory. + * If the filename is an empty string, the + * filename is obtained by a call to tmpnam(3). + * t -- Type of the object (TempFileObject::FILE or + * TempFileObject::DIRECTORY). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (filename.empty()) + { + char tempname[L_tmpnam + 1]; + + if (tmpnam(tempname) == 0) + throw FileCreationException("a temporary file"); + + name = tempname; + } + else + name = filename; + + if (t == FILE) + { + ofstream tempfile; + tempfile.open(name.c_str(), ios::out | ios::trunc); + if (!tempfile.good()) + throw FileCreationException("a temporary file"); + tempfile.close(); + } + else + { + if (mkdir(name.c_str(), 0700) != 0) + throw FileCreationException("a temporary directory"); + } + + type = t; +} + +/* ========================================================================= */ +ExternalTranslator::TempFileObject::~TempFileObject() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class TempFileObject. Deletes the file or + * the directory associated with the object (displays a warning + * if this fails). + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (remove(name.c_str()) != 0) + { + string msg("error removing temporary "); + + if (type == TempFileObject::FILE) + msg += "file"; + else + msg += "directory"; + + msg += " `" + name + "'"; + + printWarning(msg); + } +} diff --git a/lbtt/src/ExternalTranslator.h b/lbtt/src/ExternalTranslator.h new file mode 100644 index 000000000..4835f6ef0 --- /dev/null +++ b/lbtt/src/ExternalTranslator.h @@ -0,0 +1,388 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef EXTERNALTRANSLATOR_H +#define EXTERNALTRANSLATOR_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#ifdef HAVE_SSTREAM +#include +#else +#include +#endif /* HAVE_SSTREAM */ +#include "Alloc.h" +#include "Exception.h" +#include "LtlFormula.h" +#include "translate.h" +#include "TranslatorInterface.h" + +/****************************************************************************** + * + * Interface class for an LTL-to-Büchi translation algorithm operating as an + * external program. An actual implementation of this interface (a class + * derived from this base class) must provide definitions for the following + * functions: + * + * string commandLine + * (const string& input_filename, const string& output_filename) + * Formats the command line of the external translator such that the + * external program will read its input (i.e., an LTL formula) from + * `input_filename' and store its output into `output_filename'. + * + * The returned string should include _only_ the argument part of the + * command line (the returned string will be appended to the name of + * the program). Use input/output redirection in the string if the + * program does not have an option to explicitly specify an + * input/output file. + * + * void parseAutomaton + * (const string& input_filename, const string& output_filename) + * Reads the automaton constructed by the external program from + * `input_filename' and stores the automaton in lbtt syntax into + * `output_filename'. + * + * In addition, the implementation can override the default definitions for the + * following functions: + * + * void translateFormula + * (const LtlFormula& formula, string& translated_formula) + * Translates a given LtlFormula into the input syntax of the + * external translator. The result must be stored in + * `translated_formula'. The default implementation simply returns + * the formula in lbtt's prefix notation. + * + * void formatInput(Exceptional_ostream& stream, const string& formula) + * Prepares an input file for the external translator program. + * The default implementation simply writes `formula' (followed by a + * newline) into `stream'. + * + * bool execSuccess(int exitcode) + * Tests whether the execution of the external program was performed + * successfully by examining the exit status of the program (as + * returned by a call to `system'). The default implementation + * returns true if and only if `exitcode' has the value 0. + * + * When performing the translation, the above functions will be called in the + * following order: + * 1. translateFormula (translate the input formula into the external + * program syntax) + * 2. formatInput (write the translated formula into a file) + * 3. commandLine (construct the command line required for executing + * the external translator) + * 4. execSuccess (test whether the execution succeeded) + * 5. parseAutomaton (translate the constructed automaton into lbtt + * format) + * + * The interface class additionally provides a function to help deleting any + * temporary files or directories that might be created during the execution of + * the external program, provided that their names are static or can be derived + * from the names of the input/output files. Each of these files should be + * "registered" before calling the external program with the function + * + * void registerTempFileObject + * (const string& filename, TempFileObject::Type t) + * + * where `filename' is the full name of the temporary file and `t' is a type + * of the object (TempFileObject::FILE or TempFileObject::DIRECTORY). + * + * All files or directories registered using this function will then be + * automatically deleted after the translation is finished or aborted. + * The files or directories will be deleted in the reverse order of + * registration, i.e., the most recently registered file/directory will be + * deleted first. + * + *****************************************************************************/ + +class ExternalTranslator : public TranslatorInterface +{ +public: + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class TempFileObject /* A class for storing */ + { /* information about */ + public: /* temporary files and + * directories. + */ + + enum Type {FILE, DIRECTORY}; /* Types for a temporary + * file object. + */ + + TempFileObject /* Constructor. */ + (const string& filename = "", Type t = FILE); + + ~TempFileObject(); /* Destructor. */ + + const string& getName() const; /* Returns the filename + * associated with the + * object. + */ + + Type getType() const; /* Returns the type of + * the object. + */ + + private: + string name; /* Name of the file object. + */ + + Type type; /* Type of the file object. + */ + + TempFileObject(const TempFileObject&); /* Prevent copying and */ + TempFileObject& operator= /* assignment of */ + (const TempFileObject&); /* TempFileObjects. */ + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + ExternalTranslator(); /* Constructor. */ + + ~ExternalTranslator(); /* Destructor. */ + + TempFileObject& registerTempFileObject /* Registers a temporary */ + (const string& filename = "", /* file or directory */ + TempFileObject::Type /* such that it will be */ + t = TempFileObject::FILE); /* automatically deleted + * when the translation + * is complete. + */ + + void translate /* Main translation */ + (const ::Ltl::LtlFormula& formula, /* function. */ + const char* filename); + + virtual void translateFormula /* Translates an */ + (const ::Ltl::LtlFormula& formula, /* LtlFormula into */ + string& translated_formula); /* the syntax of some + * external program. + */ + + virtual void formatInput /* Prepares an input */ + (Exceptional_ostream& stream, /* file for an external */ + const string& formula); /* translator. */ + + virtual string commandLine /* Constructs the */ + (const string& input_filename, /* argument part of the */ + const string& output_filename) = 0; /* command line for + * an external + * translator. + */ + + virtual bool execSuccess(int exitcode); /* Tests whether the + * execution of the + * external translator was + * successful. + */ + + virtual void parseAutomaton /* Translates the */ + (const string& input_filename, /* automaton description */ + const string& output_filename) = 0; /* into lbtt format. */ + +private: + ExternalTranslator(const ExternalTranslator&); /* Prevent copying and */ + ExternalTranslator& operator= /* assignment of */ + (const ExternalTranslator&); /* ExternalTranslator + * objects. + */ + + stack > /* information. */ + temporary_file_objects; + + friend class KecWrapper; /* Friend declarations. */ + friend class Ltl2AutWrapper; + friend class Ltl2BaWrapper; + friend class ProdWrapper; + friend class SpinWrapper; + friend class WringWrapper; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class WriterErrorReporter /* Class for reporting */ + { /* about unsupported */ + public: /* input formula */ + static void write /* operators. */ + (Exceptional_ostream&, int); + }; +}; + + + +/****************************************************************************** + * + * Inline function definitions for class ExternalTranslator. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline ExternalTranslator::ExternalTranslator() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class ExternalTranslator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline void ExternalTranslator::translateFormula + (const ::Ltl::LtlFormula& formula, string& translated_formula) +/* ---------------------------------------------------------------------------- + * + * Description: Converts an LtlFormula object into a string. + * + * Arguments: formula -- An LtlFormula. + * translated_formula -- A reference to a string for storing + * the result. + * + * Returns: Nothing. The result can be found in the string + * `translated_formula'. + * + * ------------------------------------------------------------------------- */ +{ +#ifdef HAVE_SSTREAM + ostringstream formula_stream; + formula.print(formula_stream, ::Ltl::LTL_PREFIX); + translated_formula = formula_stream.str(); +#else + ostrstream formula_stream; + formula.print(formula_stream, ::Ltl::LTL_PREFIX); + formula_stream << ends; + translated_formula = formula_stream.str(); + formula_stream.freeze(0); +#endif /* HAVE_SSTREAM */ +} + +/* ========================================================================= */ +inline void ExternalTranslator::formatInput + (Exceptional_ostream& stream, const string& formula) +/* ---------------------------------------------------------------------------- + * + * Description: Writes a string (assumed to contain an LTL formula in the + * input format of some external translator program) followed by + * a newline into a stream. + * + * Arguments: stream -- A reference to an Exceptional_ostream. + * formula -- A reference to a constant string assumed to + * contain an LTL formula. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + stream << formula << '\n'; +} + +/* ========================================================================= */ +inline bool ExternalTranslator::execSuccess(int exitcode) +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether the exit status of an external program is 0. + * + * Arguments: exitcode -- Exit status of an external program as reported + * by a call to `system'. + * + * Returns: `true' if the exit status is equal to 0. + * + * ------------------------------------------------------------------------- */ +{ + return (exitcode == 0); +} + + + +/****************************************************************************** + * + * Inline function definitions for class ExternalTranslator::TempFileObject. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline const string& ExternalTranslator::TempFileObject::getName() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the name associated with the + * ExternalTranslator::TempFileObject. + * + * Arguments: None. + * + * Returns: The name associated with the object. + * + * ------------------------------------------------------------------------- */ +{ + return name; +} + +/* ========================================================================= */ +inline ExternalTranslator::TempFileObject::Type +ExternalTranslator::TempFileObject::getType() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the type of the ExternalTranslator::TempFileObject. + * + * Arguments: None. + * + * Returns: The type associated with the object. + * + * ------------------------------------------------------------------------- */ +{ + return type; +} + + + +/****************************************************************************** + * + * Inline function definitions for class + * ExternalTranslator::WriterErrorReporter. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline void ExternalTranslator::WriterErrorReporter::write + (Exceptional_ostream&, int) +/* ---------------------------------------------------------------------------- + * + * Description: Aborts the formula translation if the formula contains an + * unsupported operator. + * + * Arguments: The arguments are required only for supporting the function + * call interface. + * + * Returns: Nothing. Instead, throws an Exception with an error message. + * + * ------------------------------------------------------------------------- */ +{ + throw Exception("unsupported operators in formula"); +} + +#endif /* !EXTERNALTRANSLATOR_H */ diff --git a/lbtt/src/FormulaRandomizer.cc b/lbtt/src/FormulaRandomizer.cc new file mode 100644 index 000000000..bdd5f87df --- /dev/null +++ b/lbtt/src/FormulaRandomizer.cc @@ -0,0 +1,233 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include "FormulaRandomizer.h" +#include "Random.h" + +namespace Ltl +{ + +/****************************************************************************** + * + * Function definitions for class FormulaRandomizer. + * + *****************************************************************************/ + +/* ========================================================================= */ +void FormulaRandomizer::reset() +/* ---------------------------------------------------------------------------- + * + * Description: Sets the random formula generation parameters to their + * default values. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + number_of_available_variables = 5; + size = 5; + max_size = 5; + propositional_symbol_priorities.clear(); + short_formula_operators.clear(); + long_formula_operators.clear(); + propositional_symbol_priorities.push_back(make_pair(-1, 0)); + short_formula_operators.push_back(make_pair(-1, 0)); + long_formula_operators.push_back(make_pair(-1, 0)); + number_of_generated_formulas = 0; + proposition_statistics.clear(); + symbol_statistics.clear(); +} + +/* ========================================================================= */ +LtlFormula* FormulaRandomizer::generate() +/* ---------------------------------------------------------------------------- + * + * Description: Generates a random (newly allocated) LtlFormula using the + * parameters stored in `this' object. + * + * Arguments: None. + * + * Returns: A pointer to the generated formula. + * + * ------------------------------------------------------------------------- */ +{ + unsigned long int target_size(size); + + if (max_size > size) + target_size += LRAND(0, max_size - size + 1); + + number_of_generated_formulas++; + return recGenerate(target_size); +} + +/* ========================================================================= */ +LtlFormula* FormulaRandomizer::recGenerate(unsigned long int target_size) +/* ---------------------------------------------------------------------------- + * + * Description: Implementation of the recursive random formula generation + * algorithm. + * + * Arguments: target_size -- Size of the formula to be generated. + * + * Returns: A pointer to the generated formula. + * + * ------------------------------------------------------------------------- */ +{ + vector::const_iterator symbol_priority; + LtlFormula* formula; + long int x; + + /* + * Select a list of allowable symbols according to the target size. If the + * size is 1, only atomic propositions and Boolean constants are allowed + * If the size is 2, only unary operators are allowed. Otherwise select the + * symbols from the set of unary and binary operators. + */ + + switch (target_size) + { + case 1 : + x = LRAND(0, propositional_symbol_priorities[0].second); + symbol_priority = propositional_symbol_priorities.begin(); + break; + + case 2 : + x = LRAND(0, short_formula_operators[0].second); + symbol_priority = short_formula_operators.begin(); + break; + + default : + x = LRAND(0, long_formula_operators[0].second); + symbol_priority = long_formula_operators.begin(); + break; + } + + /* + * Using the selected list, choose a random symbol in the list using the + * priority distribution of the different symbols in the list. The list + * consists of pairs. The first element of the list does + * not correspond to any symbol, however; instead, it gives the sum of the + * priorities of the symbols in the list. + */ + + ++symbol_priority; + while (x >= symbol_priority->second) + { + x -= symbol_priority->second; + ++symbol_priority; + } + + symbol_statistics[symbol_priority->first]++; + + switch (symbol_priority->first) + { + case LTL_ATOM : + { + unsigned long int atom = LRAND(0, number_of_available_variables); + proposition_statistics[atom]++; + formula = &Atom::construct(atom); + break; + } + + case LTL_TRUE : + formula = &True::construct(); + break; + + case LTL_FALSE : + formula = &False::construct(); + break; + + case LTL_NEGATION : + formula = &Not::construct(recGenerate(target_size - 1)); + break; + + case LTL_NEXT : + formula = &Next::construct(recGenerate(target_size - 1)); + break; + + case LTL_FINALLY : + formula = &Finally::construct(recGenerate(target_size - 1)); + break; + + case LTL_GLOBALLY : + formula = &Globally::construct(recGenerate(target_size - 1)); + break; + + default : + { + unsigned long int s = LRAND(1, target_size - 1); + LtlFormula* g = recGenerate(s); + LtlFormula* h = recGenerate(target_size - s - 1); + + switch (symbol_priority->first) + { + case LTL_CONJUNCTION : + formula = &And::construct(g, h); + break; + + case LTL_DISJUNCTION : + formula = &Or::construct(g, h); + break; + + case LTL_IMPLICATION : + formula = &Imply::construct(g, h); + break; + + case LTL_EQUIVALENCE : + formula = &Equiv::construct(g, h); + break; + + case LTL_XOR : + formula = &Xor::construct(g, h); + break; + + case LTL_UNTIL : + formula = &Until::construct(g, h); + break; + + case LTL_V : + formula = &V::construct(g, h); + break; + + case LTL_WEAK_UNTIL : + formula = &WeakUntil::construct(g, h); + break; + + case LTL_STRONG_RELEASE : + formula = &StrongRelease::construct(g, h); + break; + + default : /* LTL_BEFORE */ + formula = &Before::construct(g, h); + break; + } + } + } + + return formula; +} + +} diff --git a/lbtt/src/FormulaRandomizer.h b/lbtt/src/FormulaRandomizer.h new file mode 100644 index 000000000..c23348f49 --- /dev/null +++ b/lbtt/src/FormulaRandomizer.h @@ -0,0 +1,333 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FORMULARANDOMIZER_H +#define FORMULARANDOMIZER_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include "Alloc.h" +#include "LtlFormula.h" + +namespace Ltl +{ + +/****************************************************************************** + * + * Class for generating random LTL formulae. + * + *****************************************************************************/ + +class FormulaRandomizer +{ +public: + FormulaRandomizer(); /* Constructor. */ + + /* default copy constructor */ + + ~FormulaRandomizer(); /* Destructor. */ + + /* default assignment operator */ + + void reset(); /* Resets the formula + * generation parameters. + */ + + void useSymbol /* Inserts an atomic */ + (const int symbol, const int priority); /* symbol (an atomic + * proposition or a + * Boolean constant) + * into the set of + * operands used for + * generating random + * formulae. + */ + + void useShortOperator /* Inserts an element */ + (const int symbol, const int priority); /* into the set of + * operators used for + * generating random + * formulae of size + * two. + */ + + void useLongOperator /* Inserts an element */ + (const int symbol, const int priority); /* into the set of + * operators used for + * generating random + * formulae of size + * greater than two. + */ + + LtlFormula* generate(); /* Generates a random LTL + * formula. + */ + + unsigned long int numberOfFormulas() const; /* Get the number of + * generated formulas since + * the last call to + * `reset'. + */ + + const map, /* different atomic */ + ALLOC(unsigned long int) >& /* propositions */ + propositionStatistics() const; /* generated since the + * last call to `reset'. + */ + + const map, /* Get the numbers of */ + ALLOC(unsigned long int) >& /* different symbols */ + symbolStatistics() const; /* generated since the + * last call to `reset'. + */ + + unsigned long int number_of_available_variables; /* Size of the set of + * atomic propositions used + * for generating random + * LTL formulae. + */ + + unsigned long int size; /* Minimum size of the + * generated formulae + * (number of nodes in the + * formula parse tree). + */ + + unsigned long int max_size; /* Maximum size of the + * generated formulae. + */ + +private: + LtlFormula* recGenerate /* Implementation of */ + (unsigned long int target_size); /* the random formula + * generation algorithm. + */ + + typedef pair IntegerPair; + + vector /* Operand symbols and */ + propositional_symbol_priorities; /* their priorities in + * random formulae. + */ + + vector /* Operators and their */ + short_formula_operators; /* priorities in random + * formulae of size + * two. + */ + + vector /* Operators and their */ + long_formula_operators; /* priorities in random + * formulae of size + * greater than two. + */ + + unsigned long int number_of_generated_formulas; /* Number of generated + * formulas since the + * last call to `reset'. + */ + + map, /* atomic propositions */ + ALLOC(unsigned long int) > /* generated since the */ + proposition_statistics; /* last call to `reset' */ + + map, /* Number of different */ + ALLOC(unsigned long int) > /* formula symbols */ + symbol_statistics; /* generated since the + * last call to `reset'. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class FormulaRandomizer. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline FormulaRandomizer::FormulaRandomizer() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class FormulaRandomizer. Creates a new + * formula parameter information object and initializes the + * generation parameters with their default values. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + reset(); +} + +/* ========================================================================= */ +inline FormulaRandomizer::~FormulaRandomizer() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class FormulaRandomizer. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline void FormulaRandomizer::useSymbol(const int symbol, const int priority) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts an atomic symbol (an atomic proposition or a Boolean + * constant) into the set of symbols used for generating random + * LTL formulae. The symbol will then be chosen into a formula + * by the random formula generation algorithm with a given + * priority. + * + * Arguments: symbol -- A symbol type identifier (one of the LTL_ + * constants defined in LtlFormula.h). + * priority -- Priority for the symbol. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (priority > 0) + { + propositional_symbol_priorities.push_back(make_pair(symbol, priority)); + propositional_symbol_priorities[0].second += priority; + } +} + +/* ========================================================================= */ +inline void FormulaRandomizer::useShortOperator + (const int symbol, const int priority) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a symbol into the set of operators considered when + * generating random LTL formulae with two nodes in their parse + * tree. The symbol will then be chosen into a formula by the + * random formula generation algorithm with a given priority. + * + * Arguments: symbol -- A symbol type identifier (one of the LTL_ + * constants defined in LtlFormula.h). + * priority -- Priority for the symbol. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (priority > 0) + { + short_formula_operators.push_back(make_pair(symbol, priority)); + short_formula_operators[0].second += priority; + } +} + +/* ========================================================================= */ +inline void FormulaRandomizer::useLongOperator + (const int symbol, const int priority) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a symbol into the set of operators considered when + * generating random LTL formulae with two nodes in their parse + * tree. The symbol will then be chosen into a formula by the + * random formula generation algorithm with a given priority. + * + * Arguments: symbol -- A symbol type identifier (one of the LTL_ + * constants defined in LtlFormula.h). + * priority -- Priority for the symbol. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (priority > 0) + { + long_formula_operators.push_back(make_pair(symbol, priority)); + long_formula_operators[0].second += priority; + } +} + +/* ========================================================================= */ +inline unsigned long int FormulaRandomizer::numberOfFormulas() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the number of formulae generated since the last call to + * `reset'. + * + * Arguments: None. + * + * Returns: Number of formulae generated since the last call to `reset'. + * + * ------------------------------------------------------------------------- */ +{ + return number_of_generated_formulas; +} + +/* ========================================================================= */ +inline const map, + ALLOC(unsigned long int) >& +FormulaRandomizer::propositionStatistics() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the number of different atomic propositions generated + * since the last call to `reset'. + * + * Arguments: None. + * + * Returns: A reference to a constant mapping between proposition + * identifiers and their numbers. + * + * ------------------------------------------------------------------------- */ +{ + return proposition_statistics; +} + +/* ========================================================================= */ +inline const map, ALLOC(unsigned long int) >& +FormulaRandomizer::symbolStatistics() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the number of different formula symbols generated since + * the last call to `reset'. + * + * Arguments: None. + * + * Returns: A reference to a constant mapping between LTL formula symbols + * and their numbers. + * + * ------------------------------------------------------------------------- */ +{ + return symbol_statistics; +} + +} + +#endif /* !FORMULARANDOMIZER_H */ diff --git a/lbtt/src/FormulaWriter.h b/lbtt/src/FormulaWriter.h new file mode 100644 index 000000000..0df6ecfbf --- /dev/null +++ b/lbtt/src/FormulaWriter.h @@ -0,0 +1,452 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FORMULAWRITER_H +#define FORMULAWRITER_H + +#include "Exception.h" + +namespace Ltl +{ + +class LtlFormula; +class Atom; + +/****************************************************************************** + * + * A function template class for writing the formula to a stream. + * + *****************************************************************************/ + +template +class FormulaWriter +{ +public: + FormulaWriter(Exceptional_ostream& stream); /* Constructor. */ + + ~FormulaWriter(); /* Destructor. */ + + void operator() /* Implements the write */ + (const LtlFormula* f, int operand); /* operation. */ + +private: + Exceptional_ostream& estream; /* Output stream. */ + + FormulaWriter(const FormulaWriter&); /* Prevent copying and */ + FormulaWriter& operator=(const FormulaWriter&); /* assignment of + * FormulaWriter + * objects. + */ +}; + + + +/****************************************************************************** + * + * Class for printing atomic propositions. + * + *****************************************************************************/ + +class AtomWriter +{ +public: + static void write /* Implements the write */ + (Exceptional_ostream& estream, /* operation. */ + long int atom_id); +}; + + + +/****************************************************************************** + * + * Template class for printing Boolean constants. + * + *****************************************************************************/ + +template +class ConstantWriter +{ +public: + static void write(Exceptional_ostream& estream); /* Implements the write */ +}; /* operation. */ + + + +/****************************************************************************** + * + * Template class for printing unary operators. + * + *****************************************************************************/ + +template +class UnaryOperatorWriter +{ +public: + static void write /* Implements the write */ + (Exceptional_ostream& estream, int operand); /* operation. */ +}; + + + +/****************************************************************************** + * + * Template class for printing binary operators in prefix notation. + * + *****************************************************************************/ + +template +class BinaryOperatorPrefixWriter +{ +public: + static void write /* Implements the write */ + (Exceptional_ostream& estream, int operand); /* operation. */ +}; + + + +/****************************************************************************** + * + * Template class for printing binary operators in infix notation. + * + *****************************************************************************/ + +template +class BinaryOperatorInfixWriter +{ +public: + static void write /* Implements the write */ + (Exceptional_ostream& estream, int operand); /* operation. */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for template class FormulaWriter. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline FormulaWriter:: +FormulaWriter(Exceptional_ostream& stream) : + estream(stream) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class FormulaWriter. + * + * Arguments: stream -- A reference to an exception-aware output + * stream. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline FormulaWriter:: +~FormulaWriter() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class FormulaWriter. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + + + +/****************************************************************************** + * + * Inline function definitions for class AtomWriter. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline void AtomWriter::write + (Exceptional_ostream& estream, long int atom_id) +/* ---------------------------------------------------------------------------- + * + * Description: Writes an atomic proposition into a stream. + * + * Arguments: estream -- A reference to an exception-aware output stream. + * atom_id -- Numeric identifier of the proposition. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + estream << 'p' << atom_id; +} + + + +/****************************************************************************** + * + * Inline function definitions for template class ConstantWriter. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline void ConstantWriter::write(Exceptional_ostream& estream) +/* ---------------------------------------------------------------------------- + * + * Description: Writes a Boolean constant into a stream. + * + * Arguments: estream -- A reference to an exception-aware output stream. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + estream << symbol; +} + + + +/****************************************************************************** + * + * Inline function definitions for template class UnaryOperatorWriter. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline void UnaryOperatorWriter::write + (Exceptional_ostream& estream, int operand) +/* ---------------------------------------------------------------------------- + * + * Description: Writes an unary operator symbol into a stream. + * + * Arguments: estream -- A reference to an exception-aware output stream. + * operand -- Identifies the state of the depth-first search + * that manages the writing. The operator will be + * written to the stream if and only if `operand' + * has the value 0. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (operand == 0) + estream << symbol << ' '; +} + + + +/****************************************************************************** + * + * Inline function definitions for template class BinaryOperatorPrefixWriter. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline void BinaryOperatorPrefixWriter::write + (Exceptional_ostream& estream, int operand) +/* ---------------------------------------------------------------------------- + * + * Description: Writes a binary operator symbol into a stream. + * + * Arguments: estream -- A reference to an exception-aware output stream. + * operand -- Identifies the state of the depth-first search + * that manages thel writing. This value is used to + * decide what to write to the stream. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (operand == 0) + estream << symbol << ' '; + else if (operand == 1) + estream << ' '; +} + + + +/****************************************************************************** + * + * Inline function definitions for template class BinaryOperatorInfixWriter. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline void BinaryOperatorInfixWriter::write + (Exceptional_ostream& estream, int operand) +/* ---------------------------------------------------------------------------- + * + * Description: Writes a binary operator symbol into a stream. + * + * Arguments: estream -- A reference to an exception-aware output stream. + * operand -- Identifies the state of the depth-first search + * that manages the writing. This value is + * used to decide what to write to the stream. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (operand == 0) + estream << '('; + else if (operand == 1) + estream << ' ' << symbol << ' '; + else + estream << ')'; +} + + + +/****************************************************************************** + * + * Function definitions for template class FormulaWriter. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +void FormulaWriter:: +operator()(const LtlFormula* f, int operand) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the formula writing operation. + * + * Arguments: f -- A pointer to a constant LtlFormula. + * operand -- Used for checking when to write certain `extra' + * symbols (parentheses or spaces) to the stream. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + switch (f->what()) + { + case LTL_ATOM : + AtomWriter::write(estream, static_cast(f)->getId()); + break; + + case LTL_TRUE : + TrueWriter::write(estream); + break; + + case LTL_FALSE : + FalseWriter::write(estream); + break; + + case LTL_NEGATION : + NotWriter::write(estream, operand); + break; + + case LTL_NEXT : + NextWriter::write(estream, operand); + break; + + case LTL_FINALLY : + FinallyWriter::write(estream, operand); + break; + + case LTL_GLOBALLY : + GloballyWriter::write(estream, operand); + break; + + case LTL_CONJUNCTION : + AndWriter::write(estream, operand); + break; + + case LTL_DISJUNCTION : + OrWriter::write(estream, operand); + break; + + case LTL_IMPLICATION : + ImplyWriter::write(estream, operand); + break; + + case LTL_EQUIVALENCE : + EquivWriter::write(estream, operand); + break; + + case LTL_XOR : + XorWriter::write(estream, operand); + break; + + case LTL_UNTIL : + UntilWriter::write(estream, operand); + break; + + case LTL_V : + ReleaseWriter::write(estream, operand); + break; + + case LTL_WEAK_UNTIL : + WeakUntilWriter::write(estream, operand); + break; + + case LTL_STRONG_RELEASE : + StrongReleaseWriter::write(estream, operand); + break; + + default : /* LTL_BEFORE */ + BeforeWriter::write(estream, operand); + break; + } +} + +} + +#endif /* !FORMULAWRITER_H */ diff --git a/lbtt/src/Graph.h.in b/lbtt/src/Graph.h.in new file mode 100644 index 000000000..f701b58a3 --- /dev/null +++ b/lbtt/src/Graph.h.in @@ -0,0 +1,1859 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef GRAPH_H +#define GRAPH_H + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SLIST +@INCLUDE_SLIST_HEADER@ +using SLIST_NAMESPACE::slist; +#endif /* HAVE_SLIST */ + +#include +#include +#include "Alloc.h" +#include "BitArray.h" +#include "Exception.h" + +using namespace std; + +namespace Graph +{ + +/* + * Output formats for printing a graph, its edges or nodes. All output + * functions for these objects will accept a parameter `fmt' which can be + * either of these constants (and defaults to NORMAL). + * `NORMAL' corresponds to a plain text description of the printed object. + * `DOT' corresponds to the format recognized by the tool `dot' (a tool + * for displaying graphs graphically), so this format can be used + * for generating input files for the tool. + */ + +enum GraphOutputFormat {NORMAL, DOT}; + + + +class BuchiAutomaton; +class StateSpace; +class ProductAutomaton; + + + +/****************************************************************************** + * + * A template base class for directed graphs. The template parameter specifies + * a class that will be used for storing the collection of edges beginning at + * the nodes of the graph. That is, each graph node will include a container + * (an object of the given class) storing all the edges beginning at the node. + * + * Different containers can be used to optimize the efficiency of different + * graph algorithms. For example, if the edges are often accessed randomly, a + * container which supports fast searching of any edge in the container may + * be useful: on the other hand, algorithms which only need to go through + * all the edges in some order will benefit from a more simple container since + * this container may consume less memory per container element. + * + * Definitions are provided for the following container classes: + * EdgeList -- Uses the STL `list' class (a doubly linked list) to store + * the edges. + * EdgeVector -- Uses the STL `vector' class to store the edges. + * EdgeSet -- Uses the STL `set' class to store the edges. Important + * note: This container will not allow multiple edges + * between a pair of nodes -- use EdgeMultiSet instead. + * EdgeMultiSet -- Uses the STL `multiset' class to store the edges. + * + * In addition, if using the SGI STL implementation, an additional container + * class `EdgeSlist' is available. This class is based on the `slist' (a + * singly linked list) container included in the SGI STL implementation. + * + * Any class used as a container (`EdgeContainer') must be able to hold + * objects of type Graph::Edge* and must support the following + * interface: + * + * Default constructor which can be called without arguments. + * + * EdgeContainer::size_type + * Data type able to represent the maximum number of elements + * that can be stored in the container. + * + * EdgeContainer::size_type size() + * Returns the number of elements currently stored in the + * containers. + * + * bool empty() + * Tells whether the container is currently empty. + * + * void clear() + * Makes the container empty. + * + * EdgeContainer::iterator + * EdgeContainer::const_iterator + * Input iterators that can be used to traverse through the + * elements of the container. The iterators must support + * increment and dereferencing operations. However, support for + * changing the container elements through the iterator is not + * required. + * + * const_iterator EdgeContainer::begin() + * Returns a const_iterator pointing to the first element in the + * container. + * + * iterator EdgeContainer::begin() + * Returns an iterator pointing to the last element in the + * container. + * + * const_iterator EdgeContainer::end() + * Returns a const_iterator pointing to the end of the container. + * + * iterator EdgeContainer::end() + * Returns an iterator pointing to the end of the container. + * + * const_iterator find(const Graph::Edge*) + * Finds an element of a given value in the container and returns + * a const_iterator pointing to it (or end() if the element is + * not found in the container). + * + * Very important note: + * In order for all the algorithms provided in this class to + * work correctly, searching an element in the container must + * be done by comparing the actual objects pointed to by the + * elements of the container, _not_ the pointers itself. + * + * The class Graph::Edge already provides two + * classes for this purpose: + * 1. Graph::Edge::ptr_less defines a `less + * than' relation between two pointers to + * Graph::Edges + * 2. Graph::Edge::ptr_equal defines an + * `equality' relation between two pointers to + * Graph::Edges. + * + * See the comments on these classes (and the provided + * container class examples) for information on how to use + * them. + * + * iterator find(const Graph::Edge*) + * Finds an element of a given value in the container and returns + * a iterator pointing to it (or end() if the element is not + * found in the container). + * + * insert(const Graph::Edge*) + * Inserts an edge into the container. + * + * erase(iterator pos) + * Removes the element pointed by the iterator `pos' from the + * container. + * + *****************************************************************************/ + +template +class Graph +{ +public: + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class Edge; /* A class for representing + * edges between graph + * nodes. + */ + + class Node; /* A class for representing + * graph nodes. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +protected: + vector nodes; /* Nodes of the graph. + * Derived classes can + * access this vector + * directly. + */ + +public: + typedef typename /* Type definition for */ + vector::size_type /* the size of the */ + size_type; /* graph. The size can + * be no greater than + * the maximum size of + * the vector containing + * the graph nodes. + */ + + typedef pair StateIdPair; /* Type definition for a + * pair of state + * identifiers in a graph. + */ + + explicit Graph /* Default constructor. */ + (const size_type initial_number_of_nodes = 0); + + Graph(const Graph& graph); /* Copy constructor. */ + + virtual ~Graph(); /* Destructor. */ + + Graph& operator=(const Graph& graph); /* Assignment operator. */ + + virtual Node& operator[](const size_type index) /* Indexing operator */ + const; /* (for referencing the + * nodes of the graph). + * This function will + * not check whether the + * given index is in a + * valid range. + */ + + virtual Node& node(const size_type index) const; /* Alternative way for + * referencing the graph + * nodes. This function + * also checks the range of + * the argument. + */ + + size_type size() const; /* Returns the number of + * nodes in the graph. + */ + + bool empty() const; /* Tests whether the graph + * is empty. + */ + + virtual void clear(); /* Makes the graph empty. + */ + + virtual size_type expand /* Inserts nodes to the */ + (size_type node_count = 1); /* graph. */ + + virtual void connect /* Inserts an edge */ + (const size_type father, /* between two nodes. */ + const size_type child); + + virtual void disconnect /* Removes an edge from */ + (const size_type father, /* between two nodes. */ + const size_type child); + + virtual bool connected /* Tests whether two */ + (const size_type father, /* nodes are connected */ + const size_type child) const; /* with an edge. */ + + virtual pair /* Returns the number of */ + stats() const; /* nodes and edges in + * the graph. + */ + + virtual pair /* Returns the number of */ + subgraphStats(const size_type index) const; /* nodes and edges in a + * connected subgraph of + * the graph. + */ + + virtual void print /* Prints the contents */ + (ostream& stream = cout, /* of the graph in */ + const int indent = 0, /* various formats */ + const GraphOutputFormat fmt = NORMAL) const; /* (determined by the + * `fmt' argument which + * can have the values + * NORMAL or DOT). + */ +}; + + + +/****************************************************************************** + * + * A template class for representing the directed edges between graph nodes. + * + *****************************************************************************/ + +template +class Graph::Edge +{ +public: + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class ptr_equal /* Class for a function */ + { /* object defining an */ + public: /* equality relation */ + bool operator() /* between pointers to */ + (const Edge* edge_1, const Edge* edge_2) /* Edges. (Used with */ + const; /* container */ + }; /* algorithms.) */ + + class ptr_less /* Class for a function */ + { /* object defining a */ + public: /* `less than' relation */ + bool operator() /* between pointers to */ + (const Edge* edge_1, const Edge* edge_2) /* Edges. (Used with */ + const; /* container */ + }; /* algorithms.) */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + Edge /* Constructor. */ + (const Graph::size_type target); + + /* default copy constructor */ + + virtual ~Edge(); /* Destructor. */ + + /* default assignment operator */ + + Graph::size_type targetNode() /* Returns the index of */ + const; /* the target node of + * the directed edge. + */ + + virtual void print /* Writes information */ + (ostream& stream = cout, /* about the edge to a */ + const int indent = 0, /* stream in various */ + const GraphOutputFormat fmt = NORMAL) const; /* formats (determined + * by the `fmt' + * argument). + */ + + friend class ptr_equal; + friend class ptr_less; + +protected: + virtual bool operator==(const Edge& edge) const; /* Equality relation + * between two edges. Used + * for sorting Edges in an + * STL container. + */ + + virtual bool operator<(const Edge& edge) const; /* `Less than' relation + * between two edges. Used + * for sorting Edges in an + * STL container. + */ + +private: + Graph::size_type target_node; /* Identifier of the edge's + * target node. + */ +}; + + + +/****************************************************************************** + * + * A template class for representing graph nodes. + * + *****************************************************************************/ + +template +class Graph::Node +{ +public: + Node(); /* Constructor. */ + + Node(const Node& node); /* Copy constructor. */ + + virtual ~Node(); /* Destructor. */ + + Node& operator=(const Node& node); /* Assignment operator. */ + + const EdgeContainer& edges() const; /* Returns the container of + * edges beginning at the + * node. + */ + + virtual void print /* Writes information */ + (ostream& stream = cout, /* about the node to a */ + const int indent = 0, /* stream in various */ + const GraphOutputFormat fmt = NORMAL) const; /* formats (determined + * by the `fmt' + * argument). + */ + +protected: + friend class Graph; + friend class BuchiAutomaton; + friend class StateSpace; + friend class ProductAutomaton; + + EdgeContainer outgoing_edges; /* Container of edges + * beginning at the node. + */ +}; + + + +/****************************************************************************** + * + * An exception class for reporting errors when indexing graph nodes. + * + *****************************************************************************/ + +class NodeIndexException : public Exception +{ +public: + NodeIndexException(); /* Default constructor. */ + + /* default copy constructor */ + + ~NodeIndexException() throw(); /* Destructor. */ + + NodeIndexException& operator= /* Assignment operator. */ + (const NodeIndexException& e); + + /* `what' inherited from class Exception */ +}; + + + +/****************************************************************************** + * + * An edge container class based on the basic STL container class `list', a + * doubly linked list. + * + *****************************************************************************/ + +class EdgeList : public list::Edge*, + ALLOC(Graph::Edge*) > +{ +public: + EdgeList(); /* Constructor. */ + + /* default copy constructor */ + + ~EdgeList(); /* Destructor. */ + + /* default assignment operator */ + + void insert(Graph::Edge* edge); /* Inserts an element to + * the end of the list. + */ + + list::Edge*, /* Functions for finding */ + ALLOC(Graph::Edge*) > /* an element in the */ + ::const_iterator /* list. */ + find(const Graph::Edge* edge) const; + + list::Edge*, + ALLOC(Graph::Edge*) > + ::iterator + find(const Graph::Edge* edge); +}; + + + +/****************************************************************************** + * + * An edge container class based on the basic container class `slist' + * (available in SGI STL implementation), a singly linked list. + * + *****************************************************************************/ + +#ifdef HAVE_SLIST + +class EdgeSlist : public slist::Edge*, + ALLOC(Graph::Edge*) > +{ +public: + EdgeSlist(); /* Constructor. */ + + /* default copy constructor */ + + ~EdgeSlist(); /* Destructor. */ + + /* default assignment operator */ + + void insert(Graph::Edge* edge); /* Inserts an element to + * the beginning of the + * list. + */ + + slist::Edge*, /* Functions for finding */ + ALLOC(Graph::Edge*) > /* an element in the */ + ::const_iterator /* list. */ + find(const Graph::Edge* edge) const; + + slist::Edge*, + ALLOC(Graph::Edge*) > + ::iterator + find(const Graph::Edge* edge); +}; + +#endif /* HAVE_SLIST */ + + + +/****************************************************************************** + * + * An edge container class based on the basic STL container class `vector'. + * + *****************************************************************************/ + +class EdgeVector : public vector::Edge*, + ALLOC(Graph::Edge*) > +{ +public: + EdgeVector(); /* Constructor. */ + + /* default copy constructor */ + + ~EdgeVector(); /* Destructor. */ + + /* default assignment operator */ + + void insert(Graph::Edge* edge); /* Inserts an element to + * the vector of pointers + * to edges. + */ + + vector::Edge*, /* Functions for finding */ + ALLOC(Graph::Edge*) > /* an element in the */ + ::const_iterator /* container. */ + find(const Graph::Edge* edge) + const; + + vector::Edge*, + ALLOC(Graph::Edge*) > + ::iterator + find(const Graph::Edge* edge); +}; + + + +/****************************************************************************** + * + * An edge container class based on the basic STL container class `set'. + * + *****************************************************************************/ + +class EdgeSet : public set::Edge*, + Graph::Edge::ptr_less, + ALLOC(Graph::Edge*) > +{ +}; + + + +/****************************************************************************** + * + * An edge container class based on the basic STL container class `multiset'. + * + *****************************************************************************/ + +class EdgeMultiSet : public multiset::Edge*, + Graph::Edge::ptr_less, + ALLOC(Graph::Edge*) > +{ +}; + + + +/****************************************************************************** + * + * Inline function definitions for template class Graph. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Graph::~Graph() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for template class Graph. + * Deallocates the memory reserved for the object. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + clear(); +} + +/* ========================================================================= */ +template +inline typename Graph::Node& Graph::operator[] + (const size_type index) const +/* ---------------------------------------------------------------------------- + * + * Description: Indexing operator for template class Graph. + * Can be used to access single nodes of the graph. This + * function does not perform any range checks on its argument. + * + * Argument: index -- Index of a node. + * + * Returns: A reference to a graph node. + * + * ------------------------------------------------------------------------- */ +{ + return *nodes[index]; +} + +/* ========================================================================= */ +template +inline typename Graph::size_type Graph::size() + const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the number of nodes in the graph. + * + * Arguments: None. + * + * Returns: Number of nodes in the graph. + * + * ------------------------------------------------------------------------- */ +{ + return nodes.size(); +} + +/* ========================================================================= */ +template +inline bool Graph::empty() const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether the graph is empty. + * + * Arguments: None. + * + * Returns: A truth value. + * + * ------------------------------------------------------------------------- */ +{ + return nodes.empty(); +} + +/* ========================================================================= */ +template +inline ostream& operator<<(ostream& stream, const Graph& graph) +/* ---------------------------------------------------------------------------- + * + * Description: Defines an alternative way for printing a Graph by using the + * << operator. + * + * Arguments: stream -- A reference to an output stream. + * graph -- A reference to a constant graph to be printed. + * + * Returns: A reference to the output stream. + * + * ------------------------------------------------------------------------- */ +{ + graph.print(stream); + return stream; +} + + + +/****************************************************************************** + * + * Function definitions for template class Graph. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +Graph::Graph(const size_type initial_number_of_nodes) : + nodes(initial_number_of_nodes) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for template class Graph. + * Initializes a new Graph object with a given + * initial size (which can be extended later). + * + * Argument: initial_number_of_nodes -- Initial size of the graph. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + nodes.reserve(initial_number_of_nodes); + + for (typename vector::iterator node = nodes.begin(); + node != nodes.end(); + ++node) + *node = new Node(); +} + +/* ========================================================================= */ +template +Graph::Graph(const Graph& graph) +/* ---------------------------------------------------------------------------- + * + * Description: Copy constructor for template class Graph. + * Initializes a copy of a Graph object. + * + * Argument: graph -- A reference to a constant Graph + * to be copied. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + nodes.reserve(graph.nodes.size()); + for (typename vector::const_iterator + node = graph.nodes.begin(); + node != graph.nodes.end(); ++node) + nodes.push_back(new Node(**node)); +} + +/* ========================================================================= */ +template +Graph& Graph::operator= + (const Graph& graph) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for template class Graph. + * Copies a Graph object to another. + * + * Argument: graph -- A reference to the constant Graph + * to be copied. + * + * Returns: A reference to the graph whose contents were changed. + * + * ------------------------------------------------------------------------- */ +{ + if (&graph != this) + { + clear(); + + nodes.reserve(graph.nodes.size()); + for (typename vector::const_iterator + node = graph.nodes.begin(); + node != graph.nodes.end(); + ++node) + nodes.push_back(new Node(**node)); + } + + return *this; +} + +/* ========================================================================= */ +template +typename Graph::Node& Graph::node + (const size_type index) const +/* ---------------------------------------------------------------------------- + * + * Description: Function for accessing individual nodes of the graph. This + * function also performs a range check on its argument. + * + * Argument: index -- Index of the node. + * + * Returns: A reference to the node. + * + * ------------------------------------------------------------------------- */ +{ + if (index >= nodes.size()) + throw NodeIndexException(); + + return *nodes[index]; +} + +/* ========================================================================= */ +template +void Graph::clear() +/* ---------------------------------------------------------------------------- + * + * Description: Makes the graph empty. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + for (typename vector::reverse_iterator + node = nodes.rbegin(); + node != nodes.rend(); + ++node) + delete *node; + + nodes.clear(); + nodes.reserve(0); +} + +/* ========================================================================= */ +template +typename Graph::size_type Graph::expand + (size_type node_count) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a given number of nodes to a graph. + * + * Arguments: node_count -- Number of nodes to be inserted. + * + * Returns: The index of the last inserted node. + * + * ------------------------------------------------------------------------- */ +{ + nodes.reserve(nodes.size() + node_count); + + Node* new_node; + + while (node_count > 0) + { + new_node = new Node(); + try + { + nodes.push_back(new_node); + } + catch (...) + { + delete new_node; + throw; + } + node_count--; + } + + return nodes.size() - 1; +} + +/* ========================================================================= */ +template +void Graph::connect + (const size_type father, const size_type child) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts an edge between two nodes of the graph. + * + * Arguments: father -- Source node of the edge. + * child -- Target node of the edge. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Edge* new_edge(new Edge(child)); + + try + { + nodes[father]->outgoing_edges.insert(new_edge); + } + catch (...) + { + delete new_edge; + throw; + } +} + +/* ========================================================================= */ +template +void Graph::disconnect + (const size_type father, const size_type child) +/* ---------------------------------------------------------------------------- + * + * Description: Removes an edge between two nodes of the graph. + * + * Arguments: father -- Source node of the edge. + * child -- Target node of the edge. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Edge e(child); + + /* + * Scan the set of the node's outgoing edges for an edge with the given + * target node and remove it if such an edge exists. + */ + + typename EdgeContainer::iterator search_edge + = nodes[father]->outgoing_edges.find(&e); + + if (search_edge != nodes[father]->outgoing_edges.end()) + { + delete *search_edge; + nodes[father]->outgoing_edges.erase(search_edge); + } +} + +/* ========================================================================= */ +template +bool Graph::connected + (const size_type father, const size_type child) const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether there exists an edge from a given graph node + * to another one. + * + * Arguments: father -- Source node of the edge. + * child -- Target node of the edge. + * + * Returns: A truth value. + * + * ------------------------------------------------------------------------- */ +{ + Edge e(child); + return (nodes[father]->outgoing_edges.find(&e) + != nodes[father]->outgoing_edges.end()); +} + +/* ========================================================================= */ +template +pair::size_type, unsigned long int> +Graph::stats() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the number of nodes and edges in a graph. + * + * Arguments: None. + * + * Returns: A pair consisting of the number of nodes and the number of + * transitions in the graph. + * + * ------------------------------------------------------------------------- */ +{ + pair::size_type, unsigned long int> result; + + result.first = nodes.size(); + result.second = 0; + + for (typename vector::const_iterator + node = nodes.begin(); + node != nodes.end(); ++node) + result.second += (*node)->edges().size(); + + return result; +} + +/* ========================================================================= */ +template +pair::size_type, unsigned long int> +Graph::subgraphStats(const size_type index) const +/* ---------------------------------------------------------------------------- + * + * Description: Computes the number of nodes and edges in some connected + * subgraph of the graph. The subgraph is computed by performing + * a depth-first traversal of a graph component starting from a + * given node (the `root' of the subgraph). + * + * Arguments: index -- Index of the node acting as the `root' of the + * subgraph. + * + * Returns: A pair consisting of the number of nodes and the number of + * transitions in the subgraph. + * + * ------------------------------------------------------------------------- */ +{ + if (empty()) + return make_pair(0, 0); + + const size_type s = nodes.size(); + + if (index >= s) + throw NodeIndexException(); + + stack > unprocessed_nodes; + BitArray visited_nodes(s); + visited_nodes.clear(s); + + unprocessed_nodes.push(index); + visited_nodes.setBit(index); + + size_type current_node, child_node, number_of_subgraph_nodes = 0; + unsigned long int number_of_subgraph_edges = 0; + + while (!unprocessed_nodes.empty()) + { + current_node = unprocessed_nodes.top(); + unprocessed_nodes.pop(); + + ++number_of_subgraph_nodes; + number_of_subgraph_edges += nodes[current_node]->edges().size(); + + for (typename EdgeContainer::const_iterator + edge = nodes[current_node]->edges().begin(); + edge != nodes[current_node]->edges().end(); + ++edge) + { + child_node = (*edge)->targetNode(); + if (!visited_nodes.test(child_node)) + { + unprocessed_nodes.push(child_node); + visited_nodes.setBit(child_node); + } + } + } + + return make_pair(number_of_subgraph_nodes, number_of_subgraph_edges); +} + +/* ========================================================================= */ +template +void Graph::print + (ostream& stream, const int indent, const GraphOutputFormat fmt) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about the graph to a stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * fmt -- Determines the graph output format. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + if (fmt == DOT) + estream << string(indent, ' ') + "digraph G {\n"; + + if (nodes.empty()) + { + if (fmt == NORMAL) + estream << string(indent, ' ') + "The graph is empty.\n"; + } + else + { + if (fmt == NORMAL) + { + pair statistics = stats(); + + estream << string(indent, ' ') + "The graph consists of\n" + + string(indent + 4, ' ') + << statistics.first + << " states and\n" + string(indent + 4, ' ') + << statistics.second + << " transitions.\n"; + } + + size_type s = nodes.size(); + for (size_type node = 0; node < s; ++node) + { + estream << string(indent, ' '); + if (fmt == NORMAL) + { + estream << "Node " << node << ":\n"; + nodes[node]->print(stream, indent + 4, fmt); + } + else if (fmt == DOT) + { + typename EdgeContainer::const_iterator edge; + + estream << " n" << node << " [shape=circle,label=\"" << node + << "\",fontsize=12];\n"; + + for (edge = nodes[node]->edges().begin(); + edge != nodes[node]->edges().end(); + ++edge) + { + estream << string(indent + 2, ' ') + 'n' << node; + (*edge)->print(stream, 0, fmt); + estream << ";\n"; + } + } + } + } + + if (fmt == DOT) + estream << string(indent, ' ') + "}\n"; + + estream.flush(); +} + + + +/****************************************************************************** + * + * Inline function definitions for class Graph::Edge. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Graph::Edge::Edge + (const Graph::size_type target) : target_node(target) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for template class Graph::Edge. + * Creates a new edge and initializes its target node. + * + * Arguments: target -- Identifier of the edge's target node. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline Graph::Edge::~Edge() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for template class Graph::Edge. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline bool Graph::Edge::operator==(const Edge& edge) const +/* ---------------------------------------------------------------------------- + * + * Description: Equality relation for class Graph::Edge. Two + * edges are `equal' if and only if their target nodes have the + * same identifier. + * + * Argument: edge -- A reference to a constant + * Graph::Edge. + * + * Returns: Truth value according to the relationship between the two + * edges. + * + * ------------------------------------------------------------------------- */ +{ + return (target_node == edge.target_node); +} + +/* ========================================================================= */ +template +inline bool Graph::Edge::operator<(const Edge& edge) const +/* ---------------------------------------------------------------------------- + * + * Description: `Less than' relation for class Graph::Edge. An + * edge is `less than' another if and only if the edge's target + * node has a smaller identifier than that of the other edge. + * + * Argument: edge -- A reference to a constant + * Graph::Edge. + * + * Returns: Truth value according to the relationship between the two + * edges. + * + * ------------------------------------------------------------------------- */ +{ + return (target_node < edge.target_node); +} + +/* ========================================================================= */ +template +inline typename Graph::size_type +Graph::Edge::targetNode() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the identifier of the edge's target node. + * + * Arguments: None. + * + * Returns: Identifier of the target node. + * + * ------------------------------------------------------------------------- */ +{ + return target_node; +} + +/* ========================================================================= */ +template +inline ostream& operator<< + (ostream& stream, const typename Graph::Edge& edge) +/* ---------------------------------------------------------------------------- + * + * Description: Defines an alternative way for printing a + * Graph::Edge by using the << operator. + * + * Arguments: stream -- A reference to an output stream. + * edge -- A reference to a constant edge to be printed. + * + * Returns: A reference to the output stream. + * + * ------------------------------------------------------------------------- */ +{ + edge.print(stream); + return stream; +} + + + +/****************************************************************************** + * + * Function definitions for class Graph::Edge. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +void Graph::Edge::print + (ostream& stream, const int indent, const GraphOutputFormat fmt) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about the edge to a stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to write before the edge + * description. + * fmt -- Determines the output format of the edge. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + if (fmt == NORMAL) + estream << string(indent, ' ') << "Edge to node " << target_node << '\n'; + else if (fmt == DOT) + estream << " -> n" << target_node; + + estream.flush(); +} + + + +/****************************************************************************** + * + * Inline function definitions for class Graph::Edge::ptr_equal. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline bool Graph::Edge::ptr_equal::operator() + (const Graph::Edge* edge_1, + const Graph::Edge* edge_2) const +/* ---------------------------------------------------------------------------- + * + * Description: Function to test the `equality' of two pointers to + * Graph::Edges, the other one of which is stored + * in the function object for whom this member function is + * called. + * + * Arguments: edge_1, edge_2 -- Two pointers to constant + * Graph::Edges. + * + * Returns: A truth value according to the result of the equality test. + * + * ------------------------------------------------------------------------- */ +{ + return (*edge_1 == *edge_2); +} + + + +/****************************************************************************** + * + * Inline function definitions for class Graph::Edge::ptr_less. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline bool Graph::Edge::ptr_less::operator() + (const Graph::Edge* edge_1, + const Graph::Edge* edge_2) const +/* ---------------------------------------------------------------------------- + * + * Description: Defines a `less than' relation between two pointers to + * Graph::Edges by mapping the relation between + * the pointers to the relation between the objects itself. + * (Used for storing pointers to Edges into a container.) + * + * Arguments: edge_1, edge_2 -- Two pointers to constant + * Graph::Edges. + * + * Returns: Truth value according to the relationship of the two edges + * involved. + * + * ------------------------------------------------------------------------- */ +{ + return (*edge_1 < *edge_2); +} + + + +/****************************************************************************** + * + * Inline function definitions for class Graph::Node. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Graph::Node::Node() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Graph::Node. Creates a + * new node. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline const EdgeContainer& Graph::Node::edges() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the set of edges starting from the node. + * + * Arguments: None. + * + * Returns: A reference to the constant container of pointers to the + * edges starting from the node. + * + * ------------------------------------------------------------------------- */ +{ + return outgoing_edges; +} + +/* ========================================================================= */ +template +inline ostream& operator<< + (ostream& stream, const typename Graph::Node& node) +/* ---------------------------------------------------------------------------- + * + * Description: An alternative method for printing a + * Graph::Node object by using the << operator. + * + * Arguments: stream -- A reference to an output stream. + * node -- Node to be printed. + * + * Returns: A reference to the output stream. + * + * ------------------------------------------------------------------------- */ +{ + node.print(stream); + return stream; +} + + + +/****************************************************************************** + * + * Function definitions for class Graph::Node. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +Graph::Node::Node(const Node& node) +/* ---------------------------------------------------------------------------- + * + * Description: Copy constructor for Graph::Node. Initializes + * a copy of a Node. + * + * Argument: node -- A reference to a constant node to be copied. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + typename EdgeContainer::const_iterator edge; + + for (edge = node.outgoing_edges.begin(); edge != node.outgoing_edges.end(); + ++edge) + outgoing_edges.insert(new Edge(**edge)); +} + +/* ========================================================================= */ +template +Graph::Node::~Node() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for Graph::Node. Deallocates the + * memory reserved by the object. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + typename EdgeContainer::iterator edge; + + for (edge = outgoing_edges.begin(); edge != outgoing_edges.end(); ++edge) + delete *edge; +} + +/* ========================================================================= */ +template +typename Graph::Node& Graph::Node::operator= + (const Graph::Node& node) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class Graph::Node. + * Assigns a graph node a copy of another one. + * + * Argument: node -- A reference to the constant node to be copied. + * + * Returns: A reference to the graph node whose contents were changed. + * + * ------------------------------------------------------------------------- */ +{ + if (&node != this) + { + typename EdgeContainer::iterator edge; + + for (edge = outgoing_edges.begin(); edge != outgoing_edges.end(); ++edge) + delete *edge; + + outgoing_edges.clear(); + + for (edge = node.outgoing_edges.begin(); edge != node.outgoing_edges.end(); + ++edge) + outgoing_edges.insert(new Edge(**edge)); + } + + return *this; +} + +/* ========================================================================= */ +template +void Graph::Node::print + (ostream& stream, const int indent, const GraphOutputFormat fmt) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a graph node to a stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * fmt -- Determines the node output format. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (fmt == DOT) + return; + + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + if (outgoing_edges.empty()) + estream << string(indent, ' ') + "The node has no successors.\n"; + else + { + typename EdgeContainer::const_iterator edge; + + estream << string(indent, ' ') + "The node has " + << outgoing_edges.size() + << " successor nodes:\n"; + + for (edge = outgoing_edges.begin(); edge != outgoing_edges.end(); ++edge) + (*edge)->print(stream, indent + 4); + } + + estream.flush(); +} + + + +/****************************************************************************** + * + * Inline function definitions for class NodeIndexException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline NodeIndexException::NodeIndexException() : + Exception("graph node index out of range") +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class NodeIndexException. Creates a new + * exception object. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline NodeIndexException::~NodeIndexException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class NodeIndexException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline NodeIndexException& NodeIndexException::operator= + (const NodeIndexException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class NodeIndexException. + * + * Arguments: e -- A reference to another NodeIndexException. + * + * Returns: A reference to the assigned exception object. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class EdgeList. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline EdgeList::EdgeList() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class EdgeList. Creates a new container based + * on doubly linked lists for storing graph edges. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline EdgeList::~EdgeList() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class EdgeList. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline void EdgeList::insert(Graph::Edge* edge) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a new edge to the end of the list. + * + * Argument: edge -- A pointer to a Graph::Edge. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + push_back(edge); +} + +/* ========================================================================= */ +inline list::Edge*, ALLOC(Graph::Edge*) > + ::const_iterator +EdgeList::find(const Graph::Edge* edge) const +/* ---------------------------------------------------------------------------- + * + * Description: Finds an edge in the EdgeList. + * + * Argument: edge -- Pointer to an edge to be searched in the list. + * Note that during the search, comparison is done + * between the actual values of the edges (not the + * pointers). + * + * Returns: A list::Edge*, ALLOC>::const_iterator + * pointing to the edge in the list or + * list::Edge*, ALLOC>::end() if the edge is + * not found in the list. + * + * ------------------------------------------------------------------------- */ +{ + Graph::Edge::ptr_equal isEqual; + + const_iterator e; + for (e = begin(); e != end() && !isEqual(edge, *e); ++e) + ; + + return e; +} + +/* ========================================================================= */ +inline list::Edge*, ALLOC(Graph::Edge*) >::iterator +EdgeList::find(const Graph::Edge* edge) +/* ---------------------------------------------------------------------------- + * + * Description: Finds an edge in the EdgeList. + * + * Argument: edge -- Pointer to an edge to be searched in the list. + * Note that during the search, comparison is done + * between the actual values of the edges (not the + * pointers). + * + * Returns: A list::Edge*, ALLOC>::iterator pointing + * to the edge in the list or + * list::Edge*, ALLOC>::end() if the edge is + * not found in the list. + * + * ------------------------------------------------------------------------- */ +{ + Graph::Edge::ptr_equal isEqual; + + iterator e; + for (e = begin(); e != end() && !isEqual(edge, *e); ++e) + ; + + return e; +} + + + +/****************************************************************************** + * + * Inline function definitions for class EdgeSlist. + * + *****************************************************************************/ + +#ifdef HAVE_SLIST + +/* ========================================================================= */ +inline EdgeSlist::EdgeSlist() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class EdgeList. Creates a new container based + * on singly linked lists for storing graph edges. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline EdgeSlist::~EdgeSlist() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class EdgeSlist. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline void EdgeSlist::insert(Graph::Edge* edge) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a new edge to the beginning of the list. + * + * Argument: edge -- A pointer to a Graph::Edge. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + push_front(edge); +} + +/* ========================================================================= */ +inline slist::Edge*, ALLOC(Graph::Edge*) > + ::const_iterator +EdgeSlist::find(const Graph::Edge* edge) const +/* ---------------------------------------------------------------------------- + * + * Description: Finds an edge in the EdgeSlist. + * + * Argument: edge -- Pointer to an edge to be searched in the list. + * Note that during the search, comparison is done + * between the actual values of the edges (not the + * pointers). + * + * Returns: A slist::Edge*, ALLOC>::const_iterator + * pointing to the edge in the list or + * slist::Edge*, ALLOC>::end() if the edge + * is not found in the list. + * + * ------------------------------------------------------------------------- */ +{ + Graph::Edge::ptr_equal isEqual; + + const_iterator e; + for (e = begin(); e != end() && !isEqual(edge, *e); ++e) + ; + + return e; +} + +/* ========================================================================= */ +inline slist::Edge*, ALLOC(Graph::Edge*) > + ::iterator +EdgeSlist::find(const Graph::Edge* edge) +/* ---------------------------------------------------------------------------- + * + * Description: Finds an edge in the EdgeSlist. + * + * Argument: edge -- Pointer to an edge to be searched in the list. + * Note that during the search, comparison is done + * between the actual values of the edges (not the + * pointers). + * + * Returns: A slist::Edge*, ALLOC>::iterator + * pointing to the edge in the list or + * slist::Edge*, ALLOC>::end() if the edge + * is not found in the list. + * + * ------------------------------------------------------------------------- */ +{ + Graph::Edge::ptr_equal isEqual; + + iterator e; + for (e = begin(); e != end() && !isEqual(edge, *e); ++e) + ; + + return e; +} + +#endif /* HAVE_SLIST */ + + + +/****************************************************************************** + * + * Inline function definitions for class EdgeVector. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline EdgeVector::EdgeVector() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class EdgeVector. Creates a new container + * based on a vector for storing graph edges. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline EdgeVector::~EdgeVector() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class EdgeVector. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline void EdgeVector::insert(Graph::Edge* edge) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts an edge into the edge vector. + * + * Arguments: edge -- A pointer to a Graph::Edge. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + push_back(edge); +} + +/* ========================================================================= */ +inline vector::Edge*, ALLOC(Graph::Edge*) > + ::const_iterator +EdgeVector::find(const Graph::Edge* edge) const +/* ---------------------------------------------------------------------------- + * + * Description: Finds an edge in the EdgeVector. + * + * Argument: edge -- Pointer to an edge to be searched in the vector. + * Note that during the search, comparison is done + * between the actual values of the edges (not the + * pointers). + * + * Returns: A vector::Edge*, ALLOC>::const_iterator + * pointing to the edge in the container or + * vector::Edge*, ALLOC>::end() if the + * edge is not found in the container. + * + * ------------------------------------------------------------------------- */ +{ + Graph::Edge::ptr_equal isEqual; + + const_iterator e; + for (e = begin(); e != end() && !isEqual(edge, *e); ++e) + ; + + return e; +} + +/* ========================================================================= */ +inline vector::Edge*, ALLOC(Graph::Edge*) > + ::iterator +EdgeVector::find(const Graph::Edge* edge) +/* ---------------------------------------------------------------------------- + * + * Description: Finds an edge in the EdgeVector. + * + * Argument: edge -- Pointer to an edge to be searched in the vector. + * Note that during the search, comparison is done + * between the actual values of the edges (not the + * pointers). + * + * Returns: A vector::Edge*, ALLOC>::iterator + * pointing to the edge in the container or + * vector::Edge*, ALLOC>::end() if the edge + * is not found in the container. + * + * ------------------------------------------------------------------------- */ +{ + Graph::Edge::ptr_equal isEqual; + + iterator e; + for (e = begin(); e != end() && !isEqual(edge, *e); ++e) + ; + + return e; +} + +} + +#endif /* !GRAPH_H */ diff --git a/lbtt/src/LbtWrapper.h b/lbtt/src/LbtWrapper.h new file mode 100644 index 000000000..70319c2da --- /dev/null +++ b/lbtt/src/LbtWrapper.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LBTWRAPPER_H +#define LBTWRAPPER_H + +#include +#include +#include "ExternalTranslator.h" +#include "translate.h" + +/****************************************************************************** + * + * Interface class for lbt. + * + *****************************************************************************/ + +class LbtWrapper : public ExternalTranslator +{ +public: + LbtWrapper(); /* Constructor. */ + + ~LbtWrapper(); /* Destructor. */ + + /* `translateFormula' inherited from ExternalTranslator */ + + /* `formatInput' inherited from ExternalTranslator */ + + string commandLine /* Prepares the command */ + (const string& input_filename, /* line for executing */ + const string&); /* lbt. */ + + /* `execSuccess' inherited from ExternalTranslator */ + + void parseAutomaton /* Dummy function, */ + (const string&, const string&); /* needed to support the + * ExternalTranslator + * interface. + */ + +private: + LbtWrapper(const LbtWrapper&); /* Prevent copying and */ + LbtWrapper& operator=(const LbtWrapper&); /* assignment of + * LbtWrapper objects. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class LbtWrapper. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline LbtWrapper::LbtWrapper() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class LbtWrapper. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline LbtWrapper::~LbtWrapper() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class LbtWrapper. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline string LbtWrapper::commandLine + (const string& input_filename, const string&) +/* ---------------------------------------------------------------------------- + * + * Description: Prepares the command line for lbt. + * + * Arguments: input_filename -- Name of the input file. + * The other argument is only needed for supporting the + * ExternalTranslator interface; the output will be written to + * the filename stored in `command_line_arguments[4]'. + * + * Returns: The command line string. + * + * ------------------------------------------------------------------------- */ +{ + return string(" <") + input_filename + " >" + + string(command_line_arguments[4]); +} + +/* ========================================================================= */ +inline void LbtWrapper::parseAutomaton(const string&, const string&) +/* ---------------------------------------------------------------------------- + * + * Description: Dummy function which is needed to support the + * ExternalTranslator interface. + * + * Arguments: References to two constant strings. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +#endif /* !LBTWRAPPER_H */ diff --git a/lbtt/src/LtlFormula.cc b/lbtt/src/LtlFormula.cc new file mode 100644 index 000000000..5d0aa0cef --- /dev/null +++ b/lbtt/src/LtlFormula.cc @@ -0,0 +1,1329 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include "FormulaWriter.h" +#include "LtlFormula.h" + +namespace Ltl +{ + +map + LtlFormula::formula_storage; + +unsigned long int /* Upper limit for the */ + LtlFormula::eval_proposition_id_limit; /* atomic proposition + * identifiers (used + * when evaluating the + * truth value of the + * formula in a given + * truth assignment). + */ + + + +/****************************************************************************** + * + * Function for obtaining the infix symbol associated with a given + * FormulaType constant. + * + *****************************************************************************/ + +/* ========================================================================= */ +const char* infixSymbol(const int symbol) +/* ---------------------------------------------------------------------------- + * + * Description: Tells the infix symbol corresponding to a given FormulaType + * constant. + * + * Argument: symbol -- A symbol identifier. + * + * Returns: A pointer to a C-style string containing the symbol (or + * "UNKNOWN" if the argument does not correspond to a valid + * FormulaType symbol). + * + * ------------------------------------------------------------------------- */ +{ + switch (symbol) + { + case LTL_TRUE : + return static_cast(LtlTrue::infix_symbol); + + case LTL_FALSE : + return static_cast(LtlFalse::infix_symbol); + + case LTL_ATOM : + return "p"; + + case LTL_NEGATION : + return static_cast(LtlNegation::infix_symbol); + + case LTL_CONJUNCTION : + return static_cast(LtlConjunction::infix_symbol); + + case LTL_DISJUNCTION : + return static_cast(LtlDisjunction::infix_symbol); + + case LTL_IMPLICATION : + return static_cast(LtlImplication::infix_symbol); + + case LTL_EQUIVALENCE : + return static_cast(LtlEquivalence::infix_symbol); + + case LTL_XOR : + return static_cast(LtlXor::infix_symbol); + + case LTL_NEXT : + return static_cast(LtlNext::infix_symbol); + + case LTL_UNTIL : + return static_cast(LtlUntil::infix_symbol); + + case LTL_WEAK_UNTIL : + return static_cast(LtlWeakUntil::infix_symbol); + + case LTL_FINALLY : + return static_cast(LtlFinally::infix_symbol); + + case LTL_V : + return static_cast(LtlV::infix_symbol); + + case LTL_STRONG_RELEASE : + return static_cast(LtlStrongRelease::infix_symbol); + + case LTL_GLOBALLY : + return static_cast(LtlGlobally::infix_symbol); + + case LTL_BEFORE : + return static_cast(LtlBefore::infix_symbol); + + default : + return "UNKNOWN"; + } +} + + + +/****************************************************************************** + * + * A function class for convering an LtlFormula into negation normal form. + * + *****************************************************************************/ + +class NnfConverter +{ +public: + NnfConverter(); /* Constructor. */ + + ~NnfConverter(); /* Destructor. */ + + void operator() /* Implements the */ + (const LtlFormula* f, int operand); /* conversion operation. */ + + LtlFormula* getResult() const; /* Returns the result of + * the conversion. + */ + +private: + stack > + formula_stack; + + stack > + negation_stack; + + NnfConverter(const NnfConverter&); /* Prevent copying and */ + NnfConverter& operator=(const NnfConverter&); /* assignment of + * NnfConverter objects. + */ +}; + + + +/****************************************************************************** + * + * A function class for computing the size of the parse tree of a formula. + * + *****************************************************************************/ + +class FormulaSizeCounter +{ +public: + FormulaSizeCounter(); /* Constructor. */ + + ~FormulaSizeCounter(); /* Destructor. */ + + void operator()(const LtlFormula*, int); /* Implements the node + * counting operation. + */ + + unsigned long int size; /* Node count. */ + +private: + FormulaSizeCounter(const FormulaSizeCounter&); /* Prevent copying and */ + FormulaSizeCounter& operator= /* assignment of */ + (const FormulaSizeCounter&); /* FormulaSizeCounter + * objects. + */ +}; + + + +/****************************************************************************** + * + * A function class for collecting the subformulae of an LtlFormula into a + * stack. + * + *****************************************************************************/ + +class SubformulaCollector +{ +public: + SubformulaCollector /* Constructor. */ + (stack >& + result_stack); + + ~SubformulaCollector(); /* Destructor. */ + + void operator()(const LtlFormula* f, int); /* Implements the + * subformula collection + * operation. + */ + +private: + stack >& + subformula_stack; + + SubformulaCollector(const SubformulaCollector&); /* Prevent copying and */ + SubformulaCollector& operator= /* assignment of */ + (const SubformulaCollector&); /* SubformulaCollector + * objects. + */ +}; + + + +/****************************************************************************** + * + * A function class for finding the largest atom identifier in a LtlFormula. + * + *****************************************************************************/ + +class MaxAtomFinder +{ +public: + MaxAtomFinder(); /* Constuctor. */ + + ~MaxAtomFinder(); /* Destructor. */ + + void operator()(const LtlFormula* f, int); /* Implements the atom + * identifier search + * operation. + */ + + long int getResult() const; /* Returns the result of + * the operation. + */ + +private: + long int max_atom_id; /* Largest identifier for + * an atom in an + * LtlFormula. + */ + + MaxAtomFinder(const SubformulaCollector&); /* Prevent copying and */ + MaxAtomFinder& operator= /* assignment of */ + (const MaxAtomFinder&); /* MaxAtomFinder + * objects. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class NnfConverter. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline NnfConverter::NnfConverter() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class NnfConverter. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + negation_stack.push(true); +} + +/* ========================================================================= */ +inline NnfConverter::~NnfConverter() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class NnfConverter. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + while (!formula_stack.empty()) + { + LtlFormula::destruct(formula_stack.top()); + formula_stack.pop(); + } +} + +/* ========================================================================= */ +inline LtlFormula* NnfConverter::getResult() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the result of negation normal form conversion or + * `static_cast(0)' if there is no result + * available. + * + * Arguments: None. + * + * Returns: A pointer to the result of the conversion. + * + * ------------------------------------------------------------------------- */ +{ + if (!formula_stack.empty()) + return formula_stack.top()->clone(); + else + return static_cast(0); +} + + + +/****************************************************************************** + * + * Inline function definitions for class FormulaSizeCounter. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline FormulaSizeCounter::FormulaSizeCounter() : + size(0) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class FormulaSizeCounter. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FormulaSizeCounter::~FormulaSizeCounter() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class FormulaSizeCounter. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline void FormulaSizeCounter::operator()(const LtlFormula*, int) +/* ---------------------------------------------------------------------------- + * + * Description: Implementation for the parse tree node counting operation. + * + * Arguments: The arguments are needed only for supporting the function + * interface. + * + * Returns: Nothing; increments `this->size' by 1. + * + * ------------------------------------------------------------------------- */ +{ + ++size; +} + + + +/****************************************************************************** + * + * Inline function definitions for class SubformulaCollector. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline SubformulaCollector::SubformulaCollector + (stack >& + result_stack) : + subformula_stack(result_stack) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class SubformulaCollector. + * + * Arguments: result_stack -- A stack of constant LtlFormulae for + * collecting the results. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline SubformulaCollector::~SubformulaCollector() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class SubformulaCollector. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline void SubformulaCollector::operator()(const LtlFormula* f, int) +/* ---------------------------------------------------------------------------- + * + * Description: Implementation for the subformula collection operation. + * + * Arguments: f -- A pointer to a constant LtlFormula. + * The other argument is needed only for supporting the function + * interface. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + subformula_stack.push(f); +} + + + +/****************************************************************************** + * + * Inline function definitions for class MaxAtomFinder. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline MaxAtomFinder::MaxAtomFinder() : + max_atom_id(-1) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class MaxAtomFinder. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline MaxAtomFinder::~MaxAtomFinder() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class MaxAtomFinder. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline void MaxAtomFinder::operator()(const LtlFormula* f, int) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the search for the largest atomic proposition in + * an LtlFormula. + * + * Arguments: f -- A pointer to a constant LtlFormula. + * The other argument is needed only for supporting the function + * interface. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (f->what() == LTL_ATOM + && static_cast(f)->getId() > max_atom_id) + max_atom_id = static_cast(f)->getId(); +} + +/* ========================================================================= */ +inline long int MaxAtomFinder::getResult() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the largest identifier of an atomic proposition + * found by the MaxAtomFinder object. + * + * Arguments: None. + * + * Returns: Largest identifier for an atomic proposition in an + * LtlFormula or -1 if the formula does not contain any + * atomic propositions. + * + * ------------------------------------------------------------------------- */ +{ + return max_atom_id; +} + + + +/****************************************************************************** + * + * Function definitions for class NnfConverter. + * + *****************************************************************************/ + +/* ========================================================================= */ +void NnfConverter::operator()(const LtlFormula* f, int operand) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the negation normal form conversion for an + * LtlFormula. + * + * Arguments: f -- A pointer to a constant LtlFormula to be + * converted into negation normal form. + * operand -- + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (operand == 0) + { + switch (f->what()) + { + case LTL_ATOM : + { + Atom* a = &Atom::construct(static_cast(f)->getId()); + if (negation_stack.top()) + formula_stack.push(a); + else + formula_stack.push(&Not::construct(a)); + negation_stack.pop(); + break; + } + + case LTL_TRUE : case LTL_FALSE : + if ((f->what() == LTL_TRUE) == negation_stack.top()) + formula_stack.push(&True::construct()); + else + formula_stack.push(&False::construct()); + negation_stack.pop(); + break; + + default : + negation_stack.push(f->what() != LTL_NEGATION + && f->what() != LTL_BEFORE + ? negation_stack.top() + : !negation_stack.top()); + + switch (f->what()) + { + case LTL_CONJUNCTION : case LTL_DISJUNCTION : case LTL_UNTIL : + case LTL_V : case LTL_WEAK_UNTIL : case LTL_STRONG_RELEASE : + negation_stack.push(negation_stack.top()); + break; + + case LTL_IMPLICATION : + case LTL_BEFORE : + negation_stack.push(!negation_stack.top()); + break; + + case LTL_EQUIVALENCE : + negation_stack.push(true); + break; + + case LTL_XOR : + negation_stack.push(false); + break; + + default : + break; + } + } + + return; + } + + switch (f->what()) + { + case LTL_NEGATION : + break; + + case LTL_NEXT : + formula_stack.top() = &Next::construct(formula_stack.top()); + break; + + case LTL_FINALLY : case LTL_GLOBALLY : + if ((f->what() == LTL_FINALLY) == negation_stack.top()) + formula_stack.top() + = &Until::construct(&True::construct(), formula_stack.top()); + else + formula_stack.top() + = &V::construct(&False::construct(), formula_stack.top()); + break; + + default : /* binary operator */ + { + LtlFormula* rgt = formula_stack.top(); + formula_stack.pop(); + + switch (f->what()) + { + case LTL_CONJUNCTION : case LTL_DISJUNCTION : case LTL_IMPLICATION : + if ((f->what() == LTL_CONJUNCTION) == negation_stack.top()) + formula_stack.top() + = &And::construct(formula_stack.top(), rgt); + else + formula_stack.top() + = &Or::construct(formula_stack.top(), rgt); + break; + + case LTL_EQUIVALENCE : case LTL_XOR : + { + LtlFormula* g = &Not::construct(*formula_stack.top()); + LtlFormula* lft_neg = g->nnfClone(); + LtlFormula::destruct(g); + g = &Not::construct(*rgt); + LtlFormula* rgt_neg = g->nnfClone(); + LtlFormula::destruct(g); + + formula_stack.top() + = &And::construct(&Or::construct(formula_stack.top(), rgt_neg), + &Or::construct(lft_neg, rgt)); + break; + } + + case LTL_UNTIL : case LTL_V : case LTL_BEFORE : + if ((f->what() == LTL_UNTIL) == negation_stack.top()) + formula_stack.top() + = &Until::construct(formula_stack.top(), rgt); + else + formula_stack.top() + = &V::construct(formula_stack.top(), rgt); + break; + + default : /* LTL_WEAK_UNTIL || LTL_STRONG_RELEASE */ + if ((f->what() == LTL_WEAK_UNTIL) == negation_stack.top()) + formula_stack.top() + = &Or::construct(&Until::construct(*formula_stack.top(), rgt), + &V::construct(&False::construct(), + formula_stack.top())); + else + formula_stack.top() + = &And::construct(&V::construct(*formula_stack.top(), rgt), + &Until::construct(&True::construct(), + formula_stack.top())); + break; + } + + break; + } + } + + negation_stack.pop(); +} + + + +/****************************************************************************** + * + * Definitions for symbols used for printing the formula. + * + *****************************************************************************/ + +const char LtlTrue::prefix_symbol[] = "t"; +const char LtlTrue::infix_symbol[] = "true"; + +const char LtlFalse::prefix_symbol[] = "f"; +const char LtlFalse::infix_symbol[] = "false"; + +const char LtlNegation::prefix_symbol[] = "!"; +const char LtlNegation::infix_symbol[] = "!"; + +const char LtlNext::prefix_symbol[] = "X"; +const char LtlNext::infix_symbol[] = "X"; + +const char LtlFinally::prefix_symbol[] = "F"; +const char LtlFinally::infix_symbol[] = "<>"; + +const char LtlGlobally::prefix_symbol[] = "G"; +const char LtlGlobally::infix_symbol[] = "[]"; + +const char LtlConjunction::prefix_symbol[] = "&"; +const char LtlConjunction::infix_symbol[] = "/\\"; + +const char LtlDisjunction::prefix_symbol[] = "|"; +const char LtlDisjunction::infix_symbol[] = "\\/"; + +const char LtlImplication::prefix_symbol[] = "i"; +const char LtlImplication::infix_symbol[] = "->"; + +const char LtlEquivalence::prefix_symbol[] = "e"; +const char LtlEquivalence::infix_symbol[] = "<->"; + +const char LtlXor::prefix_symbol[] = "^"; +const char LtlXor::infix_symbol[] = "xor"; + +const char LtlUntil::prefix_symbol[] = "U"; +const char LtlUntil::infix_symbol[] = "U"; + +const char LtlV::prefix_symbol[] = "V"; +const char LtlV::infix_symbol[] = "V"; + +const char LtlWeakUntil::prefix_symbol[] = "W"; +const char LtlWeakUntil::infix_symbol[] = "W"; + +const char LtlStrongRelease::prefix_symbol[] = "M"; +const char LtlStrongRelease::infix_symbol[] = "M"; + +const char LtlBefore::prefix_symbol[] = "B"; +const char LtlBefore::infix_symbol[] = "B"; + + + +/****************************************************************************** + * + * Function definitions for class LtlFormula. + * + *****************************************************************************/ + +/* ========================================================================= */ +LtlFormula* LtlFormula::nnfClone() +/* ---------------------------------------------------------------------------- + * + * Description: Creates a copy of an LtlFormula in negation normal form. + * + * Arguments: None. + * + * Returns: A pointer to a newly allocated LtlFormula, which is + * equivalent to `this' formula in negation normal form. + * + * ------------------------------------------------------------------------- */ +{ + NnfConverter nc; + traverse(nc, LTL_PREORDER | LTL_POSTORDER); + return nc.getResult(); +} + +/* ========================================================================= */ +unsigned long int LtlFormula::size() const +/* ---------------------------------------------------------------------------- + * + * Description: Computes the number of nodes in the parse tree of an + * LtlFormula. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + FormulaSizeCounter fsc; + traverse(fsc, LTL_PREORDER); + return fsc.size; +} + +/* ========================================================================= */ +void LtlFormula::collectSubformulae + (stack >& + result_stack) const +/* ---------------------------------------------------------------------------- + * + * Description: Collects the subformulae of a LtlFormula into a stack. After + * the operation, the result stack contains the subformulae of + * the formula in post-depth-first-search order, i.e., each + * subformula of the formula can be accessed in the stack only + * after its subformulae have been accessed. + * + * Argument: result_stack -- A reference to a stack for collecting the + * results. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + SubformulaCollector sc(result_stack); + traverse(sc, LTL_PREORDER); +} + +/* ========================================================================= */ +long int LtlFormula::maxAtom() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the identifier of the "greatest" atomic proposition + * in the formula. + * + * Arguments: None. + * + * Returns: The identifier of the "greatest" atomic proposition in the + * formula or -1 if the formula is a constant formula. + * + * ------------------------------------------------------------------------- */ +{ + if (info_flags.is_constant) + return -1; + + MaxAtomFinder maf; + traverse(maf, LTL_PREORDER); + return maf.getResult(); +} + +/* ========================================================================= */ +bool LtlFormula::satisfiable(long int max_atom) const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether the formula is satisfiable. (This check is + * allowed only on fully propositional formulae.) + * + * Arguments: max_atom -- Identifier of the "greatest" atomic proposition + * in the formula. + * + * Returns: A truth value according to the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + if (!info_flags.is_propositional) + throw Exception("satisfiability check not allowed on a temporal formula"); + + if (max_atom < 0) + max_atom = maxAtom(); + + TableauStack tableau_stack; + return sat_eval(tableau_stack, max_atom); +} + +/* ========================================================================= */ +Bitset LtlFormula::findPropositionalModel(long int max_atom) const +/* ---------------------------------------------------------------------------- + * + * Description: Finds a model for a fully propositional formula (if the + * formula is satisfiable). + * + * Arguments: max_atom -- Identifier of the "greatest" atomic proposition + * in the formula. + * + * Returns: A Bitset defining a truth assignment for the propositional + * variables in the formula such that the formula is satisfied. + * + * ------------------------------------------------------------------------- */ +{ + if (!info_flags.is_propositional) + throw Exception("satisfiability check not allowed on a temporal formula"); + + if (max_atom < 0) + max_atom = maxAtom(); + + TableauStack tableau_stack; + + if (!sat_eval(tableau_stack, max_atom)) + throw Exception("formula is not satisfiable"); + + Bitset model(max_atom + 1); + model.clear(); + + for (long int i = 0; i <= max_atom; i++) + { + if (tableau_stack.top().second.test(i)) + model.setBit(i); + } + + return model; +} + +/* ========================================================================= */ +LtlFormula* LtlFormula::read(Exceptional_istream& stream) +/* ---------------------------------------------------------------------------- + * + * Description: Recursively constructs an LtlFormula by parsing input from an + * exception-aware input stream. + * + * Argument: stream -- A reference to an exception-aware input stream. + * + * Returns: The constructed LtlFormula. + * + * ------------------------------------------------------------------------- */ +{ + string token; + LtlFormula* formula; + + try + { + stream >> token; + } + catch (const IOException&) + { + if (static_cast(stream).eof()) + throw ParseErrorException("error parsing LTL formula (unexpected end of " + "input)"); + else + throw ParseErrorException("error parsing LTL formula (I/O error)"); + } + + if (token[0] == 'p') + { + if (token.length() == 1) + throw ParseErrorException("error parsing LTL formula (unrecognized " + "token: `" + token + "')"); + + long int id; + char* endptr; + + id = strtol(token.c_str() + 1, &endptr, 10); + + if (*endptr != '\0' || id < 0 || id == LONG_MIN || id == LONG_MAX) + throw ParseErrorException("error parsing LTL formula (unrecognized " + "token: `" + token + "')"); + + formula = &Atom::construct(id); + } + else + { + if (token.length() > 1) + throw ParseErrorException("error parsing LTL formula (unrecognized " + "token: `" + token + "')"); + + switch (token[0]) + { + case LTL_TRUE : + formula = &True::construct(); + break; + + case LTL_FALSE : + formula = &False::construct(); + break; + + case LTL_NEGATION : + case LTL_NEXT : + case LTL_FINALLY : + case LTL_GLOBALLY : + { + LtlFormula* g = read(stream); + + try + { + switch (token[0]) + { + case LTL_NEGATION : + formula = &Not::construct(g); + break; + + case LTL_NEXT : + formula = &Next::construct(g); + break; + + case LTL_FINALLY : + formula = &Finally::construct(g); + break; + + default : /* LTL_GLOBALLY */ + formula = &Globally::construct(g); + break; + } + } + catch (...) + { + LtlFormula::destruct(g); + throw; + } + + break; + } + + case LTL_CONJUNCTION : + case LTL_DISJUNCTION : + case LTL_IMPLICATION : + case LTL_EQUIVALENCE : + case LTL_XOR : + case LTL_UNTIL : + case LTL_V : + case LTL_WEAK_UNTIL : + case LTL_STRONG_RELEASE : + case LTL_BEFORE : + { + LtlFormula* g = read(stream); + LtlFormula* h; + + try + { + h = read(stream); + } + catch (...) + { + LtlFormula::destruct(g); + throw; + } + + try + { + switch (token[0]) + { + case LTL_CONJUNCTION : + formula = &And::construct(g, h); + break; + + case LTL_DISJUNCTION : + formula = &Or::construct(g, h); + break; + + case LTL_IMPLICATION : + formula = &Imply::construct(g, h); + break; + + case LTL_EQUIVALENCE : + formula = &Equiv::construct(g, h); + break; + + case LTL_XOR : + formula = &Xor::construct(g, h); + break; + + case LTL_UNTIL : + formula = &Until::construct(g, h); + break; + + case LTL_V : + formula = &V::construct(g, h); + break; + + case LTL_WEAK_UNTIL : + formula = &WeakUntil::construct(g, h); + break; + + case LTL_STRONG_RELEASE : + formula = &StrongRelease::construct(g, h); + break; + + default : /* LTL_BEFORE */ + formula = &Before::construct(g, h); + break; + } + } + catch (...) + { + LtlFormula::destruct(g); + LtlFormula::destruct(h); + throw; + } + + break; + } + + default : + throw ParseErrorException("error parsing LTL formula (unrecognized " + "token: `" + token + "')"); + } + } + + return formula; +} + +/* ========================================================================= */ +void LtlFormula::print(Exceptional_ostream& estream, OutputMode mode) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes the formula to a stream. + * + * Arguments: estream -- A reference to an exception-aware output stream. + * mode -- Chooses between prefix and infix notation. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (mode == LTL_PREFIX) + { + FormulaWriter, + ConstantWriter, + AtomWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + BinaryOperatorPrefixWriter, + BinaryOperatorPrefixWriter, + BinaryOperatorPrefixWriter, + BinaryOperatorPrefixWriter, + BinaryOperatorPrefixWriter, + BinaryOperatorPrefixWriter, + BinaryOperatorPrefixWriter, + BinaryOperatorPrefixWriter, + BinaryOperatorPrefixWriter, + BinaryOperatorPrefixWriter + > + fw(estream); + + traverse(fw, LTL_PREORDER | LTL_INORDER | LTL_POSTORDER); + } + else + { + FormulaWriter, + ConstantWriter, + AtomWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter + > + fw(estream); + + traverse(fw, LTL_PREORDER | LTL_INORDER | LTL_POSTORDER); + } +} + +/* ========================================================================= */ +bool LtlFormula::sat_eval + (TableauStack& tableau_stack, const long int max_atom) const +/* ---------------------------------------------------------------------------- + * + * Description: Determines whether the formula is satisfiable. If yes, a + * model of the formula will be stored in the map given as a + * parameter. (This check is allowed only on fully propositional + * formulae.) + * + * Arguments: tableau_stack -- Nodes of the tableau used for + * satisfiability testing. + * max_atom -- Identifier of the "greatest" atomic + * proposition in the formula. + * + * Returns: A truth value telling whether the formula is satisfiable or + * not. + * + * ------------------------------------------------------------------------- */ +{ + bool prove_true; + const LtlFormula* current_formula; + + tableau_stack.push + (make_pair(FormulaStack(), + Bitset((static_cast(max_atom) + 1) * 2))); + tableau_stack.top().second.clear(); + + FormulaStack* formula_stack = &tableau_stack.top().first; + + formula_stack->push(make_pair(true, this)); + + while (!formula_stack->empty()) + { + prove_true = formula_stack->top().first; + current_formula = formula_stack->top().second; + formula_stack->pop(); + + switch (current_formula->what()) + { + case LTL_ATOM : + { + long int atom_id = static_cast(current_formula)->getId(); + + if (prove_true) + { + if (tableau_stack.top().second.test(atom_id + max_atom + 1)) + { + tableau_stack.pop(); + if (tableau_stack.empty()) + return false; + formula_stack = &tableau_stack.top().first; + } + else + tableau_stack.top().second.setBit(atom_id); + } + else + { + if (tableau_stack.top().second.test(atom_id)) + { + tableau_stack.pop(); + if (tableau_stack.empty()) + return false; + formula_stack = &tableau_stack.top().first; + } + else + tableau_stack.top().second.setBit(atom_id + max_atom + 1); + } + + break; + } + + case LTL_TRUE : + if (!prove_true) + { + tableau_stack.pop(); + if (tableau_stack.empty()) + return false; + formula_stack = &tableau_stack.top().first; + } + + break; + + case LTL_FALSE : + if (prove_true) + { + tableau_stack.pop(); + if (tableau_stack.empty()) + return false; + formula_stack = &tableau_stack.top().first; + } + + break; + + case LTL_NEGATION : + { + const Not* f = static_cast(current_formula); + formula_stack->push(make_pair(!prove_true, f->subformula)); + break; + } + + case LTL_DISJUNCTION : + { + const Or* f = static_cast(current_formula); + + if (prove_true) + { + tableau_stack.push(tableau_stack.top()); + formula_stack->push(make_pair(true, f->subformula2)); + formula_stack = &tableau_stack.top().first; + formula_stack->push(make_pair(true, f->subformula1)); + } + else + { + formula_stack->push(make_pair(false, f->subformula2)); + formula_stack->push(make_pair(false, f->subformula1)); + } + + break; + } + + case LTL_CONJUNCTION : + { + const And* f = static_cast(current_formula); + + if (prove_true) + { + formula_stack->push(make_pair(true, f->subformula2)); + formula_stack->push(make_pair(true, f->subformula1)); + } + else + { + tableau_stack.push(tableau_stack.top()); + formula_stack->push(make_pair(false, f->subformula2)); + formula_stack = &tableau_stack.top().first; + formula_stack->push(make_pair(false, f->subformula1)); + } + + break; + } + + case LTL_IMPLICATION : + { + const Imply* f = static_cast(current_formula); + + if (prove_true) + { + tableau_stack.push(tableau_stack.top()); + formula_stack->push(make_pair(true, f->subformula2)); + formula_stack = &tableau_stack.top().first; + formula_stack->push(make_pair(false, f->subformula1)); + } + else + { + formula_stack->push(make_pair(false, f->subformula2)); + formula_stack->push(make_pair(true, f->subformula1)); + } + + break; + } + + case LTL_EQUIVALENCE : + { + const Equiv* f = static_cast(current_formula); + + tableau_stack.push(tableau_stack.top()); + formula_stack->push(make_pair(!prove_true, f->subformula2)); + formula_stack->push(make_pair(false, f->subformula1)); + formula_stack = &tableau_stack.top().first; + formula_stack->push(make_pair(prove_true, f->subformula2)); + formula_stack->push(make_pair(true, f->subformula1)); + + break; + } + + case LTL_XOR : + { + const Xor* f = static_cast(current_formula); + + tableau_stack.push(tableau_stack.top()); + formula_stack->push(make_pair(prove_true, f->subformula2)); + formula_stack->push(make_pair(false, f->subformula1)); + formula_stack = &tableau_stack.top().first; + formula_stack->push(make_pair(!prove_true, f->subformula2)); + formula_stack->push(make_pair(true, f->subformula1)); + + break; + } + + default : + throw Exception("satisfiable(): unknown formula type"); + } + } + + return true; +} + +} diff --git a/lbtt/src/LtlFormula.h b/lbtt/src/LtlFormula.h new file mode 100644 index 000000000..fee7e8f60 --- /dev/null +++ b/lbtt/src/LtlFormula.h @@ -0,0 +1,2659 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef LTLFORMULA_H +#define LTLFORMULA_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include +#include +#include "Alloc.h" +#include "BitArray.h" +#include "Exception.h" + +using namespace std; + +extern bool user_break; + +namespace Ltl +{ + +/****************************************************************************** + * + * Constants for identifying LTL formulae of various types. + * + *****************************************************************************/ + +enum FormulaType + {LTL_TRUE = 't', LTL_FALSE = 'f', LTL_ATOM = 'p', LTL_NEGATION = '!', + LTL_NEXT = 'X', LTL_FINALLY = 'F', LTL_GLOBALLY = 'G', + LTL_CONJUNCTION = '&', LTL_DISJUNCTION = '|', LTL_IMPLICATION = 'i', + LTL_EQUIVALENCE = 'e', LTL_XOR = '^', LTL_UNTIL = 'U', LTL_V = 'V', + LTL_WEAK_UNTIL = 'W', LTL_STRONG_RELEASE = 'M', LTL_BEFORE = 'B'}; + +const char* infixSymbol(const int symbol); /* Returns the infix + * symbol corresponding to + * a given FormulaType + * constant. + */ + + + +/****************************************************************************** + * + * Constants for selecting between the traversal modes in the parse tree of an + * LTL formula. + * + *****************************************************************************/ + +enum TraversalMode {LTL_PREORDER = 1, LTL_INORDER = 2, LTL_POSTORDER = 4}; + + + +/****************************************************************************** + * + * Constants for selecting the notation in which to print an LtlFormula. + * + *****************************************************************************/ + +enum OutputMode {LTL_PREFIX, LTL_INFIX}; + + + +/****************************************************************************** + * + * A base class for LTL formulae. + * + *****************************************************************************/ + +class LtlFormula +{ +public: + LtlFormula(); /* Creates a new LTL + * formula. + */ + + virtual ~LtlFormula(); /* Destructor. */ + +private: + LtlFormula(const LtlFormula& f); /* Prevent copying and */ + LtlFormula& operator=(const LtlFormula& f); /* assignment of + * LtlFormula objects. + */ + +public: + static void destruct(LtlFormula* f); /* Removes an LTL formula + * from `formula_storage'. + */ + + virtual bool operator<(const LtlFormula& f) /* ``Less than'' */ + const = 0; /* comparison. */ + + LtlFormula* clone(); /* Creates a copy of the + * formula object. + */ + + LtlFormula* nnfClone(); /* Creates a copy of the + * formula object in + * negation normal form. + */ + + bool evaluate /* These functions */ + (const BitArray& valuation, /* evaluate the formula */ + const unsigned long int valuation_size) const; /* in a given truth */ + /* assignment for the */ + bool evaluate /* atomic propositions. */ + (const Bitset& valuation) const; /* (The functions are + * intended to be used + * mainly with pure + * propositional + * formulae. If the + * formula contains + * temporal operators, + * calling one of these + * functions is + * interpreted as a + * request to evaluate + * the formula in a + * state space + * consisting of a + * single state having a + * transition to itself, + * with the given truth + * assignment for the + * atomic propositions.) + */ + + bool propositional() const; /* Tells whether the + * formula contains any + * temporal operators. + */ + + bool constant() const; /* Tells whether the + * formula is a constant, + * i.e., whether all its + * atoms are Boolean + * constants. + */ + + unsigned long int size() const; /* Returns the number of + * nodes in the parse tree + * of the formula. + */ + + long int maxAtom() const; /* Finds the largest + * identifier of the + * atomic propositions in + * the formula. + */ + + bool satisfiable(long int max_atom = -1) const; /* Tells whether the + * formula is satisfiable. + * (This test is only + * allowed if the formula + * is propositional.) + */ + + Bitset findPropositionalModel /* Finds a model for a */ + (long int max_atom = -1) const; /* purely propositional + * formula (if it is + * satisfiable). + */ + + virtual FormulaType what() const = 0; /* Tells the type of the + * formula. + */ + + static LtlFormula* read(istream& stream); /* Constructs an LtlFormula + * by parsing input from an + * input stream. + */ + + static LtlFormula* read /* Constructs an */ + (Exceptional_istream& stream); /* LtlFormula by parsing + * input from an + * exception-aware input + * stream. + */ + + void print /* Writes the formula to */ + (ostream& stream = cout, /* an output stream. */ + OutputMode mode = LTL_INFIX) const; + + void print /* Writes the formula to */ + (Exceptional_ostream& stream, /* an exception-aware */ + OutputMode mode) const; /* output stream. */ + + template /* Performs a depth- */ + void traverse(F& f, int mode) const; /* first traversal on */ + /* the formula parse + * tree, calling the + * function object `f' + * at each node. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class ParseErrorException : public Exception /* A class for reporting */ + /* errors when */ + /* constructing an */ + /* LtlFormula by reading */ + { /* it from a stream. */ + public: + ParseErrorException(const string& msg); /* Constructor. */ + + /* default copy constructor */ + + ~ParseErrorException() throw(); /* Destructor. */ + + ParseErrorException& operator= /* Assignment operator. */ + (const ParseErrorException& e); + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class ptr_less /* Class for mapping */ + { /* the `less than' */ + public: /* relation between two */ + bool operator() /* pointers to */ + (const LtlFormula* f1, /* LtlFormulae to the */ + const LtlFormula* f2) const; /* corresponding */ + }; /* relation between the + * formulae itself. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +protected: + struct /* These flags tell */ + { /* whether the formula */ + unsigned int is_propositional : 1; /* contains any temporal */ + unsigned int is_constant : 1; /* operators and atomic */ + } info_flags; /* propositions. */ + + static LtlFormula& /* Updates the shared */ + insertToStorage(LtlFormula* f); /* formula storage with + * a new formula. + */ + +private: + virtual bool eval /* Helper function for */ + (const BitArray& valuation) const = 0; /* evaluating the + * formula in a given + * truth assignment. + */ + + void collectSubformulae /* Builds a stack of the */ + (stack >& + result_stack) const; + + typedef pair /* Shorthand type */ + FormulaStackElement; /* definitions for the */ + typedef stack >/* checking algorithm. */ + FormulaStack; + typedef pair + TableauStackElement; + typedef stack > + TableauStack; + + bool sat_eval /* Helper function for */ + (TableauStack& tableau_stack, /* testing the */ + const long int max_atom) /* satisfiability of a */ + const; /* propositional + * formula. + */ + + static map /* LTL formulae. */ + formula_storage; + + static unsigned long int /* Upper limit for the */ + eval_proposition_id_limit; /* atomic proposition + * identifiers (used + * when evaluating the + * truth value of the + * formula in a given + * truth assignment). + */ + + friend class Atom; /* Friend declarations. */ + + friend class LtlNegation; + friend class LtlNext; + friend class LtlFinally; + friend class LtlGlobally; + + friend class LtlDisjunction; + friend class LtlConjunction; + friend class LtlImplication; + friend class LtlEquivalence; + friend class LtlXor; + friend class LtlUntil; + friend class LtlV; + friend class LtlWeakUntil; + friend class LtlStrongRelease; + friend class LtlBefore; + + friend class PathEvaluator; + + template + friend class UnaryFormula; + + template + friend class BinaryFormula; +}; + + + +/****************************************************************************** + * + * A class for atomic propositions. + * + *****************************************************************************/ + +class Atom : public LtlFormula +{ +public: + static Atom& construct(long int a); /* Creates a new atomic + * proposition. + */ + +private: + explicit Atom(long int a); /* Constructor for an + * atomic proposition. + */ + + ~Atom(); /* Destructor. */ + + Atom(const Atom&); /* Prevent copying and */ + Atom& operator=(const Atom&); /* assignment of Atom + * objects. + */ + +public: + long int getId() const; /* Returns the identifier + * of the atomic + * proposition. + */ + + bool operator<(const LtlFormula& f) const; /* ``Less than'' + * comparison. + */ + + FormulaType what() const; /* Returns the type of + * the formula, i.e. the + * constant LTL_ATOM. + */ + + friend class LtlNegation; /* Friend declarations. */ + friend class LtlNext; + friend class LtlFinally; + friend class LtlGlobally; + + friend class LtlDisjunction; + friend class LtlConjunction; + friend class LtlImplication; + friend class LtlEquivalence; + friend class LtlXor; + friend class LtlUntil; + friend class LtlV; + friend class LtlWeakUntil; + friend class LtlStrongRelease; + friend class LtlBefore; + + friend class PathEvaluator; + +private: + bool eval(const BitArray& valuation) const; /* Returns the truth value + * of the atom in a given + * truth assignment. + */ + + long int atom; /* Identifier of a + * propositional variable. + */ +}; + + + +/****************************************************************************** + * + * A template class for Boolean constants. The class (`Symbol') used to + * instantiate the template must satisfy the following requirements: + * + * (1) The class contains two static public constant arrays of characters + * `prefix_symbol' and `infix_symbol' representing the textual symbols + * that should be used when printing the constant in prefix or infix + * notation, respectively. + * + * (2) The class contains a static public constant attribute `type' of type + * FormulaType which can be used to identify the constant. + * + * (3) The class contains a static public member function `eval' which can + * be called without any parameters. The function returns a truth value + * of the constant. + * + *****************************************************************************/ + +template +class Constant : public LtlFormula +{ +public: + static Constant& construct(); /* Creates a Boolean + * constant. + */ + +private: + Constant(); /* Constructor for a + * Boolean constant. + */ + + ~Constant(); /* Destructor. */ + + Constant(const Constant&); /* Prevent copying and */ + Constant& operator= /* assignment of */ + (const Constant&); /* Constants. */ + +public: + bool operator<(const LtlFormula& f) const; /* ``Less than'' + * comparison. + */ + + FormulaType what() const; /* Returns the type of + * the constant, i.e. + * Symbol::type. + */ + + friend class LtlNegation; /* Friend declarations. */ + friend class LtlNext; + friend class LtlFinally; + friend class LtlGlobally; + + friend class LtlDisjunction; + friend class LtlConjunction; + friend class LtlImplication; + friend class LtlEquivalence; + friend class LtlXor; + friend class LtlUntil; + friend class LtlV; + friend class LtlWeakUntil; + friend class LtlStrongRelease; + friend class LtlBefore; + + friend class PathEvaluator; + +private: + bool eval(const BitArray&) const; /* This function only tells + * the truth value of the + * constant. It is needed + * to satisfy the + * LtlFormula member + * function interface. + */ +}; + + + +/****************************************************************************** + * + * A class for the Boolean constant `true'. + * + *****************************************************************************/ + +class LtlTrue +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the constant. */ + + static const FormulaType type = LTL_TRUE; /* Type of the constant. */ + + static bool eval(); /* Returns the truth value + * of the constant. + */ +}; + + + +/****************************************************************************** + * + * A class for the Boolean constant `false'. + * + *****************************************************************************/ + +class LtlFalse +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the constant. */ + + static const FormulaType type = LTL_FALSE; /* Type of the constant. */ + + static bool eval(); /* Returns the truth value + * of the constant. + */ +}; + + + +/****************************************************************************** + * + * Type definitions for propositional constants to make them accessible + * without using the template instantiation syntax. + * + *****************************************************************************/ + +typedef Constant True; +typedef Constant False; + + + +/****************************************************************************** + * + * A template class for LTL formulae consisting of a unary operator (e.g. + * negation) and a subformula. The class `Operator' used to instantiate the + * template must satisfy the following requirements: + * + * (1) The class contains two static public constant arrays of characters + * `prefix_symbol' and `infix_symbol' representing the textual symbols + * that should be used when printing the operator in prefix or infix + * notation, respectively. + * + * (2) The class contains a static public constant attribute `type' of type + * FormulaType which can be used to identify the exact type of the + * formula. + * + * (3) The class contains a static public member function `eval' which can + * be called with two parameters: + * - a BitArray object assigning truth values to atomic propositions + * - a pointer to an LtlFormula object (the subformula associated with + * the unary operator). + * The function must return a truth value (assumed to be the truth value + * of the formula in the truth assignment given by the BitArray object). + * + *****************************************************************************/ + +template +class UnaryFormula : public LtlFormula +{ +public: + static UnaryFormula& construct /* Create a new unary */ + (LtlFormula* f); /* LTL formula. */ + static UnaryFormula& construct + (LtlFormula& f); + +private: + UnaryFormula(LtlFormula* f); /* Constructor for unary + * LTL formulae. + */ + + ~UnaryFormula(); /* Destructor. */ + + UnaryFormula /* Prevent copying and */ + (const UnaryFormula& f); /* assignment of */ + UnaryFormula& operator= /* UnaryFormula objects. */ + (const UnaryFormula& f); + +public: + bool operator<(const LtlFormula& f) const; /* ``Less than'' + * comparison. + */ + + FormulaType what() const; /* Tells the type of the + * formula. + */ + + friend class LtlFormula; /* Friend declarations. */ + + friend class LtlNegation; + friend class LtlNext; + friend class LtlFinally; + friend class LtlGlobally; + + friend class LtlDisjunction; + friend class LtlConjunction; + friend class LtlImplication; + friend class LtlEquivalence; + friend class LtlXor; + friend class LtlUntil; + friend class LtlV; + friend class LtlWeakUntil; + friend class LtlStrongRelease; + friend class LtlBefore; + + friend class PathEvaluator; + +private: + bool eval(const BitArray& valuation) const; /* Returns the truth value + * of the formula in a + * given truth assignment. + */ + + LtlFormula* subformula; /* Pointer to the operand + * formula of the unary + * operator. + */ +}; + + + +/****************************************************************************** + * + * A class for negation operator. + * + *****************************************************************************/ + +class LtlNegation +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_NEGATION; /* Type of the formula. */ + + static bool eval /* Evaluates a negated */ + (const BitArray& valuation, /* formula in a given */ + const LtlFormula* f); /* truth assignment (a + * single-state state + * space). + */ +}; + + + +/****************************************************************************** + * + * A class for "Next" operator. + * + *****************************************************************************/ + +class LtlNext +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_NEXT; /* Type of the formula. */ + + static bool eval /* Evaluates a "Next" */ + (const BitArray& valuation, /* formula in a given */ + const LtlFormula* f); /* truth assignment (a + * single-state state + * space). + */ +}; + + + +/****************************************************************************** + * + * A class for "Finally" operator. + * + *****************************************************************************/ + +class LtlFinally +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_FINALLY; /* Type of the formula. */ + + static bool eval /* Evaluates a "Finally" */ + (const BitArray& valuation, /* formula in a given */ + const LtlFormula* f); /* truth assignment (a + * single-state state + * space). + */ +}; + + + +/****************************************************************************** + * + * A class for "Globally" operator. + * + *****************************************************************************/ + +class LtlGlobally +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_GLOBALLY; /* Type of the formula. */ + + static bool eval /* Evaluates a */ + (const BitArray& valuation, /* "Globally" formula in */ + const LtlFormula* f); /* a given truth + * assignment (a single- + * state state space). + */ +}; + + + +/****************************************************************************** + * + * Type definitions for unary LTL formulae to make them accessible without + * using the template instantiation syntax. + * + *****************************************************************************/ + +typedef UnaryFormula Not; +typedef UnaryFormula Next; +typedef UnaryFormula Finally; +typedef UnaryFormula Globally; + + + +/****************************************************************************** + * + * A template class for LTL formulae consisting of a binary operator + * (e.g. conjunction) with two subformulae. The class `Operator' used for + * instantiating the template must satisfy the following requirements: + * + * (1) The class contains two static public constant arrays of characters + * `prefix_symbol' and `infix_symbol' representing the textual symbols + * that should be used when printing the operator in prefix or infix + * notation, respectively. + * + * (2) The class contains a static public constant attribute `type' of type + * FormulaType which can be used to identify the exact type of the + * formula. + * + * (3) The class contains a static public member function `eval' which can + * be called with three parameters: + * - a BitArray object assigning truth values to atomic propositions + * - two pointers to LtlFormula objects (the subformulae associated + * with the binary operator) + * The function must return a truth value (assumed to be the truth value + * of the formula in the truth assignment given by the BitArray object). + * + *****************************************************************************/ + +template +class BinaryFormula : public LtlFormula +{ +public: + static BinaryFormula& construct /* Create a binary LTL */ + (LtlFormula* f1, LtlFormula* f2); /* formula. */ + static BinaryFormula& construct + (LtlFormula& f1, LtlFormula& f2); + static BinaryFormula& construct + (LtlFormula* f1, LtlFormula& f2); + static BinaryFormula& construct + (LtlFormula& f1, LtlFormula* f2); + +private: + BinaryFormula(LtlFormula* f1, LtlFormula* f2); /* Constructor for a */ + /* binary LTL formula. */ + + ~BinaryFormula(); /* Destructor. */ + + BinaryFormula /* Prevent copying and */ + (const BinaryFormula& f); /* assignment of */ + BinaryFormula& operator= /* BinaryFormula */ + (const BinaryFormula& f); /* objects. */ + +public: + bool operator<(const LtlFormula& f) const; /* ``Less than'' + * comparison. + */ + + FormulaType what() const; /* Tells the type of the + * formula. + */ + + friend class LtlFormula; /* Friend declarations. */ + + friend class LtlNegation; + friend class LtlNext; + friend class LtlFinally; + friend class LtlGlobally; + + friend class LtlDisjunction; + friend class LtlConjunction; + friend class LtlImplication; + friend class LtlEquivalence; + friend class LtlXor; + friend class LtlUntil; + friend class LtlV; + friend class LtlWeakUntil; + friend class LtlStrongRelease; + friend class LtlBefore; + + friend class PathEvaluator; + +private: + bool eval(const BitArray& valuation) const; /* Returns the truth value + * of the formula in a + * given truth assignment. + */ + + LtlFormula* subformula1; /* Pointer to the first + * operand of the binary + * operator. + */ + + LtlFormula* subformula2; /* Pointer to the second + * operand. + */ +}; + + + +/****************************************************************************** + * + * A class for disjunction operator. + * + *****************************************************************************/ + +class LtlDisjunction +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_DISJUNCTION; /* Type of the formula. */ + + static bool eval /* Evaluates the */ + (const BitArray& valuation, /* disjunctive formula */ + const LtlFormula* f1, /* in a given truth */ + const LtlFormula* f2); /* assignment. */ +}; + + + +/****************************************************************************** + * + * A class for conjunction operator. + * + *****************************************************************************/ + +class LtlConjunction +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_CONJUNCTION; /* Type of the formula. */ + + static bool eval /* Evaluates the */ + (const BitArray& valuation, /* conjunctive formula */ + const LtlFormula* f1, /* in a given truth */ + const LtlFormula* f2); /* assignment. */ +}; + + + +/****************************************************************************** + * + * A class for implication operator. + * + *****************************************************************************/ + +class LtlImplication +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_IMPLICATION; /* Type of the formula. */ + + static bool eval /* Evaluates the */ + (const BitArray& valuation, /* implication formula */ + const LtlFormula* f1, /* in a given truth */ + const LtlFormula* f2); /* assignment. */ +}; + + + +/****************************************************************************** + * + * A class for equivalence operator. + * + *****************************************************************************/ + +class LtlEquivalence +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_EQUIVALENCE; /* Type of the formula. */ + + static bool eval /* Evaluates the */ + (const BitArray& valuation, /* equivalence formula */ + const LtlFormula* f1, /* in a given truth */ + const LtlFormula* f2); /* assignment. */ +}; + + + +/****************************************************************************** + * + * A class for the ``exclusive or'' operator. + * + *****************************************************************************/ + +class LtlXor +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_XOR; /* Type of the formula. */ + + static bool eval /* Evaluates the ``xor'' */ + (const BitArray& valuation, /* formula in a given */ + const LtlFormula* f1, /* truth assignment. */ + const LtlFormula* f2); +}; + + + +/****************************************************************************** + * + * A class for `Until' operator. + * + *****************************************************************************/ + +class LtlUntil +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_UNTIL; /* Type of the formula. */ + + static bool eval /* Evaluates the */ + (const BitArray& valuation, /* "Until" formula in a */ + const LtlFormula*, /* given truth */ + const LtlFormula* f2); /* assignment. */ +}; + + + +/****************************************************************************** + * + * A class for `V' (the dual of `Until') operator. + * + *****************************************************************************/ + +class LtlV +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_V; /* Type of the formula. */ + + static bool eval /* Evaluates the "V" */ + (const BitArray& valuation, /* formula in a given */ + const LtlFormula*, /* truth assignment. */ + const LtlFormula* f2); +}; + + + +/****************************************************************************** + * + * A class for `weak until' operator. + * + *****************************************************************************/ + +class LtlWeakUntil +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_WEAK_UNTIL; /* Type of the formula. */ + + static bool eval /* Evaluates the "weak */ + (const BitArray& valuation, /* until" formula in a */ + const LtlFormula* f1, /* given truth */ + const LtlFormula* f2); /* assignment. */ +}; + + + +/****************************************************************************** + * + * A class for `strong release' operator. + * + *****************************************************************************/ + +class LtlStrongRelease +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type /* Type of the formula. */ + = LTL_STRONG_RELEASE; + + static bool eval /* Evaluates the "strong */ + (const BitArray& valuation, /* release" formula in a */ + const LtlFormula* f1, /* given truth */ + const LtlFormula* f2); /* assignment. */ +}; + + + +/****************************************************************************** + * + * A class for `before' operator. + * + *****************************************************************************/ + +class LtlBefore +{ +public: + static const char prefix_symbol[]; /* Symbols for printing */ + static const char infix_symbol[]; /* the formula. */ + + static const FormulaType type = LTL_BEFORE; /* Type of the formula. */ + + static bool eval /* Evaluates the */ + (const BitArray& valuation, /* "before" formula in a */ + const LtlFormula*, /* given truth */ + const LtlFormula* f2); /* assignment. */ +}; + + + +/****************************************************************************** + * + * Type definitions for the different binary LTL formulae to make them + * accessible without using the template instantiation syntax. + * + *****************************************************************************/ + +typedef BinaryFormula Or; +typedef BinaryFormula And; +typedef BinaryFormula Imply; +typedef BinaryFormula Equiv; +typedef BinaryFormula Xor; +typedef BinaryFormula Until; +typedef BinaryFormula V; +typedef BinaryFormula WeakUntil; +typedef BinaryFormula StrongRelease; +typedef BinaryFormula Before; + + + +/****************************************************************************** + * + * Inline function definitions for class LtlFormula. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline LtlFormula::LtlFormula() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class LtlFormula. Initializes the attributes + * of the base class LtlFormula. + * + * Arguments: None. + * + * Returns: Nothing. + * + * --------------------------------------------------------------------------*/ +{ + info_flags.is_propositional = 0; + info_flags.is_constant = 0; +} + +/* ========================================================================= */ +inline LtlFormula::~LtlFormula() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class LtlFormula. Deallocates the memory + * reserved for the formula. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline void LtlFormula::destruct(LtlFormula* f) +/* ---------------------------------------------------------------------------- + * + * Description: Updates the shared formula storage to remove one reference to + * the formula. The formula object is deleted if the reference + * count becomes zero. + * + * Argument: f -- A pointer to the formula to be removed from the shared + * storage. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + map::iterator + deleter; + + deleter = formula_storage.find(f); + if (--deleter->second == 0) + { + formula_storage.erase(deleter); + delete f; + } +} + +/* ========================================================================= */ +inline LtlFormula* LtlFormula::clone() +/* ---------------------------------------------------------------------------- + * + * Description: Updates the shared formula storage to add a new reference to + * `this' LtlFormula. + * + * Arguments: None. + * + * Returns: A pointer to the same formula. + * + * ------------------------------------------------------------------------- */ +{ + formula_storage.find(this)->second++; + return this; +} + +/* ========================================================================= */ +inline bool LtlFormula::evaluate + (const BitArray& valuation, const unsigned long int valuation_size) const +/* ---------------------------------------------------------------------------- + * + * Description: Computes the truth value of the formula in a given truth + * assignment for the atomic propositions in the formula. This + * function can be used to evaluate purely propositional + * formulae without the requirement to evaluate the formula with + * respect to some state space. However, if the formula + * contains temporal operators, calling this function for the + * formula is equivalent to evaluating the formula in a state + * space consisting of a single state with a self-loop (with the + * given assignment for the propositions in the state). + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * valuation_size -- Number of propositions in the valuation + * (if the formula contains propositions + * with an identifier greater or equal to + * the limit, they are assumed to be false + * in the valuation). + * + * Returns: The truth value of the formula. + * + * ------------------------------------------------------------------------- */ +{ + eval_proposition_id_limit = valuation_size; + return eval(valuation); +} + +/* ========================================================================= */ +inline bool LtlFormula::evaluate(const Bitset& valuation) const +/* ---------------------------------------------------------------------------- + * + * Description: See above. + * + * Argument: valuation -- A reference to a constant Bitset representing + * the truth assignment. + * + * Returns: The truth value of the formula. + * + * ------------------------------------------------------------------------- */ +{ + return evaluate(valuation, valuation.capacity()); +} + +/* ========================================================================= */ +inline bool LtlFormula::propositional() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether the formula contains any temporal operators. + * + * Arguments: None. + * + * Returns: true or false according to the test whether the formula is + * propositional or not. + * + * ------------------------------------------------------------------------- */ +{ + return info_flags.is_propositional; +} + +/* ========================================================================= */ +inline bool LtlFormula::constant() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether the formula contains any non-constant atoms. + * + * Arguments: None. + * + * Returns: A truth value according to the test for the property. + * + * ------------------------------------------------------------------------- */ +{ + return info_flags.is_constant; +} + +/* ========================================================================= */ +inline LtlFormula* LtlFormula::read(istream& stream) +/* ---------------------------------------------------------------------------- + * + * Description: Constructs an LtlFormula by parsing input from a stream. + * + * Arguments: stream -- A reference to an input stream. + * + * Returns: The constructed LtlFormula. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_istream estream(&stream, ios::badbit | ios::failbit); + return read(estream); +} + +/* ========================================================================= */ +inline void LtlFormula::print(ostream& stream, OutputMode mode) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes the formula to a stream. + * + * Arguments: stream -- A reference to an output stream. + * mode -- Chooses between prefix and infix notation. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); + print(estream, mode); +} + +/* ========================================================================= */ +inline ostream& operator<<(ostream& stream, const LtlFormula& f) +/* ---------------------------------------------------------------------------- + * + * Description: Alternative method for printing an LTL formula by using the + * << operator. + * + * Arguments: stream -- A reference to the output stream to which the + * formula should be printed. + * f -- A reference to the constant LTL formula. + * + * Returns: A reference to the output stream (to support chaining of the + * << operators). + * + * --------------------------------------------------------------------------*/ +{ + f.print(stream); + return stream; +} + +/* ========================================================================= */ +inline Exceptional_ostream& operator<< + (Exceptional_ostream& stream, const LtlFormula& f) +/* ---------------------------------------------------------------------------- + * + * Description: Alternative method for printing an LTL formula by using the + * << operator. + * + * Arguments: stream -- A reference to the exception-aware output stream + * to which the formula should be printed. + * f -- A reference to the constant LTL formula. + * + * Returns: A reference to the output stream (to support chaining of the + * << operators). + * + * --------------------------------------------------------------------------*/ +{ + f.print(stream, LTL_INFIX); + return stream; +} + + + +/* ========================================================================= */ +inline LtlFormula& LtlFormula::insertToStorage(LtlFormula* f) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a formula to the formula storage. + * + * Arguments: f -- The formula to be inserted. + * + * Returns: A pointer to the formula. + * + * ------------------------------------------------------------------------- */ +{ + map::iterator + inserter; + + inserter = formula_storage.find(f); + if (inserter != formula_storage.end()) + { + delete f; + inserter->second++; + return *(inserter->first); + } + + formula_storage.insert(make_pair(f, 1)); + return *f; +} + + + +/****************************************************************************** + * + * Template function definitions for class LtlFormula. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +void LtlFormula::traverse(F& f, int mode) const +/* ---------------------------------------------------------------------------- + * + * Description: Implements a depth-first traversal algorithm in the parse + * tree of an LtlFormula. + * + * Arguments: f -- A reference to a function object implementing an + * operation that will be performed in each node of + * the parse tree of the formula. The class F must + * define the member function + * void operator()(const LtlFormula* f, int operand). + * The function will then be called by the depth-first + * traversal function such that + * `f' + * points to the subformula rooted at the node, and + * + * `operand' + * is an integer that can be used to determine the + * state of the depth-first search. The possible + * values are: + * 0 -- The depth-first has just entered the + * node from its parent node. + * 1 -- The depth-first search has traversed + * the left-hand subtree of the node and + * will proceed next to the right-hand + * subtree of the node. (`operand' may + * have this value only if the node has + * two children, i.e., if the node is + * associated with a binary operator.) + * 2 -- The depth-first search has traversed + * all subtrees of the node and will + * backtrack to the parent node. + * mode -- A bitwise combination of the constants + * `LTL_PREORDER', `LTL_INORDER' and `LTL_POSTORDER'. + * The value of `mode' controls when the node + * operation `f' should be performed. If + * `mode & LTL_PREORDER != 0', the node operation will + * be performed when the depth-first search has just + * entered the node. If `mode & LTL_INORDER != 0' and + * the node is associated with a binary operator, the + * node operation will be performed when the depth- + * first search is about to enter the right-hand + * subtree of the node. If `mode & LTL_POSTORDER != 0' + * the operation will be performed just before the + * depth-first search backtracks from the node. (If + * the current parse tree node is a leaf node, the + * node operation is always performed once using the + * value 0 for the `operand' parameter.) + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (what() == LTL_ATOM || what() == LTL_TRUE || what() == LTL_FALSE) + { + f(this, 0); + return; + } + + if (mode & LTL_PREORDER) + f(this, 0); + + switch (what()) + { + case LTL_NEGATION : + static_cast(this)->subformula->traverse(f, mode); + goto postorder_visit; + + case LTL_NEXT : + static_cast(this)->subformula->traverse(f, mode); + goto postorder_visit; + + case LTL_FINALLY : + static_cast(this)->subformula->traverse(f, mode); + goto postorder_visit; + + case LTL_GLOBALLY : + static_cast(this)->subformula->traverse(f, mode); + goto postorder_visit; + + case LTL_CONJUNCTION : + static_cast(this)->subformula1->traverse(f, mode); + break; + + case LTL_DISJUNCTION : + static_cast(this)->subformula1->traverse(f, mode); + break; + + case LTL_IMPLICATION : + static_cast(this)->subformula1->traverse(f, mode); + break; + + case LTL_EQUIVALENCE : + static_cast(this)->subformula1->traverse(f, mode); + break; + + case LTL_XOR : + static_cast(this)->subformula1->traverse(f, mode); + break; + + case LTL_UNTIL : + static_cast(this)->subformula1->traverse(f, mode); + break; + + case LTL_V : + static_cast(this)->subformula1->traverse(f, mode); + break; + + case LTL_WEAK_UNTIL : + static_cast(this)->subformula1->traverse(f, mode); + break; + + case LTL_STRONG_RELEASE : + static_cast(this)->subformula1->traverse(f, mode); + break; + + default : /* LTL_BEFORE */ + static_cast(this)->subformula1->traverse(f, mode); + break; + } + + if (mode & LTL_INORDER) + f(this, 1); + + switch (what()) + { + case LTL_CONJUNCTION : + static_cast(this)->subformula2->traverse(f, mode); + break; + + case LTL_DISJUNCTION : + static_cast(this)->subformula2->traverse(f, mode); + break; + + case LTL_IMPLICATION : + static_cast(this)->subformula2->traverse(f, mode); + break; + + case LTL_EQUIVALENCE : + static_cast(this)->subformula2->traverse(f, mode); + break; + + case LTL_XOR : + static_cast(this)->subformula2->traverse(f, mode); + break; + + case LTL_UNTIL : + static_cast(this)->subformula2->traverse(f, mode); + break; + + case LTL_V : + static_cast(this)->subformula2->traverse(f, mode); + break; + + case LTL_WEAK_UNTIL : + static_cast(this)->subformula2->traverse(f, mode); + break; + + case LTL_STRONG_RELEASE : + static_cast(this)->subformula2->traverse(f, mode); + break; + + default : /* LTL_BEFORE */ + static_cast(this)->subformula2->traverse(f, mode); + break; + } + +postorder_visit: + if (mode & LTL_POSTORDER) + f(this, 2); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlFormula::ptr_less. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlFormula::ptr_less::operator() + (const LtlFormula* f1, const LtlFormula* f2) const +/* ---------------------------------------------------------------------------- + * + * Description: This function maps the `less than' relation between two + * pointers to LtlFormula objects to the corresponding relation + * between the formulae. Used when storing pointers to + * LtlFormulae into STL containers. + * + * Arguments: f1, f2 -- Two pointers to LtlFormulae. + * + * Returns: A truth value according to the relation between the formulae. + * + * ------------------------------------------------------------------------- */ +{ + return f1->operator<(*f2); +} + + + +/****************************************************************************** + * + * Inline function definitions for class Atom. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline Atom& Atom::construct(long int a) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a new propositional atom into the shared formula + * storage. + * + * Arguments: a -- Identifier of the atom (>=0). + * + * Returns: A reference to the atom. + * + * ------------------------------------------------------------------------- */ +{ + if (a < 0) + throw Exception("cannot construct an atomic proposition with a negative " + "id"); + + return static_cast(LtlFormula::insertToStorage(new Atom(a))); +} + +/* ========================================================================= */ +inline Atom::Atom(long int a) : + LtlFormula(), atom(a) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class Atom. Creates a new propositional atom. + * + * Argument: a -- Identifier of a propositional variable. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + info_flags.is_propositional = 1; + info_flags.is_constant = 0; +} + +/* ========================================================================= */ +inline Atom::~Atom() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class Atom. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline long int Atom::getId() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the identifier of an atomic proposition. + * + * Arguments: None. + * + * Returns: Identifier of the atomic proposition. + * + * ------------------------------------------------------------------------- */ +{ + return atom; +} + +/* ========================================================================= */ +inline bool Atom::operator<(const LtlFormula& f) const +/* ---------------------------------------------------------------------------- + * + * Description: ``Less than'' comparison between an Atom and an LtlFormula. + * An Atom is ``less than'' another LtlFormula if and only if + * the corresponding relation holds between the integer type + * identifiers between the formulae or if the other formula is + * also an Atom, but it refers to a propositional variable + * with a larger identifier. + * + * Arguments: f -- A reference to an LtlFormula. + * + * Returns: A truth value according to the result of the comparison. + * + * ------------------------------------------------------------------------- */ +{ + if (what() < f.what()) + return true; + + if (f.what() < what()) + return false; + + return atom < static_cast(f).atom; +} + +/* ========================================================================= */ +inline FormulaType Atom::what() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the `formula type' of the atom, i.e. the constant + * LTL_ATOM. + * + * Arguments: None. + * + * Returns: The constant LTL_ATOM. + * + * ------------------------------------------------------------------------- */ +{ + return LTL_ATOM; +} + +/* ========================================================================= */ +inline bool Atom::eval(const BitArray& valuation) const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the truth value of the atom in a given truth + * assignment. + * + * Argument: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * + * Returns: The truth value of the atom in the assignment. (If the + * atom identifier exceeds + * `LtlFormula::eval_proposition_id_limit' representing the + * assignment, the truth value of the atom is assumed to be + * false.) + * + * ------------------------------------------------------------------------- */ +{ + return (static_cast(atom) + < LtlFormula::eval_proposition_id_limit + && valuation.test(atom)); +} + + + +/****************************************************************************** + * + * Inline function definitions for template class Constant. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline Constant& Constant::construct() +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a new propositional constant into the shared formula + * storage. + * + * Arguments: None. + * + * Returns: A reference to the constant. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast&> + (LtlFormula::insertToStorage(new Constant())); +} + +/* ========================================================================= */ +template +inline Constant::Constant() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for a LTL constant. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + info_flags.is_propositional = 1; + info_flags.is_constant = 1; +} + +/* ========================================================================= */ +template +inline Constant::~Constant() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for a LTL constant. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline bool Constant::operator<(const LtlFormula& f) const +/* ---------------------------------------------------------------------------- + * + * Description: ``Less than'' comparison between a constant and an + * LtlFormula. A constant is ``less than'' another LtlFormula if + * and only if the corresponding relation holds between the + * integer type identifiers between the formulae. + * + * Arguments: f -- A reference to an LtlFormula. + * + * Returns: A truth value according to the result of the comparison. + * + * ------------------------------------------------------------------------- */ +{ + return (what() < f.what()); +} + +/* ========================================================================= */ +template +inline FormulaType Constant::what() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the type of the constant. + * + * Arguments: None. + * + * Returns: Type of the constant as a value of type FormulaType. + * + * ------------------------------------------------------------------------- */ +{ + return Symbol::type; +} + +/* ========================================================================= */ +template +inline bool Constant::eval(const BitArray&) const +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the constant in a truth assignment, i.e., simply + * returns the constant's truth value. + * + * Argument: A reference to a constant BitArray (required in order to + * satisfy the class interface restrictions). + * + * Returns: Truth value of the constant. + * + * ------------------------------------------------------------------------- */ +{ + return Symbol::eval(); +} + + + +/****************************************************************************** + * + * Inline function definitions for class True. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlTrue::eval() +/* ---------------------------------------------------------------------------- + * + * Description: Tells the truth value of a `true' constant. + * + * Arguments: None. + * + * Returns: true + * + * ------------------------------------------------------------------------- */ +{ + return true; +} + + + +/****************************************************************************** + * + * Inline function definitions for class False. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlFalse::eval() +/* ---------------------------------------------------------------------------- + * + * Description: Tells the truth value of a `false' constant. + * + * Arguments: None. + * + * Returns: false + * + * ------------------------------------------------------------------------- */ +{ + return false; +} + + + +/****************************************************************************** + * + * Inline function definitions for template class UnaryFormula. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline UnaryFormula& UnaryFormula::construct(LtlFormula* f) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a new unary formula into the shared formula storage. + * + * Arguments: f -- A pointer to the subformula to be associated with the + * unary formula. After the initialization, the new + * object will ``own'' its subformula. + * + * Returns: A reference to the formula. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast&> + (LtlFormula::insertToStorage(new UnaryFormula(f))); +} + +/* ========================================================================= */ +template +inline UnaryFormula& UnaryFormula::construct(LtlFormula& f) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a new unary formula into the shared formula storage. + * + * Arguments: f -- A reference to a LtlFormula to be associated with the + * unary formula. + * + * Returns: A reference to the formula. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast&> + (LtlFormula::insertToStorage(new UnaryFormula + (f.clone()))); +} + +/* ========================================================================= */ +template +inline UnaryFormula::UnaryFormula(LtlFormula* f) : + LtlFormula(), subformula(f) +/* ---------------------------------------------------------------------------- + * + * Description: Constructs an LTL formula with a unary operator. + * + * Argument: f -- Pointer to the subformula of the operator. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + info_flags.is_propositional = (f->propositional() + && Operator::type == LTL_NEGATION); + info_flags.is_constant = f->constant(); +} + +/* ========================================================================= */ +template +inline UnaryFormula::~UnaryFormula() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for unary LTL formulae. Releases the memory + * allocated for the operand of the unary operator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + LtlFormula::destruct(subformula); +} + +/* ========================================================================= */ +template +inline bool UnaryFormula::operator<(const LtlFormula& f) const +/* ---------------------------------------------------------------------------- + * + * Description: ``Less than'' comparison between a unary formula and an + * LtlFormula. A unary formula is ``less than'' another + * LtlFormula if and only if the corresponding relation holds + * between the integer type identifiers between the formulae, + * or if both formulae are of the same type and the relation + * holds between their subformulae. + * + * Arguments: f -- A reference to an LtlFormula. + * + * Returns: A truth value according to the result of the comparison. + * + * ------------------------------------------------------------------------- */ +{ + if (what() < f.what()) + return true; + + if (f.what() < what()) + return false; + + return subformula->operator< + (*(static_cast&>(f).subformula)); +} + +/* ========================================================================= */ +template +inline FormulaType UnaryFormula::what() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the exact type of the unary formula. + * + * Arguments: None. + * + * Returns: Type of the formula (a FormulaType constant). + * + * ------------------------------------------------------------------------- */ +{ + return Operator::type; +} + +/* ========================================================================= */ +template +inline bool UnaryFormula::eval(const BitArray& valuation) const +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the unary formula in a given truth assignment (in + * a state space consisting of a single state with a transition + * to itself). + * + * Argument: A reference to a constant BitArray, defining the truth + * assignment. + * + * Returns: Truth value of the formula in the assignment. + * + * ------------------------------------------------------------------------- */ +{ + return Operator::eval(valuation, subformula); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlNegation. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlNegation::eval(const BitArray& valuation, const LtlFormula* f) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the negated formula in a given assignment for the + * propositional variables. + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f -- A pointer to the subformula of the negation + * operator. + * + * Returns: The truth value of the formula in the truth assignment. + * + * ------------------------------------------------------------------------- */ +{ + return (!f->eval(valuation)); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlNext. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlNext::eval(const BitArray& valuation, const LtlFormula* f) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the `Next' formula in a given truth assignment for + * the propositional variables (in a state space consisting of a + * single state with a transition to itself). + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f -- A pointer to the subformula of the `Next' + * operator. + * + * Returns: The truth value of the formula in the assignment. + * + * ------------------------------------------------------------------------- */ +{ + return f->eval(valuation); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlFinally. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlFinally::eval(const BitArray& valuation, const LtlFormula* f) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the `Finally' formula in a given truth assignment + * for the propositional variables (in a state space consisting + * of a single state with a transition to itself). + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f -- A pointer to the subformula of the `Finally' + * operator. + * + * Returns: The truth value of the formula in the assignment. + * + * ------------------------------------------------------------------------- */ +{ + return f->eval(valuation); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlGlobally. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlGlobally::eval(const BitArray& valuation, const LtlFormula* f) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the `Globally' formula in a given truth assignment + * for the propositional variables (in a state space consisting + * of a single state with a transition to itself). + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f -- A pointer to the subformula of the `Globally' + * operator. + * + * Returns: The truth value of the formula in the assignment. + * + * ------------------------------------------------------------------------- */ +{ + return f->eval(valuation); +} + + + +/****************************************************************************** + * + * Inline function definitions for template class BinaryFormula. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline BinaryFormula& +BinaryFormula::construct(LtlFormula* f1, LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a new binary formula into the shared formula storage. + * + * Arguments: f1, f2 -- Pointers to the subformulae to be associated with + * the binary formula. The new returned object will + * ``own'' its both subformulae. + * + * Returns: A reference to the formula. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast&> + (LtlFormula::insertToStorage(new BinaryFormula(f1, f2))); +} + +/* ========================================================================= */ +template +inline BinaryFormula& +BinaryFormula::construct(LtlFormula& f1, LtlFormula& f2) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a new binary formula into the shared formula storage. + * + * Arguments: f1, f2 -- References to two LtlFormulae to be associated + * with the binary formula. + * + * Returns: A reference to the formula. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast&> + (LtlFormula::insertToStorage(new BinaryFormula + (f1.clone(), f2.clone()))); +} + +/* ========================================================================= */ +template +inline BinaryFormula& +BinaryFormula::construct(LtlFormula* f1, LtlFormula& f2) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a new binary formula into the shared formula storage. + * + * Arguments: f1 -- A pointer to a LtlFormula to be associated with the + * binary formula (the ``left-hand'' subformula). + * f2 -- A reference to a LtlFormula to be associated with the + * binary formula (the ``right-hand'' subformula). + * + * Returns: A reference to the formula. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast&> + (LtlFormula::insertToStorage(new BinaryFormula + (f1, f2.clone()))); +} + +/* ========================================================================= */ +template +inline BinaryFormula& +BinaryFormula::construct(LtlFormula& f1, LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a new binary formula into the shared formula storage. + * + * Arguments: f1 -- A reference to a LtlFormula to be associated with the + * binary formula (the ``left-hand'' subformula). + * f2 -- A pointer to a LtlFormula to be associated with the + * binary formula (the ``right-hand'' subformula). + * + * Returns: A reference to the formula. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast&> + (LtlFormula::insertToStorage(new BinaryFormula + (f1.clone(), f2))); +} + +/* ========================================================================= */ +template +inline BinaryFormula::BinaryFormula(LtlFormula* f1, LtlFormula* f2) : + LtlFormula(), subformula1(f1), subformula2(f2) +/* ---------------------------------------------------------------------------- + * + * Description: Constructs a binary LTL formula. + * + * Arguments: f1 -- Pointer to an LTL formula to be associated with the + * new formula as its left operand. + * f2 -- Pointer to an LTL formula to be associated with the + * new formula as its right operand. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + info_flags.is_propositional = (f1->propositional() + && f2->propositional() + && Operator::type != LTL_UNTIL + && Operator::type != LTL_V + && Operator::type != LTL_WEAK_UNTIL + && Operator::type != LTL_STRONG_RELEASE + && Operator::type != LTL_BEFORE); + info_flags.is_constant = (f1->constant() && f2->constant()); +} + +/* ========================================================================= */ +template +inline BinaryFormula::~BinaryFormula() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for binary LTL formulae. Releases the memory + * allocated for the operands of the binary operator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + LtlFormula::destruct(subformula1); + LtlFormula::destruct(subformula2); +} + +/* ========================================================================= */ +template +inline bool BinaryFormula::operator<(const LtlFormula& f) const +/* ---------------------------------------------------------------------------- + * + * Description: ``Less than'' comparison between a binary formula and an + * LtlFormula. A binary formula is ``less than'' another + * LtlFormula if and only if + * (1) the corresponding relation holds between the integer + * type identifiers between the formulae; + * OR (2) both formulae are of the same type and the relation + * holds between their ``left-hand'' subformulae; + * OR (3) both formulae are of the same type, their + * ``left-hand'' subformulae are of the same type, and + * the relation holds between the ``right-hand'' + * subformulae + * + * Arguments: f -- A reference to an LtlFormula. + * + * Returns: A truth value according to the result of the comparison. + * + * ------------------------------------------------------------------------- */ +{ + if (what() < f.what()) + return true; + + if (f.what() < what()) + return false; + + const BinaryFormula& ff + = static_cast&>(f); + + if (subformula1->operator<(*ff.subformula1)) + return true; + + if (ff.subformula1->operator<(*subformula1)) + return false; + + return (subformula2->operator<(*ff.subformula2)); +} + +/* ========================================================================= */ +template +inline FormulaType BinaryFormula::what() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the exact type of the binary formula. + * + * Arguments: None. + * + * Returns: Type of the formula (a FormulaType constant). + * + * ------------------------------------------------------------------------- */ +{ + return Operator::type; +} + +/* ========================================================================= */ +template +inline bool BinaryFormula::eval(const BitArray& valuation) const +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the binary formula in a given truth assignment (in + * a state space consisting of a single state with a transition + * to itself). + * + * Argument: A reference to a constant BitArray, defining the truth + * assignment. + * + * Returns: Truth value of the formula in the assignment. + * + * ------------------------------------------------------------------------- */ +{ + return Operator::eval(valuation, subformula1, subformula2); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlDisjunction. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlDisjunction::eval + (const BitArray& valuation, const LtlFormula* f1, const LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the disjunctive formula in a given assignment for + * the propositional variables. + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f1, f2 -- Pointers to the subformulae of the disjunction + * operator. + * + * Returns: The truth value of the formula in the truth assignment. + * + * ------------------------------------------------------------------------- */ +{ + return (f1->eval(valuation) || f2->eval(valuation)); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlConjunction. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlConjunction::eval + (const BitArray& valuation, const LtlFormula* f1, const LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the conjunctive formula in a given assignment for + * the propositional variables. + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f1, f2 -- Pointers to the subformulae of the conjunction + * operator. + * + * Returns: The truth value of the formula in the truth assignment. + * + * ------------------------------------------------------------------------- */ +{ + return (f1->eval(valuation) && f2->eval(valuation)); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlImplication. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlImplication::eval + (const BitArray& valuation, const LtlFormula* f1, const LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the implication in a given assignment for the + * propositional variables. + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f1, f2 -- Pointers to the subformulae of the implication + * operator. + * + * Returns: The truth value of the formula in the truth assignment. + * + * ------------------------------------------------------------------------- */ +{ + return (!f1->eval(valuation) || f2->eval(valuation)); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlEquivalence. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlEquivalence::eval + (const BitArray& valuation, const LtlFormula* f1, const LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the equivalence in a given assignment for the + * propositional variables. + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f1, f2 -- Pointers to the subformulae of the equivalence + * operator. + * + * Returns: The truth value of the formula in the truth assignment. + * + * ------------------------------------------------------------------------- */ +{ + return (f1->eval(valuation) == f2->eval(valuation)); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlXor. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlXor::eval + (const BitArray& valuation, const LtlFormula* f1, const LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the exclusive disjunction in a given assignment for + * the atomic propositions. + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f1, f2 -- Pointers to the subformulae of the ``exclusive + * or'' operator. + * + * Returns: The truth value of the formula in the truth assignment. + * + * ------------------------------------------------------------------------- */ +{ + return (f1->eval(valuation) != f2->eval(valuation)); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlUntil. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlUntil::eval + (const BitArray& valuation, const LtlFormula*, const LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the `Until' formula in a given truth assignment + * for the propositional variables (in a state space consisting + * of a single state with a transition to itself). + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f1, f2 -- Pointers to the subformulae of the `Until' + * operator. + * + * Returns: The truth value of the formula in the assignment. + * + * ------------------------------------------------------------------------- */ +{ + return f2->eval(valuation); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlV. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlV::eval + (const BitArray& valuation, const LtlFormula*, const LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the `Release' formula in a given truth assignment + * for the propositional variables (in a state space consisting + * of a single state with a transition to itself). + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f1, f2 -- A pointer to the subformula of the `V' + * operator. + * + * Returns: The truth value of the formula in the assignment. + * + * ------------------------------------------------------------------------- */ +{ + return f2->eval(valuation); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlWeakUntil. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlWeakUntil::eval + (const BitArray& valuation, const LtlFormula* f1, const LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the `Weak until' formula in a given truth + * assignment for the propositional variables (in a state space + * consisting of a single state with a transition to itself). + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f1, f2 -- Pointers to the subformulae of the `Weak + * until' operator. + * + * Returns: The truth value of the formula in the assignment. + * + * ------------------------------------------------------------------------- */ +{ + return (f1->eval(valuation) || f2->eval(valuation)); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlStrongRelease. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlStrongRelease::eval + (const BitArray& valuation, const LtlFormula* f1, const LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the `Strong release' formula in a given truth + * assignment for the propositional variables (in a state space + * consisting of a single state with a transition to itself). + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f1, f2 -- Pointers to the subformulae of the `String + * release' operator. + * + * Returns: The truth value of the formula in the assignment. + * + * ------------------------------------------------------------------------- */ +{ + return (f1->eval(valuation) && f2->eval(valuation)); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlBefore. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline bool LtlBefore::eval + (const BitArray& valuation, const LtlFormula*, const LtlFormula* f2) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates the `Before' formula in a given truth assignment + * for the propositional variables (in a state space consisting + * of a single state with a transition to itself). + * + * Arguments: valuation -- A reference to a constant BitArray + * representing the truth assignment. + * f1, f2 -- Pointers to the subformulae of the `String + * release' operator. + * + * Returns: The truth value of the formula in the assignment. + * + * ------------------------------------------------------------------------- */ +{ + return !f2->eval(valuation); +} + + + +/****************************************************************************** + * + * Inline function definitions for class LtlFormula::ParseErrorException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline LtlFormula::ParseErrorException::ParseErrorException + (const string& msg) : + Exception(msg) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class LtlFormula::ParseErrorException. + * Initializes an exception object with an error message. + * + * Argument: msg -- Error message. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline LtlFormula::ParseErrorException::~ParseErrorException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class LtlFormula::ParseErrorException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline LtlFormula::ParseErrorException& +LtlFormula::ParseErrorException::operator=(const ParseErrorException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class + * LtlFormula::ParseErrorException. + * + * Argument: e -- A reference to a constant exception object of the + * same type. + * + * Returns: A reference to the exception object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + +} + +#endif /* !LTLFORMULA_H */ diff --git a/lbtt/src/Makefile.am b/lbtt/src/Makefile.am new file mode 100644 index 000000000..4c45bc70d --- /dev/null +++ b/lbtt/src/Makefile.am @@ -0,0 +1,22 @@ +bin_PROGRAMS = lbtt lbtt-translate +lbtt_SOURCES = Alloc.h BitArray.h Bitset.h BitArray.cc BuchiAutomaton.h \ +BuchiAutomaton.cc Config-parse.yy Config-lex.ll Configuration.h \ +Configuration.cc DispUtil.h DispUtil.cc EdgeContainer.h Exception.h \ +FormulaRandomizer.h FormulaRandomizer.cc FormulaWriter.h LtlFormula.h \ +LtlFormula.cc main.cc PathEvaluator.h PathEvaluator.cc PathIterator.h \ +PathIterator.cc ProductAutomaton.h ProductAutomaton.cc Random.h SccIterator.h \ +SharedTestData.h StatDisplay.h StatDisplay.cc StateSpace.h StateSpace.cc \ +StateSpaceRandomizer.h StateSpaceRandomizer.cc StringUtil.h StringUtil.cc \ +TestOperations.h TestOperations.cc TestRoundInfo.h TestStatistics.h \ +TestStatistics.cc UserCommandReader.h UserCommandReader.cc UserCommands.h \ +UserCommands.cc +EXTRA_lbtt_SOURCES = gnu-getopt.h Config-parse.h +lbtt_LDADD = @LIBOBJS@ @READLINELIBS@ + +lbtt_translate_SOURCES = Alloc.h BitArray.h BitArray.cc Exception.h \ +ExternalTranslator.h ExternalTranslator.cc FormulaWriter.h LbtWrapper.h \ +LtlFormula.h LtlFormula.cc NeverClaim-parse.yy NeverClaim-lex.ll \ +NeverClaimAutomaton.h NeverClaimAutomaton.cc SpinWrapper.h SpinWrapper.cc \ +StringUtil.h StringUtil.cc translate.h translate.cc TranslatorInterface.h +EXTRA_lbtt_translate_SOURCES = gnu-getopt.h NeverClaim-parse.h +lbtt_translate_LDADD = @LIBOBJS@ diff --git a/lbtt/src/NeverClaim-lex.ll b/lbtt/src/NeverClaim-lex.ll new file mode 100644 index 000000000..8123ee3a8 --- /dev/null +++ b/lbtt/src/NeverClaim-lex.ll @@ -0,0 +1,187 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +%{ +#include +#include +#include +#include "LtlFormula.h" +#include "NeverClaim-parse.h" + +extern string current_neverclaim_line; +extern int current_neverclaim_line_number; +%} + +%option never-interactive +%option noyywrap + +%% + +[ \t] { + current_neverclaim_line += yytext; + /* Skip whitespace. */ + } +\n { + current_neverclaim_line = ""; + current_neverclaim_line_number++; + /* Skip newlines. */ + } +"/*"([^\*]*(\*[^/])?)*"*/" { + char* s = yytext, *t; + + do + { + t = strchr(s, '\n'); + if (t != static_cast(0)) + { + current_neverclaim_line = ""; + current_neverclaim_line_number++; + s = t + 1; + } + } + while (t != static_cast(0)); + + current_neverclaim_line += s; + + /* Skip comments. */ + } + +never { + current_neverclaim_line += yytext; + return NC_NEVER; + } + +if { + current_neverclaim_line += yytext; + return NC_IF; + } + +fi { + current_neverclaim_line += yytext; + return NC_FI; + } + +goto { + current_neverclaim_line += yytext; + return NC_GOTO; + } + +skip { + current_neverclaim_line += yytext; + return NC_SKIP; + } + +"::" { + current_neverclaim_line += yytext; + return NC_DOUBLE_COLON; + } + +":" { + current_neverclaim_line += yytext; + return NC_COLON; + } + +";" { + current_neverclaim_line += yytext; + return NC_SEMICOLON; + } + +"{" { + current_neverclaim_line += yytext; + return NC_LBRACE; + } + +"}" { + current_neverclaim_line += yytext; + return NC_RBRACE; + } + +"(" { + current_neverclaim_line += yytext; + return NC_LPAREN; + } + +")" { + current_neverclaim_line += yytext; + return NC_RPAREN; + } + +"->" { + current_neverclaim_line += yytext; + return NC_RIGHT_ARROW; + } + +"p"[0-9]+ { + current_neverclaim_line += yytext; + yylval.pf = new string(yytext); + return NC_PROPOSITION; + } + +"1"|true { + current_neverclaim_line += yytext; + yylval.pf = new string; + *yylval.pf += ::Ltl::LtlTrue::prefix_symbol; + return NC_TRUE; + } + +"0"|false { + current_neverclaim_line += yytext; + yylval.pf = new string; + *yylval.pf += ::Ltl::LtlFalse::prefix_symbol; + return NC_FALSE; + } + +"||" { + current_neverclaim_line += yytext; + return NC_OR; + } + +"&&" { + current_neverclaim_line += yytext; + return NC_AND; + } + +"!" { + current_neverclaim_line += yytext; + return NC_NOT; + } + +[A-Za-z_][A-Za-z0-9_]* { + current_neverclaim_line += yytext; + yylval.str = new string(yytext); + return NC_LABEL; + } + +%% + +/* ========================================================================= */ +int getCharacter() +/* ---------------------------------------------------------------------------- + * + * Description: Reads the next character from the lexer input stream. + * + * Arguments: None. + * + * Returns: The next character in the lexer input stream or EOF if there + * are no more characters to read. + * + * ------------------------------------------------------------------------- */ +{ + return yyinput(); +} diff --git a/lbtt/src/NeverClaim-parse.yy b/lbtt/src/NeverClaim-parse.yy new file mode 100644 index 000000000..8688dd00a --- /dev/null +++ b/lbtt/src/NeverClaim-parse.yy @@ -0,0 +1,301 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +%{ + +#include +#include +#include +#include "LtlFormula.h" +#include "NeverClaimAutomaton.h" + +/****************************************************************************** + * + * Variables used when parsing a never claim. + * + *****************************************************************************/ + +static NeverClaimAutomaton* automaton; /* Automaton in which the + * results are stored. + */ + +string current_neverclaim_line; /* Current input line. */ + +int current_neverclaim_line_number; /* Number of the current + * line in the never claim + * file. + */ + + + +/****************************************************************************** + * + * Declarations for external functions and variables (provided by the lexer) + * used when parsing a never claim. + * + *****************************************************************************/ + +extern void yyrestart(FILE*); /* Changes the input stream + * for the lexer (provided + * by the lexer). + */ + +extern int yylex(); /* Reads the next token + * from the input (this + * function is provided by + * the lexer). + */ + +extern int getCharacter(); /* Returns the next + * character in the lexer + * input stream (defined + * in NeverClaim-lex.ll). + */ + +extern int yyleng; /* Length of the last + * token parsed from the + * input (provided by the + * lexer). + */ + + + +/****************************************************************************** + * + * Function to be called in case of a parse error. + * + *****************************************************************************/ + +/* ========================================================================= */ +void yyerror(char*) +/* ---------------------------------------------------------------------------- + * + * Description: Function for reporting never claim parse errors. + * + * Arguments: A pointer to a char (required to satisfy the function + * interface). + * + * Returns: Nothing. Instead, throws an exception with information about + * the location of the error. + * + * ------------------------------------------------------------------------- */ +{ + int c; + + string::size_type error_pos = current_neverclaim_line.length(); + if (error_pos > static_cast(yyleng)) + error_pos -= yyleng; + + do + { + c = getCharacter(); + if (c != EOF && c != '\n') + current_neverclaim_line += static_cast(c); + } + while (c != EOF && c != '\n'); + + throw ParseErrorException + (current_neverclaim_line, current_neverclaim_line_number, error_pos); +} + + + +%} + + + +/****************************************************************************** + * + * Declarations for terminal and nonterminal symbols used in the grammar rules + * below. + * + *****************************************************************************/ + +/* Data types. */ + +%union { + string* pf; + string* str; +} + +/* Keywords. */ + +%token NC_NEVER NC_IF NC_FI NC_GOTO NC_SKIP + +/* State labels. */ + +%token NC_LABEL + +/* Punctuation symbols. */ + +%token NC_COLON NC_SEMICOLON NC_DOUBLE_COLON NC_LBRACE NC_RBRACE NC_LPAREN + NC_RPAREN NC_RIGHT_ARROW + +/* Propositional formulae. */ + +%token NC_PROPOSITION NC_TRUE NC_FALSE +%right NC_OR +%right NC_AND +%nonassoc NC_NOT + +%type formula + +%% + + + +/****************************************************************************** + * + * Grammar rule definitions. + * + *****************************************************************************/ + +never_claim: NC_NEVER NC_LBRACE states optional_semicolon NC_RBRACE + ; + +optional_semicolon: NC_SEMICOLON + | /* empty */ + ; + +states: state + | states NC_SEMICOLON state + ; + +state: { + automaton->addNewState(); + } + state_labels state_body + ; + +state_labels: NC_LABEL NC_COLON + { + automaton->addNewLabel(*$1); + delete $1; + } + | state_labels NC_LABEL NC_COLON + { + automaton->addNewLabel(*$2); + delete $2; + } + ; + +state_body: NC_IF transitions NC_FI + | NC_SKIP + | NC_FALSE + { + automaton->currentState()->accepting() = false; + } + | NC_IF NC_DOUBLE_COLON NC_FALSE NC_FI + { + automaton->currentState()->accepting() = false; + } + ; + +transitions: transition + | transitions transition + ; + +transition: NC_DOUBLE_COLON formula NC_RIGHT_ARROW NC_GOTO NC_LABEL + { + automaton->currentState()->addTransition(*$5, $2); + delete $5; + } + ; + +formula: NC_PROPOSITION + { + $$ = $1; + } + | NC_TRUE + { + $$ = $1; + } + | NC_FALSE + { + $$ = $1; + } + | NC_NOT formula + { + $$ = new string; + *$$ += ::Ltl::LtlNegation::prefix_symbol; + *$$ += ' '; + *$$ += *$2; + delete $2; + } + | formula NC_AND formula + { + $$ = new string; + *$$ += ::Ltl::LtlConjunction::prefix_symbol; + *$$ += ' '; + *$$ += *$1; + *$$ += ' '; + *$$ += *$3; + delete $1; + delete $3; + } + | formula NC_OR formula + { + $$ = new string; + *$$ += ::Ltl::LtlDisjunction::prefix_symbol; + *$$ += ' '; + *$$ += *$1; + *$$ += ' '; + *$$ += *$3; + delete $1; + delete $3; + } + | NC_LPAREN formula NC_RPAREN + { + $$ = $2; + } + ; + +%% + + + +/****************************************************************************** + * + * Main interface to the parser. + * + *****************************************************************************/ + +/* ========================================================================= */ +int parseNeverClaim(FILE* stream, NeverClaimAutomaton& a) +/* ---------------------------------------------------------------------------- + * + * Description: Main interface to the never claim file parser. Parses a + * never claim and stores the results into the given + * NeverClaimAutomaton object. + * + * Arguments: stream -- A pointer to a file from which the never claim + * should be read. The file is assumed to be open + * for reading. + * a -- A reference to a NeverClaimAutomaton object in + * which the results should be stored. + * + * Returns: + * + * ------------------------------------------------------------------------- */ +{ + yyrestart(stream); + automaton = &a; + current_neverclaim_line_number = 1; + return yyparse(); +} diff --git a/lbtt/src/NeverClaimAutomaton.cc b/lbtt/src/NeverClaimAutomaton.cc new file mode 100644 index 000000000..7f6ad3592 --- /dev/null +++ b/lbtt/src/NeverClaimAutomaton.cc @@ -0,0 +1,323 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include +#include "LtlFormula.h" +#include "NeverClaimAutomaton.h" +#include "StringUtil.h" + +/****************************************************************************** + * + * Declarations for functions and variables provided by the parser. + * + *****************************************************************************/ + +#include "NeverClaim-parse.h" /* Include declarations for + * the tokens that may be + * present in a never claim + * file. + */ + +extern int parseNeverClaim /* Parser interface. */ + (FILE*, NeverClaimAutomaton&); + +extern int current_neverclaim_line_number; /* Number of the current + * line in the never claim + * file. + */ + +/****************************************************************************** + * + * Function definitions for class NeverClaimAutomaton. + * + *****************************************************************************/ + +/* ========================================================================= */ +void NeverClaimAutomaton::clear() +/* ---------------------------------------------------------------------------- + * + * Description: Makes a NeverClaimAutomaton empty. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + for (vector::iterator + state = state_list.begin(); + state != state_list.end(); + ++state) + delete (*state); + + state_list.clear(); + label_mapping.clear(); + current_state = 0; +} + +/* ========================================================================= */ +void NeverClaimAutomaton::read(const char* input_filename) +/* ---------------------------------------------------------------------------- + * + * Description: Initializes a NeverClaimAutomaton by parsing a "never claim" + * stored in a file. + * + * Arguments: input_filename -- A C-style string containing the name of + * the input file. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + clear(); + + /* + * Parse the never claim in the given input file. + */ + + FILE* input_file = fopen(input_filename, "r"); + if (input_file == 0) + throw FileOpenException(string("`") + input_filename + "'"); + + try + { + parseNeverClaim(input_file, *this); + fclose(input_file); + } + catch (const ParseErrorException&) + { + fclose(input_file); + clear(); + throw; + } + + if (state_list.size() == 1) + state_list[0]->initial() = true; +} + +/* ========================================================================= */ +void NeverClaimAutomaton::write(const char* output_filename) +/* ---------------------------------------------------------------------------- + * + * Description: Writes a NeverClaimAutomaton to a file in `lbtt' format. + * + * Arguments: output_filename -- A C-style string containing the name of + * the output file. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + ofstream automaton_file; + automaton_file.open(output_filename, ios::out | ios::trunc); + if (!automaton_file.good()) + throw FileCreationException(string("`") + output_filename + "'"); + + try + { + Exceptional_ostream eautomaton_file(&automaton_file, + ios::failbit | ios::badbit); + + eautomaton_file << ::StringUtil::toString(state_list.size()) + " 1\n"; + + /* + * Go through the vector of states. For each state, write the following + * information to the output file: + * + * - The number of the state. + * + * - Information about whether the state is the initial state of + * the automaton. + * + * - Information about whether the state is an accepting state. + * + * - Transitions from the state to other states (in the form + * [state number] [guard formula]; end the transition list with + * `-1'. + */ + + for (vector::const_iterator + state = state_list.begin(); + state != state_list.end(); + ++state) + { + eautomaton_file << ::StringUtil::toString((*state)->number()) + ' ' + + ((*state)->initial() ? "1" : "0") + ' ' + + ((*state)->accepting() ? "0 " : "") + "-1\n"; + + for (multimap, ALLOC(Cstr*) >::const_iterator + transition = (*state)->transitions().begin(); + transition != (*state)->transitions().end(); ++transition) + { + if (label_mapping.find(transition->first) == label_mapping.end()) + { + remove(output_filename); + automaton_file.close(); + throw Exception("error in never claim: jump to undefined label `" + + transition->first + "'"); + } + + eautomaton_file << ::StringUtil::toString + ((*(label_mapping.find(transition->first))). + second->number()) + + ' ' + + ::StringUtil::toString(*(transition->second)) + + '\n'; + } + + eautomaton_file << "-1\n"; + } + + automaton_file.close(); + } + catch (const IOException&) + { + remove(output_filename); + automaton_file.close(); + throw; + } +} + +/* ========================================================================= */ +void NeverClaimAutomaton::addNewState() +/* ---------------------------------------------------------------------------- + * + * Description: Adds a state to a NeverClaimAutomaton and makes it the + * `current' state of the automaton. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + current_state = new StateInfo(state_list.size()); + state_list.push_back(current_state); +} + +/* ========================================================================= */ +void NeverClaimAutomaton::addNewLabel(Cstr& label) +/* ---------------------------------------------------------------------------- + * + * Description: Gives a new label for the `current' state of the automaton. + * The following substrings, if present in the label, have the + * following side effects (the side effects are cumulative): + * - If the label contains the substring "init", the current + * state is made an initial state. + * - If the label contains the substring "accept", the + * current state is made an accepting state. + * - If the state is labeled "accept_all", a transition is + * added from the state to itself. + * + * Argument: label -- A string giving the label for the state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + label_mapping[label] = current_state; + + if (label.find("init") != string::npos) + current_state->initial() = true; + if (label.find("accept") != string::npos) + current_state->accepting() = true; + if (label == "accept_all") + { + string* true_str = new string; + *true_str += ::Ltl::LtlTrue::prefix_symbol; + current_state->addTransition(label, true_str); + } +} + + + +/****************************************************************************** + * + * Function definitions for class NeverClaimAutomaton::StateInfo. + * + *****************************************************************************/ + +/* ========================================================================= */ +NeverClaimAutomaton::StateInfo::~StateInfo() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class NeverClaimAutomaton::StateInfo. + * Releases the memory allocated by the state object. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + for (multimap, ALLOC(Cstr*) >::const_iterator + transition = state_transitions.begin(); + transition != state_transitions.end(); + ++transition) + delete transition->second; +} + + + +/****************************************************************************** + * + * Function definitions for class ParseErrorException. + * + *****************************************************************************/ + +/* ========================================================================= */ +ParseErrorException::ParseErrorException + (const string& msg, int line_number, string::size_type error_pos) : + Exception() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class ParseErrorException. Initializes an + * exception object with an error message. + * + * Arguments: msg -- A string containing the error message. + * line_number -- If nonzero, `msg' is interpreted as the line + * containing the parse error. In this case, the + * error message indicates the position of the + * error on the line. + * error_pos -- Position of the error on the line. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (line_number == 0) + changeMessage(msg); + else + { + string space_string(msg.substr(0, error_pos)); + for (string::size_type c = 0; c < msg.length(); c++) + if (space_string[c] != ' ' && space_string[c] != '\t') + space_string[c] = ' '; + + changeMessage(string("never claim parse error on line ") + + StringUtil::toString(line_number) + '\n' + msg + '\n' + + space_string + "^\n"); + } +} diff --git a/lbtt/src/NeverClaimAutomaton.h b/lbtt/src/NeverClaimAutomaton.h new file mode 100644 index 000000000..6f93881f0 --- /dev/null +++ b/lbtt/src/NeverClaimAutomaton.h @@ -0,0 +1,460 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef NEVERCLAIMAUTOMATON_H +#define NEVERCLAIMAUTOMATON_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include +#include "Alloc.h" +#include "Exception.h" + +using namespace std; + +/****************************************************************************** + * + * A class for representing the Büchi automaton obtained by parsing a "never + * claim" (model checker Spin's representation for Büchi automata). This class + * provides only a very limited set of operations that suffice for parsing a + * never claim and outputting the parsed automaton in the format used by + * `lbtt'. + * + *****************************************************************************/ + +class NeverClaimAutomaton +{ +private: + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class StateInfo; /* A class for storing the + * states of the automaton. + */ + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + typedef const string Cstr; + +public: + NeverClaimAutomaton(); /* Constructor. */ + + ~NeverClaimAutomaton(); /* Destructor. */ + + void clear(); /* Makes the automaton + * empty. + */ + + void read(const char* input_filename); /* Initializes the + * automaton by parsing a + * never claim stored in a + * file. + */ + + void write(const char* output_filename); /* Outputs the automaton in + * `lbtt' format. + */ + + StateInfo* currentState(); /* Returns a pointer to the + * `current' state of the + * automaton (corresponding + * to the most recently + * introduced state in the + * never claim that is + * currently being parsed). + */ + + void addNewState(); /* Adds a new state to the + * automaton. + */ + + void addNewLabel(Cstr& label); /* Adds a new label for the + * `current' state of the + * automaton (a single + * state can have several + * different labels). + */ + +private: + vector /* States of the */ + state_list; /* automaton. */ + + map, /* Mapping from state */ + ALLOC(StateInfo*) > /* labels to the states */ + label_mapping; /* itself. */ + + StateInfo* current_state; /* Pointer to the state + * introduced most recently + * in the input file. + */ + + NeverClaimAutomaton /* Prevent copying and */ + (const NeverClaimAutomaton& automaton); /* assignment of */ + NeverClaimAutomaton& operator= /* NeverClaimAutomaton */ + (const NeverClaimAutomaton& automaton); /* objects. */ +}; + + + +/****************************************************************************** + * + * A class for storing the states of a Büchi automaton that is being generated + * by parsing a never claim. + * + *****************************************************************************/ + +class NeverClaimAutomaton::StateInfo +{ +public: + explicit StateInfo(unsigned long int num); /* Constructor. */ + + ~StateInfo(); /* Destructor. */ + + unsigned long int number() const; /* Returns the unique + * identifier of the + * state. + */ + + bool initial() const; /* Returns or changes */ + bool& initial(); /* the `initialness' */ + /* of the state. + */ + + bool accepting() const; /* Returns or changes */ + bool& accepting(); /* the acceptance status */ + /* of the state. + */ + + const multimap, /* Returns the labels of */ + ALLOC(Cstr*) >& /* the state's successor */ + transitions() const; /* states, including the + * conditions controlling + * the enabledness of the + * transition. + */ + + void addTransition /* Connects the state to */ + (Cstr& target_label, Cstr* guard); /* another state, given + * a state label and a + * propositional formula + * guarding the transition. + */ + +private: + StateInfo(const StateInfo&); /* Prevent copying and */ + StateInfo& operator=(const StateInfo&); /* assignment of + * StateInfo objects. + */ + + unsigned long int state_number; /* Unique state identifier. + */ + + bool is_initial; /* Is the state an initial + * state? + */ + + bool accept; /* Is the state an + * accepting state? + */ + + multimap, ALLOC(Cstr*) > /* Labels of the state's */ + state_transitions; /* successors, including + * the guard formulae + * controlling the + * enabledness of the + * transitions between + * states. + */ +}; + + + +/****************************************************************************** + * + * A class for reporting errors when parsing a never claim. + * + *****************************************************************************/ + +class ParseErrorException : public Exception +{ +public: + ParseErrorException /* Constructor. */ + (const string& msg + = "error parsing never claim", + int line_number = 0, + string::size_type error_pos = 0); + + /* default copy constructor */ + + ~ParseErrorException() throw(); /* Destructor. */ + + ParseErrorException& operator= /* Assignment operator. */ + (const ParseErrorException& e); +}; + + + +/****************************************************************************** + * + * Inline function definitions for class NeverClaimAutomaton. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline NeverClaimAutomaton::NeverClaimAutomaton() : + current_state(static_cast(0)) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class NeverClaimAutomaton. Creates an empty + * automaton. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline NeverClaimAutomaton::~NeverClaimAutomaton() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class NeverClaimAutomaton. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + clear(); +} + +/* ========================================================================= */ +inline NeverClaimAutomaton::StateInfo* NeverClaimAutomaton::currentState() +/* ---------------------------------------------------------------------------- + * + * Description: Returns a pointer to the "current" state of the automaton, + * i.e., the state corresponding to the most recently introduced + * state parsed from the never claim. + * + * Arguments: None. + * + * Returns: A pointer to the state. + * + * ------------------------------------------------------------------------- */ +{ + return current_state; +} + + + +/****************************************************************************** + * + * Inline function definitions for class NeverClaimAutomaton::StateInfo. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline NeverClaimAutomaton::StateInfo::StateInfo(unsigned long int num) : + state_number(num), is_initial(false), accept(false) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class NeverClaimAutomaton::StateInfo. + * Creates a new state with a given identifier. By default, the + * created state will be noninitial and nonaccepting. + * + * Arguments: num -- Numeric identifier for the state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline unsigned long int NeverClaimAutomaton::StateInfo::number() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the identifier of a state. + * + * Arguments: None. + * + * Returns: The identifier of the state. + * + * ------------------------------------------------------------------------- */ +{ + return state_number; +} + +/* ========================================================================= */ +inline bool NeverClaimAutomaton::StateInfo::initial() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether the state is an initial state. This function + * can be used only for querying the value. + * + * Arguments: None. + * + * Returns: Truth value telling whether the state is an initial state. + * + * ------------------------------------------------------------------------- */ +{ + return is_initial; +} + +/* ========================================================================= */ +inline bool& NeverClaimAutomaton::StateInfo::initial() +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether the state is an initial state. This function + * can be also used to change the `initialness' of the state. + * + * Arguments: None. + * + * Returns: A reference to a truth value telling whether the state is an + * initial state. + * + * ------------------------------------------------------------------------- */ +{ + return is_initial; +} + +/* ========================================================================= */ +inline bool NeverClaimAutomaton::StateInfo::accepting() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether the state is an accepting state. This function + * can be used only for querying the acceptance status. + * + * Arguments: None. + * + * Returns: A truth value telling whether the state is an accepting + * state. + * + * ------------------------------------------------------------------------- */ +{ + return accept; +} + +/* ========================================================================= */ +inline bool& NeverClaimAutomaton::StateInfo::accepting() +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether the state is an accepting state. This function + * can also be used to change the acceptance status. + * + * Arguments: None. + * + * Returns: A reference to a truth value telling whether the state is an + * accepting state. + * + * ------------------------------------------------------------------------- */ +{ + return accept; +} + +/* ========================================================================= */ +inline const multimap, + ALLOC(NeverClaimAutomaton::Cstr*) >& +NeverClaimAutomaton::StateInfo::transitions() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the set of labels of the state's successor states, + * including the propositional formulae containing the + * conditions (propositional formulae) controlling the + * enabledness of the transitions the state and its successors. + * + * Arguments: None. + * + * Returns: A constant reference to a constant multimap object containing + * the successor information. + * + * ------------------------------------------------------------------------- */ +{ + return state_transitions; +} + +/* ========================================================================= */ +inline void NeverClaimAutomaton::StateInfo::addTransition + (Cstr& target_label, Cstr* guard) +/* ---------------------------------------------------------------------------- + * + * Description: Connects the state to another state. + * + * Arguments: target_label -- Label of the target state. + * guard -- A pointer to a constant string containing + * the guard (a propositional formula) for + * the transition. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + state_transitions.insert(make_pair(target_label, guard)); +} + + + +/****************************************************************************** + * + * Inline function definitions for class ParseErrorException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline ParseErrorException::~ParseErrorException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class ParseErrorException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline ParseErrorException& +ParseErrorException::operator=(const ParseErrorException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class ParseErrorException. + * + * Argument: e -- A reference to a constant exception object of the + * same type. + * + * Returns: A reference to the exception object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + +#endif /* !NEVERCLAIMAUTOMATON_H */ diff --git a/lbtt/src/PathEvaluator.cc b/lbtt/src/PathEvaluator.cc new file mode 100644 index 000000000..84b769974 --- /dev/null +++ b/lbtt/src/PathEvaluator.cc @@ -0,0 +1,987 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include "PathEvaluator.h" + +namespace Ltl +{ + +/****************************************************************************** + * + * Function definitions for class PathEvaluator. + * + *****************************************************************************/ + +/* ========================================================================= */ +void PathEvaluator::reset() +/* ---------------------------------------------------------------------------- + * + * Description: Prepares the formula evaluator for a new computation. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + current_formula = static_cast(0); + current_path = static_cast(0); + current_loop_state = 0; + path_states.clear(); + + for (map::iterator it + = eval_info.begin(); + it != eval_info.end(); + ++it) + delete it->second; + + eval_info.clear(); +} + +/* ========================================================================= */ +bool PathEvaluator::evaluate + (const LtlFormula& formula, const StateSpace& statespace, + const vector& + states_on_path, + StateSpace::size_type loop_state) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates an LTL formula in a state space in which the states + * are connected into a non-branching sequence that ends in a + * loop. + * + * Arguments: formula -- Formula to be evaluated. + * statespace -- State space from which the path is + * extracted. + * states_on_path -- Mapping between states in the path and + * the states in `statespace' such that + * `statespace[states_on_path[i]]' + * corresponds to the ith state of the path. + * loop_state -- Number of the state in the path to which + * the ``last'' state of the path is + * connected. + * + * Returns: `true' if and only if the LTL formula holds in the path. + * + * ------------------------------------------------------------------------- */ +{ + reset(); + + if (states_on_path.empty() || loop_state >= states_on_path.size()) + return false; + + current_formula = &formula; + current_path = &statespace; + current_loop_state = loop_state; + path_states = states_on_path; + + return eval(); +} + +/* ========================================================================= */ +bool PathEvaluator::evaluate + (const LtlFormula& formula, const StateSpace& statespace) +/* ---------------------------------------------------------------------------- + * + * Description: Evaluates an LTL formula in a state space in which the states + * are connected into a non-branching sequence that ends in a + * loop. + * + * Arguments: formula -- Formula to be evaluated. + * statespace -- State space in which the formula should be + * evaluated. + * + * Returns: `true' if and only if the LTL formula holds in the path. + * + * ------------------------------------------------------------------------- */ +{ + reset(); + + if (statespace.empty()) + return false; + + current_formula = &formula; + current_path = &statespace; + + map, ALLOC(StateSpace::size_type) > ordering; + + StateSpace::size_type state = statespace.initialState(); + StateSpace::size_type state_count = 0; + + /* + * Construct a vector of state identifiers representing the path by + * traversing the state space until some state is encountered twice. + */ + + while (1) + { + path_states.push_back(state); + ordering[state] = state_count; + state_count++; + + if (statespace[state].edges().empty()) + throw Exception + ("PathEvaluator::compute: not a total transition relation"); + + state = (*(statespace[state].edges().begin()))->targetNode(); + + if (ordering.find(state) != ordering.end()) + break; + } + + current_loop_state = ordering[state]; + + return eval(); +} + +/* ========================================================================= */ +bool PathEvaluator::eval() +/* ---------------------------------------------------------------------------- + * + * Description: Implements the model checking algorithm for paths. + * + * Arguments: None. + * + * Returns: `true' if and only if `this->current_formula' holds in + * the path described by the contents of `this->path_states'. + * + * ------------------------------------------------------------------------- */ +{ + stack > + subformula_stack; + + const LtlFormula* f; + BitArray* val; + + current_formula->collectSubformulae(subformula_stack); + + try + { + while (!subformula_stack.empty()) + { + if (::user_break) + throw UserBreakException(); + + /* + * Pop a formula from the subformula stack. + */ + + f = subformula_stack.top(); + subformula_stack.pop(); + + /* + * Discard the current formula if its truth value is already known. + */ + + if (eval_info.find(f) != eval_info.end()) + continue; + + /* + * Otherwise allocate space for the evaluation results of the current + * formula and then evaluate it in all states of the path, using the + * truth values of its subformulae which have already been computed. + */ + + val = eval_info[f] = new BitArray(path_states.size()); + val->clear(path_states.size()); + + switch (f->what()) + { + case LTL_UNTIL : + case LTL_FINALLY : + case LTL_V : + case LTL_GLOBALLY : + case LTL_BEFORE : + { + StateSpace::size_type marker_state; + const LtlFormula* g, *h; + bool lfp, check_value_1, check_value_2, marker_valid = false; + + switch (f->what()) + { + case LTL_UNTIL : + g = static_cast(f)->subformula1; + h = static_cast(f)->subformula2; + check_value_1 = check_value_2 = true; + lfp = true; + break; + + case LTL_FINALLY : + g = 0; + h = static_cast(f)->subformula; + check_value_1 = check_value_2 = true; + lfp = true; + break; + + case LTL_V : + val->set(path_states.size()); + g = static_cast(f)->subformula1; + h = static_cast(f)->subformula2; + check_value_1 = check_value_2 = false; + lfp = false; + break; + + case LTL_GLOBALLY : + val->set(path_states.size()); + g = 0; + h = static_cast(f)->subformula; + check_value_1 = check_value_2 = false; + lfp = false; + break; + + default : /* LTL_BEFORE */ + val->set(path_states.size()); + g = static_cast(f)->subformula1; + h = static_cast(f)->subformula2; + check_value_1 = false; + check_value_2 = true; + lfp = false; + break; + } + + for (StateSpace::size_type state = 0; state < path_states.size(); + state++) + { + if (eval_info[h]->test(state) == check_value_2) + { + val->flipBit(state); + + if (marker_valid) + { + while (marker_state < state) + { + val->flipBit(marker_state); + marker_state++; + } + marker_valid = false; + } + } + else if (g == 0 || eval_info[g]->test(state) == check_value_1) + { + if (!marker_valid) + { + marker_valid = true; + marker_state = state; + } + } + else + marker_valid = false; + } + + if (marker_valid && eval_info[f]->test(current_loop_state) == lfp) + { + while (marker_state < path_states.size()) + { + val->flipBit(marker_state); + marker_state++; + } + } + + break; + } + + case LTL_WEAK_UNTIL : + case LTL_STRONG_RELEASE : + { + StateSpace::size_type marker_state; + const LtlFormula* g, *h; + bool check_value; + bool marker_valid = false; + + if (f->what() == LTL_WEAK_UNTIL) + { + val->set(path_states.size()); + h = static_cast(f)->subformula1; + g = static_cast(f)->subformula2; + check_value = false; + } + else + { + h = static_cast(f)->subformula1; + g = static_cast(f)->subformula2; + check_value = true; + } + + for (StateSpace::size_type state = 0; state < path_states.size(); + state++) + { + if (eval_info[g]->test(state) == check_value + && eval_info[h]->test(state) == check_value) + { + val->flipBit(state); + + if (marker_valid) + { + while (marker_state < state) + { + val->flipBit(marker_state); + marker_state++; + } + marker_valid = false; + } + } + else if (eval_info[g]->test(state) == check_value) + { + if (!marker_valid) + { + marker_valid = true; + marker_state = state; + } + } + else + marker_valid = false; + } + + if (marker_valid + && eval_info[f]->test(current_loop_state) == check_value) + { + while (marker_state < path_states.size()) + { + val->flipBit(marker_state); + marker_state++; + } + } + + break; + } + + case LTL_TRUE : + val->set(path_states.size()); + break; + + case LTL_FALSE : + break; + + case LTL_ATOM : + { + for (StateSpace::size_type s = 0; s < path_states.size(); s++) + { + /* + * Note: BitArray operations cannot be used directly, since the + * width of the array in `current_path' might be less than the + * atomic proposition identifier. + */ + + if (f->evaluate((*current_path)[path_states[s]].positiveAtoms(), + current_path->numberOfPropositions())) + val->setBit(s); + } + + break; + } + + case LTL_NEGATION : + { + const LtlFormula* g = static_cast(f)->subformula; + + for (StateSpace::size_type s = 0; s < path_states.size(); s++) + { + if (!eval_info[g]->test(s)) + val->setBit(s); + } + + break; + } + + case LTL_CONJUNCTION : + { + const LtlFormula* g = static_cast(f)->subformula1; + const LtlFormula* h = static_cast(f)->subformula2; + + for (StateSpace::size_type s = 0; s < path_states.size(); s++) + { + if (eval_info[g]->test(s) && eval_info[h]->test(s)) + val->setBit(s); + } + + break; + } + + case LTL_DISJUNCTION : + { + const LtlFormula* g = static_cast(f)->subformula1; + const LtlFormula* h = static_cast(f)->subformula2; + + for (StateSpace::size_type s = 0; s < path_states.size(); s++) + { + if (eval_info[g]->test(s) || eval_info[h]->test(s)) + val->setBit(s); + } + + break; + } + + case LTL_IMPLICATION : + { + const LtlFormula* g = static_cast(f)->subformula1; + const LtlFormula* h = static_cast(f)->subformula2; + + for (StateSpace::size_type s = 0; s < path_states.size(); s++) + { + if (!eval_info[g]->test(s) || eval_info[h]->test(s)) + val->setBit(s); + } + + break; + } + + case LTL_EQUIVALENCE : + { + const LtlFormula* g = static_cast(f)->subformula1; + const LtlFormula* h = static_cast(f)->subformula2; + + for (StateSpace::size_type s = 0; s < path_states.size(); s++) + { + if (eval_info[g]->test(s) == eval_info[h]->test(s)) + val->setBit(s); + } + + break; + } + + case LTL_XOR : + { + const LtlFormula* g = static_cast(f)->subformula1; + const LtlFormula* h = static_cast(f)->subformula2; + + for (StateSpace::size_type s = 0; s < path_states.size(); s++) + { + if (eval_info[g]->test(s) != eval_info[h]->test(s)) + val->setBit(s); + } + + break; + } + + default : /* LTL_NEXT */ + { + const LtlFormula* g = static_cast(f)->subformula; + StateSpace::size_type s; + + for (s = 0; (s + 1) < path_states.size(); s++) + { + if (eval_info[g]->test(s + 1)) + val->setBit(s); + } + + if (eval_info[g]->test(current_loop_state)) + val->setBit(s); + + break; + } + } + } + } + catch (...) + { + reset(); + throw; + } + + return eval_info[current_formula]->test(0); +} + +/* ========================================================================= */ +bool PathEvaluator::getResult(StateSpace::size_type state) const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the model checking result in the path that begins at + * the `state'th state of the path described by the vector + * `this->path_states'. + * + * Arguments: state -- Index of a state in the path. + * + * Returns: `true' if and only if the LTL formula `this->current_formula' + * holds in the path that begins at the given index. + * + * ------------------------------------------------------------------------- */ +{ + if (eval_info.empty()) + return false; + + return eval_info.find(current_formula)->second->test(state); +} + +/* ========================================================================= */ +void PathEvaluator::print(ostream& stream, const int indent) const +/* ---------------------------------------------------------------------------- + * + * Description: Displays a proof or a refutation for the formula + * `this->current_formula' in the state space described by the + * contents of `this->path_states'. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of output. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (eval_info.empty()) + return; + + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + recPrint(estream, indent, current_formula, 0); +} + +/* ========================================================================= */ +void PathEvaluator::recPrint + (Exceptional_ostream& estream, const int indent, const LtlFormula* f, + StateSpace::size_type state) const +/* ---------------------------------------------------------------------------- + * + * Description: Displays a recursive proof or a refutation for the formula + * `f' in the `state'th state of the state space described by + * the contents of `this->path_states'. + * + * Arguments: estream -- A reference to an exception-aware output + * stream. + * indent -- Number of spaces to leave to the left of output. + * state -- Index of a state in the path. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + static string line_prefix = ""; + + const bool prove_true = eval_info.find(f)->second->test(state); + + estream << string(indent, ' ') + + (line_prefix.size() == 0 + ? "" + : line_prefix.substr(0, line_prefix.size() - 4) + "+-> ") + + "M, |") + (prove_true ? '=' : '/') + "= " + << *f; + + switch (f->what()) + { + case LTL_ATOM : + case LTL_TRUE : + case LTL_FALSE : + estream << '\n'; + return; + + case LTL_NEGATION : + /* + * To display a proof (refutation) for a negated formula, display a + * refutation (proof) for the non-negated formula. + */ + + line_prefix += " "; + + estream << " :\n"; + recPrint(estream, indent, static_cast(f)->subformula, state); + + break; + + case LTL_NEXT : + { + /* + * To display a proof or refutation for a Next formula, display a + * proof or refutation for the subformula of the Next operator in + * the next state of the path. + */ + + estream << " :\n" + string(indent, ' ') + line_prefix + "+-> s" + << path_states[state] + << " --> s"; + + state++; + if (state == path_states.size()) + state = current_loop_state; + + estream << path_states[state] << '\n'; + + line_prefix += " "; + + recPrint(estream, indent, static_cast(f)->subformula, + state); + + break; + } + + case LTL_CONJUNCTION : case LTL_DISJUNCTION : case LTL_IMPLICATION : + case LTL_EQUIVALENCE : case LTL_XOR : + { + /* + * Formula: f1 OP f2 + * + * proof/refutation OP what will be shown + * ------------------------------------------------------------------ + * proof /\ proofs for `f1' and `f2' + * + * refutation /\ refutation for `f1' or `f2' + * + * proof \/ proof for `f1' or `f2' + * + * refutation \/ refutation for `f1 and `f2' + * + * proof -> proof for `f2' or refutation for + * `f1' + * + * refutation -> proof for `f1' and refutation for + * `f2' + * + * proof <-> proofs or refutations for both + * `f1' and `f2' + * + * refutation <-> proof for `f1' and refutation for + * `f2' or vice versa + * + * proof xor proof for `f1' and refutation for + * `f2' or vice versa + * + * refutation xor proofs or refutations for both + * `f1' and `f2' + */ + + estream << " :\n"; + + line_prefix += "| "; + + const LtlFormula* f1, *f2; + bool branch, val1, val2; + + switch (f->what()) + { + case LTL_CONJUNCTION : + f1 = static_cast(f)->subformula1; + f2 = static_cast(f)->subformula2; + branch = prove_true; + val1 = val2 = false; + break; + + case LTL_DISJUNCTION : + f1 = static_cast(f)->subformula1; + f2 = static_cast(f)->subformula2; + branch = !prove_true; + val1 = val2 = true; + break; + + case LTL_IMPLICATION : + f1 = static_cast(f)->subformula1; + f2 = static_cast(f)->subformula2; + branch = !prove_true; + val1 = false; + val2 = true; + break; + + case LTL_EQUIVALENCE : + f1 = static_cast(f)->subformula1; + f2 = static_cast(f)->subformula2; + branch = true; + break; + + default : /* LTL_XOR */ + f1 = static_cast(f)->subformula1; + f2 = static_cast(f)->subformula2; + branch = true; + break; + } + + if (branch) + { + recPrint(estream, indent, f1, state); + line_prefix[line_prefix.size() - 4] = ' '; + recPrint(estream, indent, f2, state); + } + else + { + line_prefix[line_prefix.size() - 4] = ' '; + + if (eval_info.find(f2)->second->test(state) != val2) + recPrint(estream, indent, f1, state); + else if (eval_info.find(f1)->second->test(state) != val1) + recPrint(estream, indent, f2, state); + else if ((f1->propositional() && !f2->propositional()) + || (f1->constant() && !f2->constant())) + recPrint(estream, indent, f1, state); + else if ((f2->propositional() && !f1->propositional()) + || (f2->constant() && !f1->constant())) + recPrint(estream, indent, f2, state); + else if (f1->size() <= f2->size()) + recPrint(estream, indent, f1, state); + else + recPrint(estream, indent, f2, state); + } + + break; + } + + default : /* LTL_UNTIL || LTL_FINALLY || LTL_V || LTL_GLOBALLY + || LTL_BEFORE || LTL_WEAK_UNTIL || LTL_STRONG_RELEASE */ + { + /* + * Formula: f1 OP f2 + * + * proof/refutation OP what will be shown + * ------------------------------------------------------------------ + * proof U proofs for `f1' until finding a + * state in which `f2' holds; + * then show a proof for `f2' + * + * refutation U refutations for `f2' until + * finding a state in which `f1' + * does not hold OR until some + * state is visited twice (in + * which case `f2' never holds in + * the path); if the search ends + * in a state where `f1' does not + * hold, show a refutation for + * `f1' and `f2' in the state + * + * proof <> proof for `f2' in the first state + * in which `f2' holds + * + * refutation <> refutation for `f2' in all states + * of the path + * + * proof V proofs for `f2' until finding a + * state in which `f1' holds OR + * until some state is visited + * twice (in which case `f2' + * always holds in the path); if + * the search ends in a state + * where `f1' holds, show a proof + * for `f1' and `f2' in the state + * + * refutation V refutations for `f1' until + * finding a state in which `f2' + * does not hold; then show a + * refutation for `f2' + * + * proof [] proof for `f2' in all states of + * the path + * + * refutation [] refutation for `f2' in the first + * state in which `f2' does not + * hold + * + * proof W proofs for `f1' until finding a + * state in which `f2' holds or + * until some state is visited + * twice (in which case `f1' + * always holds in the path); + * if the search ends in a state + * in which `f2' holds, show a + * proof for `f2' + * + * refutation W refutations for `f2' until + * finding a state in which + * neither `f1' and `f2' hold; + * then show a refutation for + * `f1' and `f2' + * + * proof M proofs for `f2' until finding a + * state in which both `f1' and + * `f2' hold; then show a proof + * for `f1' and `f2' + * + * refutation M refutations for `f1' until + * finding a state in which `f2' + * does not hold or until some + * state is visited twice (in + * which case `f1' never holds in + * the path); if the search ends + * in a state in which `f2' does + * not hold, show a refutation + * for `f2' + * + * proof B refutations for `f2' until + * finding a state in which `f1' + * holds OR until some state is + * visited twice (in which case + * `f2' never holds in the path); + * if the search ends in a state + * where `f1' holds, show a proof + * for `f1' and a refutation for + * `f2' in the state + * + * refutation B refutations for `f1' until + * finding a state in which `f2' + * holds; then show a proof for + * `f2' + * + */ + + estream << " :\n"; + + line_prefix += "| "; + + const LtlFormula* f1; + const LtlFormula* f2; + bool eventuality, check_value_1, check_value_2; + + switch (f->what()) + { + case LTL_UNTIL : + f1 = static_cast(f)->subformula1; + f2 = static_cast(f)->subformula2; + eventuality = true; + check_value_1 = check_value_2 = true; + break; + + case LTL_FINALLY : + f1 = 0; + f2 = static_cast(f)->subformula; + eventuality = true; + check_value_1 = check_value_2 = true; + break; + + case LTL_V : + f1 = static_cast(f)->subformula1; + f2 = static_cast(f)->subformula2; + eventuality = false; + check_value_1 = check_value_2 = false; + break; + + case LTL_GLOBALLY : + f1 = 0; + f2 = static_cast(f)->subformula; + eventuality = false; + check_value_1 = check_value_2 = false; + break; + + case LTL_BEFORE : + f1 = static_cast(f)->subformula1; + f2 = static_cast(f)->subformula2; + eventuality = false; + check_value_1 = false; + check_value_2 = true; + break; + + case LTL_WEAK_UNTIL : /* note the order of `f1' and `f2' */ + f1 = static_cast(f)->subformula2; + f2 = static_cast(f)->subformula1; + eventuality = false; + check_value_1 = check_value_2 = false; + break; + + default : /* LTL_STRONG_RELEASE; note the order of `f1' and `f2' */ + f1 = static_cast(f)->subformula2; + f2 = static_cast(f)->subformula1; + eventuality = true; + check_value_1 = check_value_2 = true; + break; + } + + if (prove_true == eventuality) + { + while (eval_info.find(f2)->second->test(state) != check_value_2) + { + if (f1 != 0) + recPrint(estream, indent, f1, state); + + estream << string(indent, ' ') + + line_prefix.substr(0, line_prefix.size() - 4) + + "+-> s" + << path_states[state] + << " --> s"; + + state++; + if (state == path_states.size()) + state = current_loop_state; + + estream << path_states[state] << '\n'; + } + + if (f->what() == LTL_WEAK_UNTIL || f->what() == LTL_STRONG_RELEASE) + { + recPrint(estream, indent, f2, state); + line_prefix[line_prefix.size() - 4] = ' '; + recPrint(estream, indent, f1, state); + } + else + { + line_prefix[line_prefix.size() - 4] = ' '; + recPrint(estream, indent, f2, state); + } + } + else + { + StateSpace::size_type start_state = state; + + while (f1 == 0 + || eval_info.find(f1)->second->test(state) == check_value_1) + { + recPrint(estream, indent, f2, state); + + estream << string(indent, ' ') + + line_prefix.substr(0, line_prefix.size() - 4) + + "+-> s" + << path_states[state] + << " --> s"; + + state++; + if (state == path_states.size()) + { + state = current_loop_state; + if (start_state < current_loop_state) + { + estream << path_states[state] << '\n'; + break; + } + } + + estream << path_states[state] << '\n'; + + if (state == start_state) + break; + } + + if (f1 != 0 + && eval_info.find(f1)->second->test(state) != check_value_1) + { + if (f->what() == LTL_WEAK_UNTIL || f->what() == LTL_STRONG_RELEASE) + { + line_prefix[line_prefix.size() - 4] = ' '; + recPrint(estream, indent, f1, state); + } + else + { + recPrint(estream, indent, f1, state); + line_prefix[line_prefix.size() - 4] = ' '; + recPrint(estream, indent, f2, state); + } + } + } + + break; + } + } + + line_prefix.resize(line_prefix.size() - 4); +} + +} diff --git a/lbtt/src/PathEvaluator.h b/lbtt/src/PathEvaluator.h new file mode 100644 index 000000000..f8ff84091 --- /dev/null +++ b/lbtt/src/PathEvaluator.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef PATHEVALUATOR_H +#define PATHEVALUATOR_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include "Alloc.h" +#include "BitArray.h" +#include "Exception.h" +#include "LtlFormula.h" +#include "StateSpace.h" + +namespace Ltl +{ + +using namespace Graph; + +/****************************************************************************** + * + * A class for testing whether an LtlFormula holds in a StateSpace that + * consists of states connected into a non-branching sequence, which ends in a + * loop. + * + *****************************************************************************/ + +class PathEvaluator +{ +public: + PathEvaluator(); /* Constructor. */ + + ~PathEvaluator(); /* Destructor. */ + + void reset(); /* Prepares the object for + * a new evaluation run. + */ + + bool evaluate /* Tests whether an */ + (const LtlFormula& formula, /* LtlFormula holds in a */ + const StateSpace& statespace, /* state space. */ + const vector& + states_on_path, + StateSpace::size_type loop_state); + + bool evaluate /* Same as above. */ + (const LtlFormula& formula, + const StateSpace& statespace); + + bool getResult(StateSpace::size_type state) /* Returns the result of */ + const; /* the evaluation in a + * given state of the + * current path after a + * call to `evaluate'. + */ + + void print /* Displays the results */ + (ostream& stream = cout, /* after a call to */ + const int indent = 0) const; /* `evaluate'. */ + +private: + PathEvaluator(const PathEvaluator&); /* Prevent copying and */ + PathEvaluator& operator=(const PathEvaluator&); /* assignment of + * PathEvaluator + * objects. + */ + + bool eval(); /* Evaluates a formula + * on a path. + */ + + void recPrint /* Recursively prints */ + (Exceptional_ostream& estream, /* a proof or a */ + const int indent, /* refutation for a */ + const LtlFormula* f, /* formula. */ + StateSpace::size_type state) const; + + const LtlFormula* current_formula; /* LTL formula associated + * with the path evaluator. + */ + + const StateSpace* current_path; /* State space associated + * with the path evaluator. + */ + + StateSpace::size_type current_loop_state; /* Number of the target + * state of the loop on + * the current path. + */ + + vector /* between states of the */ + path_states; /* path and the states + * of the current state + * space. + */ + + map /* truth values of the */ + eval_info; /* subformulae of the + * formula to be + * evaluated. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class PathEvaluator. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline PathEvaluator::PathEvaluator() : + current_formula(static_cast(0)), + current_path(static_cast(0)), + current_loop_state(0) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class PathEvaluator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline PathEvaluator::~PathEvaluator() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class PathEvaluator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + reset(); +} + +} + +#endif /* !PATHEVALUATOR_H */ diff --git a/lbtt/src/PathIterator.cc b/lbtt/src/PathIterator.cc new file mode 100644 index 000000000..3f468940a --- /dev/null +++ b/lbtt/src/PathIterator.cc @@ -0,0 +1,202 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include "BitArray.h" +#include "PathIterator.h" + +namespace Graph +{ + +/****************************************************************************** + * + * Function definitions for class PathIterator. + * + *****************************************************************************/ + +/* ========================================================================= */ +PathIterator::PathIterator + (unsigned long int propositions_per_state, + StateSpace::size_type number_of_states) : + path(propositions_per_state, number_of_states), loop_target_state(0) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class PathIterator. Creates a new object for + * enumerating all paths with a given number of states and + * atomic propositions. + * + * Arguments: propositions_per_state -- Number of atomic propositions + * per a state in the path. + * number_of_states -- Number of states in the path. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + StateSpace::size_type state; + + for (state = 0; (state + 1) < number_of_states; state++) + path.connect(state, state + 1); + + path.connect(state, 0); +} + +/* ========================================================================= */ +bool PathIterator::operator==(const PathIterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: Equivalence operator for class PathIterator. Two + * PathIterators are equivalent if and only if all of the + * following conditions hold: + * (1) Both iterators point to paths of equal size with the + * same number of atomic propositions in each state. + * (2) Starting from the initial states of the paths pointed + * to by the individual iterators, the paths agree on + * the truth values of the atomic propositions in the + * corresponding states of the paths. + * (3) Both paths contain a cycle of an equal length. + * + * Argument: it -- A constant reference to a PathIterator. + * + * Returns: A truth value according to the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + if (loop_target_state != it.loop_target_state + || path.size() != it.path.size() + || path.numberOfPropositions() != it.path.numberOfPropositions()) + return false; + + StateSpace::size_type state; + + for (state = 0; + state < path.size() + && path[state].positiveAtoms().equal(it.path[state].positiveAtoms(), + path.numberOfPropositions()); + state++) + ; + + return (state == path.size()); +} + +/* ========================================================================= */ +bool PathIterator::operator!=(const PathIterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: Inequivalence operator for class PathIterator. See above for + * the definition of equivalence between two PathIterators; two + * PathIterators are inequal if and only if they are not equal. + * + * Argument: it -- A constant reference to a PathIterator. + * + * Returns: A truth value according to the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + if (loop_target_state != it.loop_target_state + || path.size() != it.path.size() + || path.numberOfPropositions() != it.path.numberOfPropositions()) + return true; + + StateSpace::size_type state; + + for (state = 0; + state < path.size() + && path[state].positiveAtoms().equal(it.path[state].positiveAtoms(), + path.numberOfPropositions()); + state++) + ; + + return (state != path.size()); +} + +/* ========================================================================= */ +void PathIterator::computeNextPath() +/* ---------------------------------------------------------------------------- + * + * Description: Updates the path pointed to by the PathIterator to the + * `next' path in the sequence. The sequence is constructed by + * identifying the sequence as a binary integer (obtained by + * concatenating the truth valuations for the atomic + * propositions in each state) which is then incremented. If + * there is an overflow, the `loop state' is changed until it + * exceeds the length of the path. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (atEnd()) + return; + + StateSpace::size_type state; + const unsigned long int number_of_propositions(path.numberOfPropositions()); + + /* + * Find the first state in the current path where some proposition has the + * value `false'. Change the truth value of all propositions to `false' + * in all states preceding this state. + */ + + for (state = 0; + state < path.size() + && path[state].positiveAtoms().count(number_of_propositions) + == number_of_propositions; + state++) + path[state].positiveAtoms().clear(number_of_propositions); + + if (state == path.size()) + { + /* + * If the path did not contain a state in which some proposition had the + * value `false', update the `loop state' in the path. + */ + + path.disconnect(path.size() - 1, loop_target_state); + + loop_target_state++; + + if (loop_target_state < path.size()) + path.connect(path.size() - 1, loop_target_state); + } + else + { + /* + * In other case, change the truth value of the proposition to `true' in + * the state and reset the truth values of all propositions with smaller + * identifiers to `false'. + */ + + BitArray& truth_assignment = path[state].positiveAtoms(); + unsigned long int proposition; + + for (proposition = 0; truth_assignment[proposition]; proposition++) + truth_assignment.clearBit(proposition); + + truth_assignment.setBit(proposition); + } +} + +} diff --git a/lbtt/src/PathIterator.h b/lbtt/src/PathIterator.h new file mode 100644 index 000000000..8c6f914ca --- /dev/null +++ b/lbtt/src/PathIterator.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef PATHITERATOR_H +#define PATHITERATOR_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include "StateSpace.h" + +using namespace std; + +namespace Graph +{ + +/****************************************************************************** + * + * An iterator class for systematically enumerating all state spaces consisting + * of a single infinite path (a prefix and a loop) with a given number of + * states and a given number of atomic propositions in each state. + * + *****************************************************************************/ + +class PathIterator +{ +public: + PathIterator /* Constructor. */ + (unsigned long int propositions_per_state, + StateSpace::size_type number_of_states); + + /* default copy constructor */ + + ~PathIterator(); /* Destructor. */ + + /* default assignment operator */ + + bool operator==(const PathIterator& it) /* Equivalence operator. */ + const; + + bool operator!=(const PathIterator& it) /* Inequivalence operator. + */ + const; + + const StateSpace& operator*() const; /* Dereferencing */ + const StateSpace* operator->() const; /* operators. */ + + const StateSpace& operator++(); /* Increment operators. */ + const StateSpace operator++(int); + + bool atEnd() const; /* Tells whether the + * iterator has enumerated + * all the state spaces in + * the range determined by + * the parameters with + * which the iterator was + * initialized. + */ + +private: + void computeNextPath(); /* Updates the state space + * currently pointed to by + * the iterator. + */ + + StateSpace path; /* The state space (the + * path) currently pointed + * to by the iterator. + */ + + StateSpace::size_type loop_target_state; /* Identifier of the target + * state of the last state + * in the path. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class PathIterator. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline PathIterator::~PathIterator() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class PathIterator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline const StateSpace& PathIterator::operator*() const +/* ---------------------------------------------------------------------------- + * + * Description: Dereferencing operator for class PathIterator. Gives access + * to the state space currently pointed to by the iterator. + * + * Arguments: None. + * + * Returns: A constant reference to the state space currently pointed to + * by the iterator. + * + * ------------------------------------------------------------------------- */ +{ + return path; +} + +/* ========================================================================= */ +inline const StateSpace* PathIterator::operator->() const +/* ---------------------------------------------------------------------------- + * + * Description: Dereferencing operator for class PathIterator. Gives access + * to the state space currently pointed to by the iterator. + * + * Arguments: None. + * + * Returns: A pointer to a constant state space currently pointed to by + * the iterator. + * + * ------------------------------------------------------------------------- */ +{ + return &path; +} + +/* ========================================================================= */ +inline const StateSpace& PathIterator::operator++() +/* ---------------------------------------------------------------------------- + * + * Description: Prefix increment operator for class PathIterator. Computes + * the next path in the graph sequence and returns a constant + * reference to it. + * + * Arguments: None. + * + * Returns: A constant reference to the updated state space pointer to by + * the iterator. + * + * ------------------------------------------------------------------------- */ +{ + computeNextPath(); + return path; +} + +/* ========================================================================= */ +inline const StateSpace PathIterator::operator++(int) +/* ---------------------------------------------------------------------------- + * + * Description: Postfix increment operator for class PathIterator. Computes + * the next path in the graph sequence and returns the graph + * pointed to by the iterator after this operation. + * + * Arguments: None. + * + * Returns: A constant reference to the state space pointed to by the + * iterator before computing the next path in the sequence. + * + * ------------------------------------------------------------------------- */ +{ + StateSpace old_path(path); + computeNextPath(); + return old_path; +} + +/* ========================================================================= */ +inline bool PathIterator::atEnd() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether all possible paths have been enumerated by the + * iterator. + * + * Arguments: None. + * + * Returns: A truth value according to the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + return (loop_target_state == path.size()); +} + +} + +#endif /* !PATHITERATOR_H */ diff --git a/lbtt/src/ProductAutomaton.cc b/lbtt/src/ProductAutomaton.cc new file mode 100644 index 000000000..c0513404a --- /dev/null +++ b/lbtt/src/ProductAutomaton.cc @@ -0,0 +1,1061 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include "ProductAutomaton.h" +#include "SccIterator.h" + +namespace Graph +{ + +/****************************************************************************** + * + * Function definitions for class ProductAutomaton. + * + *****************************************************************************/ + +/* ========================================================================= */ +void ProductAutomaton::clear() +/* ---------------------------------------------------------------------------- + * + * Description: Makes the automaton empty. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + buchi_automaton = 0; + statespace_size = 0; + +#ifdef HAVE_OBSTACK_H + for (vector::iterator state = nodes.begin(); + state != nodes.end(); + ++state) + static_cast(*state)->~ProductState(); + + if (!nodes.empty()) + { + store.free(*nodes.begin()); + nodes.clear(); + nodes.reserve(0); + } + +#endif /* HAVE_OBSTACK_H */ + + Graph::clear(); +} + +/* ========================================================================= */ +ProductAutomaton::size_type ProductAutomaton::expand(size_type node_count) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts states to a ProductAutomaton. + * + * Argument: node_count -- Number of states to be inserted. + * + * Returns: The index of the last state inserted. + * + * ------------------------------------------------------------------------- */ +{ + nodes.reserve(nodes.size() + node_count); + + while (node_count > 0) + { +#ifdef HAVE_OBSTACK_H + void* state_storage = store.alloc(sizeof(ProductState)); + ProductState* new_product_state = new(state_storage) ProductState(); +#else + ProductState* new_product_state = new ProductState(); +#endif /* HAVE_OBSTACK_H */ + + try + { + nodes.push_back(new_product_state); + } + catch (...) + { +#ifdef HAVE_OBSTACK_H + new_product_state->~ProductState(); + store.free(state_storage); +#else + delete new_product_state; +#endif /* HAVE_OBSTACK_H */ + throw; + } + node_count--; + } + + return size() - 1; +} + +/* ========================================================================= */ +void ProductAutomaton::connect(const size_type father, const size_type child) +/* ---------------------------------------------------------------------------- + * + * Description: Connects two states of the product automaton. + * + * Arguments: father, child -- State identifiers. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Edge* edge = operator[](child).incoming_edge; + + if (edge != 0) + { + nodes[father]->outgoing_edges.insert(edge); + return; + } + +#ifdef HAVE_OBSTACK_H + void* edge_storage = store.alloc(sizeof(Edge)); + edge = new(edge_storage) Edge(child); +#else + edge = new Edge(child); +#endif /* HAVE_OBSTACK_H */ + + try + { + nodes[father]->outgoing_edges.insert(edge); + } + catch (...) + { +#ifdef HAVE_OBSTACK_H + edge->~Edge(); + store.free(edge_storage); +#else + delete edge; +#endif /* HAVE_OBSTACK_H */ + throw; + } + + operator[](child).incoming_edge = edge; +} + +/* ========================================================================= */ +void ProductAutomaton::disconnect + (const size_type father, const size_type child) +/* ---------------------------------------------------------------------------- + * + * Description: Disconnects two states of the product automaton. + * + * Arguments: father, child -- Identifiers for two states. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Edge e(child); + + /* + * Scan the set of `father''s outgoing transitions for a transition to the + * given target state and remove it if such a transition exists. + */ + + GraphEdgeContainer::iterator search_edge + = nodes[father]->outgoing_edges.find(&e); + + if (search_edge != nodes[father]->outgoing_edges.end()) + nodes[father]->outgoing_edges.erase(search_edge); +} + +/* ========================================================================= */ +void ProductAutomaton::print + (ostream& stream, const int indent, const GraphOutputFormat) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a product automaton to a stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * + * The third (dummy) parameter is needed to support the + * function interface defined in the base class. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + if (nodes.empty()) + estream << string(indent, ' ') + "The product automaton is empty.\n"; + else + { + pair statistics = stats(); + bool first_printed; + + estream << string(indent, ' ') + "The product automaton consists of\n" + + string(indent + 4, ' ') + << statistics.first + << " states and\n" + string(indent + 4, ' ') + << statistics.second + << " transitions.\n"; + + size_type s = nodes.size(); + for (size_type state = 0; state < s; ++state) + { + estream << string(indent, ' ') + "State " << state << ":\n" + + string(indent + 4, ' ') + "Automaton state: " + << buchiState(state) + << " (acceptance sets: {"; + + for (unsigned long int acceptance_set = 0; + acceptance_set < buchi_automaton->numberOfAcceptanceSets(); + acceptance_set++) + { + if ((*buchi_automaton)[buchiState(state)].acceptanceSets(). + test(acceptance_set)) + { + if (first_printed) + estream << ", "; + else + first_printed = true; + estream << acceptance_set; + } + } + + estream << "}\n" + string(indent + 4, ' ') + "System state: " + << systemState(state) + << '\n'; + + operator[](state).print(stream, indent + 4); + } + } + + estream.flush(); +} + +/* ========================================================================= */ +void ProductAutomaton::computeProduct + (const BuchiAutomaton& automaton, const StateSpace& statespace, + const bool global_product) +/* ---------------------------------------------------------------------------- + * + * Description: Initializes the synchronous product of a Büchi automaton and + * a state space. + * + * Arguments: automaton -- A reference to a constant BuchiAutomaton. + * statespace -- A reference to a constant StateSpace. + * global_product -- Controls whether the synchronous product + * of the automaton and the statespace is + * computed `globally' (i.e., with respect + * to all states in the state space) or + * `locally' (i.e., with respect only to the + * initial state of the state space). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + clear(); + + buchi_automaton = &automaton; + statespace_size = statespace.size(); + + /* + * If either the Büchi automaton or the state space is empty, their product + * is also empty. + */ + + if (automaton.empty() || statespace.empty()) + return; + + /* + * If the (worst-case) product of the size of the automaton and the state + * space size exceeds the maximum size of a product automaton, throw an + * exception (if this holds, the simple product state hashing technique + * used below will not work correctly). + */ + + if (automaton.size() > (nodes.max_size() / statespace.size())) + throw ProductSizeException(); + + /* + * Product states will be numerically encoded using the equation + * + * #P = #S + |S| * #B + * + * where + * |S| is the number of states in the state space, + * #P is an identifier of a product state, + * #S is an identifier of a state in the state space (0...|S|-1), + * #B is an identifier of a state in the Büchi automaton (0...|B|-1) + * and + * + * From this encoding we obtain + * #S = #P % |S|, and + * #B = #P / |S|. + */ + + map, ALLOC(size_type) > + product_state_mapping; + + /* + * Initialize the product automaton. If the product is to be computed + * globally, initialize the result with state pairs (q_0, s) where q_0 is + * the initial state of the Büchi automaton and s goes through all states + * in the state space. (These states will have the lowest indices in the + * product automaton, i.e. covering the index interval + * 0 ... [statespace.size() - 1] .) In the case of a local product, + * initialize the result with the single state (q_0, s_0), a pair consisting + * of the initial states of the two other structures. + * + * The final product automaton is obtained by computing the closure of this + * set of states under the product transition relation. For this purpose, + * a depth-first search will be used (with `mapping_stack' as the search + * stack; initially, it contains the same states as described above). + */ + + expand(global_product ? statespace.size() : 1); + + stack > mapping_stack; + + pair state_map_entry + = make_pair(automaton.initialState() * statespace_size, 0); + + for (state_map_entry.second = 0; + state_map_entry.second < (global_product ? statespace_size : 1); + ++state_map_entry.first, ++state_map_entry.second) + { + product_state_mapping.insert(state_map_entry); + mapping_stack.push(state_map_entry.first); + + operator[](state_map_entry.second).hashValue() = state_map_entry.first; + } + + /* + * Compute the product automaton by using a depth-first search. + */ + + const GraphEdgeContainer* automaton_transitions; + const GraphEdgeContainer* system_transitions; + + BuchiAutomaton::size_type current_automaton_state; + StateSpace::size_type current_system_state; + size_type current_product_state; + bool current_product_state_valid; + size_type current_mapping; + + try + { + while (!mapping_stack.empty()) + { + if (::user_break) + throw UserBreakException(); + + /* + * Pop a state mapping off the stack. + */ + + current_product_state_valid = false; + current_mapping = mapping_stack.top(); + mapping_stack.pop(); + + current_automaton_state = current_mapping / statespace_size; + current_system_state = current_mapping % statespace_size; + + /* + * Go through the transitions of the original Büchi automaton. For all + * transitions enabled in the current state of the state space: + * + * - Compute all product states that can be reached by firing the + * transition. These are the states (q', s') where q' is the + * state of the automaton after firing the transition and s' + * is a successor of the current state of the system. + * + * - If there is no corresponding product state for the state pair + * (q', s'), create a new product state and insert it into the + * product automaton. Push the mapping also on the stack so that + * the newly created product state will be eventually processed. + * + * - Connect the target product state to the current product state + * (in effect, storing information only about the _predecessors_ of + * each product state). This will result in a product space in + * which all edges are reversed. However, this is all that is + * needed since no information is needed about the successors of + * any product state when searching for the accepting cycles in the + * product graph (this will be performed using a backward search, + * see the functions `emptinessCheck' and + * `findAcceptingExecution'). + */ + + automaton_transitions = &automaton[current_automaton_state].edges(); + + for (GraphEdgeContainer::const_iterator + automaton_transition = automaton_transitions->begin(); + automaton_transition != automaton_transitions->end(); + ++automaton_transition) + { + if ((static_cast + (*automaton_transition))-> + enabled(statespace[current_system_state].positiveAtoms(), + statespace.numberOfPropositions())) + { + if (!current_product_state_valid) + { + current_product_state = product_state_mapping[current_mapping]; + current_product_state_valid = true; + system_transitions = &statespace[current_system_state].edges(); + } + + pair, ALLOC(size_type) > + ::iterator, + bool> + check_state; + + for (GraphEdgeContainer::const_iterator + system_transition = system_transitions->begin(); + system_transition != system_transitions->end(); + ++system_transition) + { + /* + * Compute a hash value for the target product state and test + * whether it has already been included in the product. + */ + + state_map_entry.first = + (*system_transition)->targetNode() + + statespace_size * (*automaton_transition)->targetNode(); + + check_state = product_state_mapping.insert(state_map_entry); + + if (check_state.second) /* insertion occurred */ + { + /* + * Create a new product state and adjust its hash value. + * `state_map_entry.second' holds the next free identifier for a + * new product state. + */ + + expand(); + + operator[](state_map_entry.second).hashValue() + = state_map_entry.first; + + connect(state_map_entry.second, current_product_state); + state_map_entry.second++; + + mapping_stack.push(state_map_entry.first); + } + else + { + size_type existing_state = (check_state.first)->second; + connect(existing_state, current_product_state); + } + } + } + } + } + } + catch (...) + { + clear(); + throw; + } +} + +/* ========================================================================= */ +void ProductAutomaton::emptinessCheck(Bitset& result) const +/* ---------------------------------------------------------------------------- + * + * Description: Performs an emptiness check on the product automaton, i.e. + * finds the set of system states starting an execution + * sequence accepted by the Büchi automaton. + * + * Argument: result -- A reference to the Bitset in which the result + * is stored. The Bitset must have enough space for + * as many states as there are in the state space. + * A `0' bit in some position n in the result means + * that no accepting executions begin from system + * state n; a `1' bit means the opposite. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + BitArray visited(nodes.size()); + visited.clear(nodes.size()); + + result.clear(); + + /* + * Scan the maximal strongly connected components of the product space to + * find any fair MSCCs (nontrivial MSCCs containing an accepting cycle of + * the Büchi automaton used for constructing the product). Note that + * although the product space contains only back edges (i.e., we are + * actually scanning the product space with all edges reversed), the + * reversal of the edges does not affect the MSCCs of the product space. + */ + + for (SccIterator + strongly_connected_component(*this); + !strongly_connected_component.atEnd(); + ++strongly_connected_component) + { + if (::user_break) + throw UserBreakException(); + + if (strongly_connected_component->fair(*this)) + { + /* + * Search for a previously unvisited state in the strongly connected + * component. + */ + + ProductScc::const_iterator st; + for (st = strongly_connected_component->begin(); + st != strongly_connected_component->end() && visited[*st]; + ++st) + ; + + if (st != strongly_connected_component->end()) + { + /* + * Starting from the unvisited state, perform a backward depth-first + * search to find all states (q_0, s) such that q_0 is the initial + * state of the automaton (the product states (q_0, s) are recognized + * by the product state numbering scheme, in which these states have + * the lowest indices). Add the corresponding system states to the + * result (these are the system states from which the nontrivial MSCC + * containing the accepting cycle can be reached). + */ + + size_type state = *st; + stack > + backward_search_stack; + const GraphEdgeContainer* predecessors; + + visited.setBit(state); + backward_search_stack.push(state); + + while (!backward_search_stack.empty()) + { + state = backward_search_stack.top(); + backward_search_stack.pop(); + + if (state < result.capacity() && state < statespace_size) + result.setBit(systemState(state)); + + predecessors = &(operator[](state).edges()); /* note that only back + * edges are stored in + * the product! */ + + for (GraphEdgeContainer::const_iterator predecessor + = predecessors->begin(); + predecessor != predecessors->end(); + ++predecessor) + { + if (!visited[(*predecessor)->targetNode()]) + { + backward_search_stack.push((*predecessor)->targetNode()); + visited.setBit((*predecessor)->targetNode()); + } + } + } + } + } + } +} + +/* ========================================================================= */ +void ProductAutomaton::findAcceptingExecution + (const StateSpace::size_type initial_state, + pair, + deque >& execution) const +/* ---------------------------------------------------------------------------- + * + * Description: Extracts an execution (beginning from a given system + * state) accepted by the Büchi automaton from the product + * space. The function behaves basically like + * ProductAutomaton::emptinessCheck, but it keeps additional + * information about the path of processed states during the + * backward search. This information is then used to extract + * the desired system execution from the graph. + * + * Arguments: initial_state -- Identifier of the system state. + * execution -- A reference to a pair of deques for + * storing the result. The `first' component + * of the pair represents an execution + * `prefix' (a sequence of + * + * pairs) leading from `initial_state' + * to an accepting cycle. The `second' + * component of the pair contains the cycle + * itself. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + BitArray visited(nodes.size()); + visited.clear(nodes.size()); + + deque& prefix = execution.first; + deque& cycle = execution.second; + + prefix.clear(); + cycle.clear(); + + /* + * Scan the non-trivial maximal strongly connected components of the + * product space to find the fair MSCCs (non-trivial MSCCs containing an + * accepting cycle of the Büchi automaton used for constructing the + * product). Note that although the product space contains only back edges + * (i.e., we are actually scanning the product space with all edges + * reversed), the reversal of the edges does not affect the MSCCs of the + * product space. + */ + + for (SccIterator nmscc(*this); + !nmscc.atEnd(); + ++nmscc) + { + if (nmscc->fair(*this)) + { + const unsigned long int num_accept_sets + = buchi_automaton->numberOfAcceptanceSets(); + + /* + * Search the fair non-trivial maximal strongly connected component for + * a state belonging to some acceptance set of the Büchi automaton from + * which the product was constructed (or if the automaton has no + * acceptance sets, any state in the non-trivial MSCC). + */ + + ProductScc::const_iterator st = nmscc->begin(); + + if (buchi_automaton->numberOfAcceptanceSets() > 0) + { + while (st != nmscc->end()) + { + if ((*buchi_automaton)[buchiState(*st)].acceptanceSets(). + find(num_accept_sets) + < num_accept_sets) + break; + + ++st; + } + } + + if (st != nmscc->end()) + { + /* + * Try to find a (backward) path from the state in the MSCC back to + * the caller-given `initial state'. + */ + + size_type search_start_state = *st; + + if (search_start_state != initial_state) + { + typedef pair + BackwardSearchStackElement; + + deque + backward_search_stack; + + visited.clear(nodes.size()); + visited.setBit(search_start_state); + backward_search_stack.push_back + (make_pair(search_start_state, + operator[](search_start_state).edges().begin())); + + size_type predecessor; + + while (!backward_search_stack.empty()) + { + /* + * If it may be possible to find a shorter path to the initial + * state by still extending the current path (or if no path to + * the initial state has yet been found), scan through the + * predecessors of the `current' state (the last state inserted + * onto `backward_search_stack'). + */ + + while ((backward_search_stack.size() < prefix.size() + || prefix.empty()) + && backward_search_stack.back().second + != operator[](backward_search_stack.back().first) + .edges().end()) + { + predecessor + = (*backward_search_stack.back().second)->targetNode(); + + backward_search_stack.back().second++; + + /* + * If the given `initial state' is a predecessor of the current + * state, extract the path from the initial state to the state + * from where the backward search was started. + */ + + if (buchiState(predecessor) == buchi_automaton->initialState() + && systemState(predecessor) == initial_state) + { + prefix.clear(); + for (deque + ::const_iterator + state = backward_search_stack.begin(); + state != backward_search_stack.end(); + ++state) + { + prefix.push_front(make_pair(buchiState((*state).first), + systemState((*state).first))); + } + prefix.push_front(make_pair(buchiState(predecessor), + systemState(predecessor))); + } + + /* + * If some predecessor of the current state has not yet been + * visited, push it onto the backward search stack, then proceed + * with checking its predecessors. + */ + + else if (!visited[predecessor]) + { + visited.setBit(predecessor); + backward_search_stack.push_back + (make_pair(predecessor, + operator[](predecessor).edges().begin())); + } + } + + /* + * If all predecessors of a state have been processed, backtrack + * to the previous state on the path. + */ + + backward_search_stack.pop_back(); + } + } + + /* + * If a path was found from the `initial state' to the state in the + * MSCC, construct an accepting cycle by performing a breadth-first + * search in the MSCC. + */ + + if (!prefix.empty() || search_start_state == initial_state) + { + BitArray in_nmscc(nodes.size()); + in_nmscc.clear(nodes.size()); + + for (ProductScc::const_iterator state = nmscc->begin(); + state != nmscc->end(); + ++state) + in_nmscc.setBit(*state); + + BitArray collected_acc_sets; + collected_acc_sets.copy + ((*buchi_automaton)[buchiState(search_start_state)] + .acceptanceSets(), + num_accept_sets); + bool all_acceptance_sets_on_path + = (collected_acc_sets.count(num_accept_sets) == num_accept_sets); + + deque backward_search_queue; + map, ALLOC(size_type) > + shortest_path_predecessor; + + size_type bfs_root = search_start_state; + const GraphEdgeContainer* predecessors; + size_type state; + + visited.clear(nodes.size()); + visited.setBit(bfs_root); + backward_search_queue.push_back(bfs_root); + + bool cycle_finished = false; + + while (!cycle_finished) + { + predecessors = &operator[](backward_search_queue.front()).edges(); + + for (GraphEdgeContainer::const_iterator + predecessor = predecessors->begin(); + predecessor != predecessors->end(); + ++predecessor) + { + state = (*predecessor)->targetNode(); + + if (in_nmscc[state]) + { + /* + * If all acceptance sets have been collected and the search + * finds the first state of the cycle again, the cycle is + * complete. + */ + + if (all_acceptance_sets_on_path && state == search_start_state) + { + cycle_finished = true; + state = backward_search_queue.front(); + break; + } + else if (!visited[state]) + { + /* + * Update information about the breadth-first predecessor of + * an unvisited state. + */ + + shortest_path_predecessor[state] + = backward_search_queue.front(); + + /* + * If the unvisited state does not cover any `new' + * acceptance conditions, prepare to continue the search in + * that state by inserting the state into the search queue. + */ + + if (all_acceptance_sets_on_path + || (*buchi_automaton)[buchiState(state)].acceptanceSets() + .subset(collected_acc_sets, num_accept_sets)) + { + visited.setBit(state); + backward_search_queue.push_back(state); + } + + /* + * If the search finds an unvisited state which covers new + * acceptance sets, begin a new breadth-first search in + * that state. + */ + + else + { + all_acceptance_sets_on_path = true; + + for (unsigned long int accept_set = 0; + accept_set + < buchi_automaton->numberOfAcceptanceSets(); + accept_set++) + { + if ((*buchi_automaton)[buchiState(state)] + .acceptanceSets().test(accept_set)) + collected_acc_sets.setBit(accept_set); + else if (!collected_acc_sets.test(accept_set)) + all_acceptance_sets_on_path = false; + } + + while (state != bfs_root) + { + cycle.push_back(make_pair(buchiState(state), + systemState(state))); + state = shortest_path_predecessor[state]; + } + + bfs_root = (*predecessor)->targetNode(); + visited.clear(nodes.size()); + visited.setBit(bfs_root); + backward_search_queue.clear(); + backward_search_queue.push_back(bfs_root); + backward_search_queue.push_back(bfs_root); + + break; + } + } + } + } + + backward_search_queue.pop_front(); + } + + while (state != bfs_root) + { + cycle.push_back(make_pair(buchiState(state), + systemState(state))); + state = shortest_path_predecessor[state]; + } + + cycle.push_back(make_pair(buchiState(search_start_state), + systemState(search_start_state))); + + /* + * "Synchronize" the prefix of the witness execution with its cycle + * by removing from the end of the prefix the longest subsequence of + * states which occurs in the end of the cycle. The states in the + * cycle must be "rotated" accordingly to align the first state of + * the cycle correctly. + */ + + while (!prefix.empty() && prefix.back() == cycle.back()) + { + cycle.push_front(cycle.back()); + prefix.pop_back(); + cycle.pop_back(); + } + + return; + } + } + } + } + + /* + * The result will be empty if no accepting execution beginning from the + * given initial state could be found. + */ +} + + + +/****************************************************************************** + * + * Function definitions for class ProductAutomaton::ProductState. + * + *****************************************************************************/ + +/* ========================================================================= */ +void ProductAutomaton::ProductState::print + (ostream& stream, const int indent, const GraphOutputFormat) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about the ProductState to a stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * + * The third (dummy) argument is needed to support the function + * interface defined in the base class. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + if (edges().empty()) + estream << string(indent,' ') + "The product state has no predecessors.\n"; + else + { + bool first_printed = false; + estream << string(indent, ' ') + "Predecessor states: {"; + + for (GraphEdgeContainer::const_iterator predecessor = edges().begin(); + predecessor != edges().end(); ++predecessor) + { + if (first_printed) + estream << ", "; + else + first_printed = true; + estream << (*predecessor)->targetNode(); + } + estream << "}\n"; + } + + estream.flush(); +} + + + +/****************************************************************************** + * + * Function definitions for class ProductAutomaton::ProductScc. + * + *****************************************************************************/ + +/* ========================================================================= */ +bool ProductAutomaton::ProductScc::fair + (const ProductAutomaton& product_automaton) const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether a strongly connected component is fair in + * a product automaton. A strongly connected component is + * fair if and only if it is nontrivial (i.e. empty or + * containing a single state with a self-loop) and contains a + * state corresponding to a state from every acceptance set of + * the Büchi automaton used for constructing the given product. + * + * Arguments: product_automaton -- A constant reference to a + * ProductAutomaton. + * + * Returns: A truth value telling whether the strongly connected + * component is fair. + * + * ------------------------------------------------------------------------- */ +{ + /* + * A maximal strongly connected component is not fair if it is trivial. + */ + + if (empty() + || (size() == 1 && !product_automaton.connected(*begin(), *begin()))) + return false; + + /* + * Check whether the strongly connected component contains a state from each + * acceptance set of the Büchi automaton used for constructing the product + * (in this case, the component contains an accepting cycle of the + * automaton and is therefore fair). + */ + + const BuchiAutomaton* buchi_automaton = product_automaton.buchi_automaton; + const BitArray* acceptance_sets; + const unsigned long int number_of_acceptance_sets + = buchi_automaton->numberOfAcceptanceSets(); + + BitArray acceptance_sets_in_scc(number_of_acceptance_sets); + acceptance_sets_in_scc.clear(number_of_acceptance_sets); + + unsigned long int accept_set; + unsigned long int acceptance_set_counter = 0; + + for (const_iterator st = begin(); + st != end() && acceptance_set_counter < number_of_acceptance_sets; + ++st) + { + acceptance_sets = &(*buchi_automaton)[product_automaton.buchiState(*st)]. + acceptanceSets(); + + accept_set = acceptance_set_counter; + while (accept_set < number_of_acceptance_sets) + { + if (acceptance_sets->test(accept_set)) + { + acceptance_sets_in_scc.setBit(accept_set); + if (accept_set == acceptance_set_counter) + { + do + acceptance_set_counter++; + while (acceptance_set_counter < number_of_acceptance_sets + && acceptance_sets_in_scc[acceptance_set_counter]); + accept_set = acceptance_set_counter; + continue; + } + } + + accept_set++; + } + } + + return (acceptance_set_counter == number_of_acceptance_sets); +} + +} diff --git a/lbtt/src/ProductAutomaton.h b/lbtt/src/ProductAutomaton.h new file mode 100644 index 000000000..ccd567468 --- /dev/null +++ b/lbtt/src/ProductAutomaton.h @@ -0,0 +1,600 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef PRODUCTAUTOMATON_H +#define PRODUCTAUTOMATON_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include "Alloc.h" +#include "BitArray.h" +#include "BuchiAutomaton.h" +#include "EdgeContainer.h" +#include "Exception.h" +#include "Graph.h" +#include "StateSpace.h" + +using namespace std; + +extern bool user_break; + +namespace UserCommands +{ + extern void printAutomatonAnalysisResults + (ostream&, int, unsigned long int, unsigned long int); +} + +namespace Graph +{ + +/****************************************************************************** + * + * A class for representing the synchronous product of a Büchi automaton and + * a state space. + * + *****************************************************************************/ + +class ProductAutomaton : public Graph +{ +private: + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class ProductState : /* A class for */ + public Graph::Node /* representing the */ + { /* states of the product + * automaton. + */ + public: + explicit ProductState /* Constructor. */ + (const size_type hash_val = 0); + + ~ProductState(); /* Destructor. */ + + /* `edges' inherited from Graph::Node */ + + size_type hashValue() const; /* Get or set the hash */ + size_type& hashValue(); /* value for the product + * state (this value can + * be used to extract + * the identifiers of + * the original state + * space and the Büchi + * automaton with which + * the product state is + * associated). + */ + + void print /* Writes information */ + (ostream& stream = cout, /* about the product */ + const int indent = 0, /* state to a stream. */ + const GraphOutputFormat fmt = NORMAL) const; + + private: + friend class ProductAutomaton; + + ProductState(const ProductState&); /* Prevent copying and */ + ProductState& operator=(const ProductState&); /* assignment of + * ProductState objects. + */ + + size_type hash_value; /* Hash value for the + * product state (can be + * used to extract the + * identifiers of the + * original state space and + * the Büchi automaton with + * which the product state + * is associated). + */ + + Edge* incoming_edge; /* The unique edge pointing + * to `this' ProductState. + */ + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class ProductScc : /* A class for storing */ + public vector /* connected components */ + { /* of the product. + */ + public: + ProductScc(); /* Constructor. */ + + /* default copy constructor */ + + ~ProductScc(); /* Destructor. */ + + /* default assignment operator */ + + bool fair /* Tests whether the */ + (const ProductAutomaton& product_automaton) /* component is fair, */ + const; /* i.e. it is a + * nontrivial component + * with a state from + * each acceptance set + * of the Büchi + * automaton used for + * constructing a + * given product. + */ + + void insert /* Inserts a state into */ + (const ProductAutomaton::size_type /* the container. */ + product_state); + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +public: + class ProductSizeException; /* An exception class for + * reporting the situation + * where the size of the + * product automaton may + * be too big. + */ + + friend class ProductScc; + friend void UserCommands::printAutomatonAnalysisResults + (ostream&, int, unsigned long int, unsigned long int); + + ProductAutomaton(); /* Constructor. */ + + ~ProductAutomaton(); /* Destructor. */ + + ProductState& operator[](const size_type index) /* Indexing operator. No */ + const; /* range check is performed + * on the argument. + */ + + ProductState& node(const size_type index) const; /* Synonym for the indexing + * operator. This function + * also checks the range of + * the argument. + */ + + /* `size' inherited from Graph */ + + /* `empty' inherited from Graph */ + + void clear(); /* Makes the automaton + * empty. + */ + + void connect /* Connects two states */ + (const size_type father, /* of the product */ + const size_type child); /* automaton. */ + + void disconnect /* Disconnects two */ + (const size_type father, /* states of the product */ + const size_type child); /* automaton. */ + + /* `connected' inherited from Graph */ + + /* `stats' inherited from Graph */ + + /* `subgraphStats' inherited from Graph */ + + void computeProduct /* Function for */ + (const BuchiAutomaton& automaton, /* initializing the */ + const StateSpace& statespace, /* product automaton. */ + const bool global_product); + + StateSpace::size_type systemState /* Returns the */ + (const size_type state) const; /* identifier of the + * state of the original + * state space with + * which a given product + * state is associated. + */ + + BuchiAutomaton::size_type buchiState /* Returns the */ + (const size_type state) const; /* identifier of the + * state of the original + * automaton with which + * a given product state + * is associated. + */ + + void emptinessCheck(Bitset& result) const; /* Performs an emptiness + * check on the product. + */ + + void findAcceptingExecution /* Extracts an accepting */ + (const StateSpace::size_type initial_state, /* execution from the */ + pair, /* product automaton. */ + deque >& + execution) const; + + void print /* Writes information */ + (ostream& stream = cout, /* about the product */ + const int indent = 0, /* automaton to a */ + const GraphOutputFormat fmt = NORMAL) const; /* stream. */ + +private: + ProductAutomaton(const ProductAutomaton&); /* Prevent copying and */ + ProductAutomaton& operator= /* assignment of */ + (const ProductAutomaton&); /* ProductAutomaton + * objects. + */ + + size_type expand(size_type node_count = 1); /* Inserts states to the + * product automaton. + */ + + const BuchiAutomaton* buchi_automaton; /* A pointer to the + * Büchi automaton used for + * constructing the + * product. + */ + + StateSpace::size_type statespace_size; /* Size of the state space + * used for constructing + * the product automaton. + */ + +#ifdef HAVE_OBSTACK_H /* Storage for product */ + ObstackAllocator store; /* states and */ +#endif /* HAVE_OBSTACK_H */ /* transitions. */ +}; + + + +/****************************************************************************** + * + * An exception class for reporting the situation where the product may be too + * big to compute. + * + *****************************************************************************/ + +class ProductAutomaton::ProductSizeException : public Exception +{ +public: + ProductSizeException(); /* Constructor. */ + + /* default copy constructor */ + + ~ProductSizeException() throw(); /* Destructor. */ + + ProductSizeException& /* Assignment operator. */ + operator=(const ProductSizeException& e); + + /* `what' inherited from class Exception */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class ProductAutomaton. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline ProductAutomaton::ProductAutomaton() : + buchi_automaton(0), statespace_size(0) +#ifdef HAVE_OBSTACK_H +, store() +#endif /* HAVE_OBSTACK_H */ +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class ProductAutomaton. Initializes a + * new object for storing the product of a Büchi automaton and a + * state space. The product must then be explicitly initialized + * by calling the function `computeProduct' on the object. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline ProductAutomaton::~ProductAutomaton() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class ProductAutomaton. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + clear(); +} + +/* ========================================================================= */ +inline ProductAutomaton::ProductState& +ProductAutomaton::operator[](const size_type index) const +/* ---------------------------------------------------------------------------- + * + * Description: Indexing operator for class ProductAutomaton. Can be used to + * refer to the individual states of the product automaton. No + * range check will be performed on the argument. + * + * Argument: index -- Index of a state of the product automaton. + * + * Returns: A reference to the product state corresponding to the index. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast(*nodes[index]); +} + +/* ========================================================================= */ +inline ProductAutomaton::ProductState& +ProductAutomaton::node(const size_type index) const +/* ---------------------------------------------------------------------------- + * + * Description: Function for referring to a single state of a + * ProductAutomaton. This function will also check the range + * argument. + * + * Argument: index -- Index of a state of the product automaton. + * + * Returns: A reference to the corresponding product state. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast(Graph::node(index)); +} + +/* ========================================================================= */ +inline StateSpace::size_type ProductAutomaton::systemState + (const size_type state) const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the identifier of the system state with which a + * given product state is associated. This function will perform + * no range checks on its argument. + * + * Argument: state -- Identifier of a product state. + * + * Returns: Identifier of a state in a state space. + * + * ------------------------------------------------------------------------- */ +{ + return operator[](state).hashValue() % statespace_size; +} + +/* ========================================================================= */ +inline StateSpace::size_type ProductAutomaton::buchiState + (const size_type state) const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the identifier of the state of the Büchi automaton + * with which a given product state is associated. This function + * will perform no range checks on its argument. + * + * Argument: state -- Identifier of a product state. + * + * Returns: Identifier of a state in a Büchi automaton. + * + * ------------------------------------------------------------------------- */ +{ + return operator[](state).hashValue() / statespace_size; +} + + + +/****************************************************************************** + * + * Inline function definitions for class ProductAutomaton::ProductState. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline ProductAutomaton::ProductState::ProductState(const size_type hash_val) : + Graph::Node(), hash_value(hash_val), incoming_edge(0) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class ProductAutomaton::ProductState. Creates + * a new object representing a synchronous product of a state of + * a Büchi automaton with a state of a state space. + * + * Arguments: hash_val -- Hash value for the product state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline ProductAutomaton::ProductState::~ProductState() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class ProductAutomaton::ProductState. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (incoming_edge != 0) + { +#ifdef HAVE_OBSTACK_H + incoming_edge->~Edge(); +#else + delete incoming_edge; +#endif /* HAVE_OBSTACK_H */ + } + outgoing_edges.clear(); +} + +/* ========================================================================= */ +inline ProductAutomaton::size_type ProductAutomaton::ProductState::hashValue() + const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the product state's hash value by value. + * + * Arguments: None. + * + * Returns: The hash value of the product state. + * + * ------------------------------------------------------------------------- */ +{ + return hash_value; +} + +/* ========================================================================= */ +inline ProductAutomaton::size_type& ProductAutomaton::ProductState::hashValue() +/* ---------------------------------------------------------------------------- + * + * Description: Returns the product state's hash value by reference. (This + * function can therefore be used to change the value.) + * + * Arguments: None. + * + * Returns: A reference to the hash value of the product state. + * + * ------------------------------------------------------------------------- */ +{ + return hash_value; +} + + + +/****************************************************************************** + * + * Inline function definitions for class ProductAutomaton::ProductScc. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline ProductAutomaton::ProductScc::ProductScc() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class ProductAutomaton::ProductScc. Creates a + * new container for storing a maximal strongly connected + * component of a ProductAutomaton. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline ProductAutomaton::ProductScc::~ProductScc() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class ProductAutomaton::ProductScc. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline void ProductAutomaton::ProductScc::insert + (const ProductAutomaton::size_type product_state) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a new product state identifier to the container. + * + * Argument: product_state -- Identifier of a product state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + push_back(product_state); +} + + + +/****************************************************************************** + * + * Inline function definitions for class + * ProductAutomaton::ProductSizeException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline ProductAutomaton::ProductSizeException::ProductSizeException() : + Exception("product may be too large") +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class ProductAutomaton::ProductSizeException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline ProductAutomaton::ProductSizeException::~ProductSizeException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class ProductAutomaton::ProductSizeException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline ProductAutomaton::ProductSizeException& +ProductAutomaton::ProductSizeException::operator= + (const ProductAutomaton::ProductSizeException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class + * ProductAutomaton::ProductSizeException. Assigns the value of + * another ProductAutomaton::ProductSizeException to `this' one. + * + * Arguments: e -- A reference to a constant + * ProductAutomaton::ProductSizeException. + * + * Returns: A reference to the object whose value was changed. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + +} + +#endif /* !PRODUCTAUTOMATON_H */ diff --git a/lbtt/src/Random.h b/lbtt/src/Random.h new file mode 100644 index 000000000..2650599a5 --- /dev/null +++ b/lbtt/src/Random.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef RANDOM_H +#define RANDOM_H + +#include +#include + +#ifdef HAVE_RAND48 +#define rand lrand48 +#define srand srand48 +static const double MAXRAND = static_cast(1 << 30) * 2.0; +#else +static const double MAXRAND = RAND_MAX + 1.0; +#endif /* HAVE_RAND48 */ + + + +/****************************************************************************** + * + * Functions for random number generation. If HAVE_RAND48 is defined, the + * functions rely on the lrand48() function for generating a random integer + * between 0 and 2^31 and srand48() for setting the seed for the random + * number generator; otherwise, the rand() and srand() functions are used, + * respectively. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline void SRAND(unsigned int seed) +/* ---------------------------------------------------------------------------- + * + * Description: Initializes the random number generator with a seed value. + * + * Argument: seed -- Seed for the random number generator. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + srand (seed); +} + +/* ========================================================================= */ +inline double DRAND() +/* ---------------------------------------------------------------------------- + * + * Description: Generates a random double. + * + * Arguments: None. + * + * Returns: A random double in the half-open interval [0.0,1.0). + * + * ------------------------------------------------------------------------- */ +{ + return rand () / MAXRAND; +} + +/* ========================================================================= */ +inline long int LRAND(long int min, long int max) +/* ---------------------------------------------------------------------------- + * + * Description: Generates a random long integer in a given interval. + * + * Arguments: min, max -- Bounds for the interval. + * + * Returns: A random long integer in the half-open interval [min,max). + * + * ------------------------------------------------------------------------- */ +{ + return min + static_cast(DRAND() * (max - min)); +} + +#ifdef HAVE_RAND48 +#undef rand +#undef srand +#endif /* HAVE_RAND48 */ + +#endif /* !RANDOM_H */ diff --git a/lbtt/src/SccIterator.h b/lbtt/src/SccIterator.h new file mode 100644 index 000000000..838008c42 --- /dev/null +++ b/lbtt/src/SccIterator.h @@ -0,0 +1,752 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SCCITERATOR_H +#define SCCITERATOR_H + +#include +#include +#include +#include +#include +#include "Alloc.h" +#include "Graph.h" + +using namespace std; + +namespace Graph +{ + +/****************************************************************************** + * + * A template iterator class for computing the maximal strongly connected + * components of a graph represented as an object of class + * Graph. + * + * The iterator class has three template arguments: + * class EdgeContainer -- Container class storing the edges in the + * graph with which the iterator is associated. + * + * class SccContainer -- Container for storing the identifiers of the + * nodes belonging to some maximal strongly + * connected component. The container class + * must be able to store elements of type + * Graph::size_type. The container + * class interface must support the following + * operations: + * + * Default constructor which can be called + * without any arguments + * Copy constructor + * Assignment operator + * clear() + * [makes the container empty] + * insert(Graph::size_type s) + * [inserts an element into the + * container] + * + * If the container class is left unspecified, + * it defaults to + * set::size_type, + * less::size_type>, + * ALLOC(Graph::size_type)>. + * + * class Filter -- Class for representing function objects that + * can be used to restrict the iterator + * dereferencing operators to return only + * those nodes of a strongly connected component + * which satisfy a certain condition that can be + * tested using Filter::operator(). This function + * has to accept a single parameter of type + * Graph::Node*. It must return a + * Boolean value. The nodes for which the + * function returns `false' will then not be + * included in the collection of nodes returned + * by the iterator dereferencing operators. + * + * If the Filter class is left unspecified, it + * defaults to the NullSccFilter + * class, which does not restrict the set of + * nodes in any way. + * + *****************************************************************************/ + +template +class NullSccFilter; + +template::size_type, + less::size_type>, + ALLOC(typename Graph::size_type) >, + class Filter = NullSccFilter > +class SccIterator +{ +public: + SccIterator(const Graph& g); /* Constructor. */ + + /* default copy constructor */ + + ~SccIterator(); /* Destructor. */ + + /* default assignment operator */ + + bool operator== /* Equality test for */ + (const SccIterator& it) const; + + bool operator!= /* Inequality test for */ + (const SccIterator& it) const; + + bool operator< /* `Less than' relation */ + (const SccIterator& it) const; + + bool operator<= /* `Less than or equal' */ + (const SccIterator& it) const; + + bool operator> /* `Greater than' */ + (const SccIterator& it) const; + + bool operator>= /* `Greater than or */ + (const SccIterator& it) const; /* iterators. */ + + const SccContainer& operator*() const; /* Dereferencing */ + const SccContainer* operator->() const; /* operators. */ + + const SccContainer& operator++(); /* Prefix and postfix */ + const SccContainer operator++(int); /* increment operators. */ + + bool atEnd() const; /* Tests whether the + * iterator has scanned + * through all the + * strongly connected + * components of the + * graph. + */ + +private: + const Graph& graph; /* Reference to the graph + * with which the iterator + * is associated. + */ + + typename Graph::size_type /* Number of graph */ + dfs_number; /* nodes processed by + * the iterator. + */ + + vector::size_type, /* dfs_ordering[i] */ + ALLOC(typename Graph /* indicates the */ + ::size_type) > /* position of graph */ + dfs_ordering; /* node i in the depth- + * first search order. + * (If the node has not + * yet been visited, + * dfs_ordering[i]==0.) + */ + + vector::size_type, /* lowlink[i] indicates */ + ALLOC(typename Graph /* the least graph node */ + ::size_type) > /* (in the depth-first */ + lowlink; /* search order) that + * is reachable from + * graph node i and + * does not belong to + * any strongly + * connected component + * which has already been + * processed. + */ + + typedef pair::size_type, + typename EdgeContainer::const_iterator> + NodeStackElement; + + stack > + node_stack; + + typename Graph::size_type /* Current graph node */ + current_node; /* the depth-first + * search. + */ + + typename EdgeContainer::const_iterator edge; /* Iterator to scan + * through the successors + * of the current node. + */ + + stack::size_type, /* Stack used for */ + deque /* collecting the nodes */ + ::size_type, /* in a strongly */ + ALLOC(typename Graph /* connected component. */ + ::size_type) + > + > + scc_stack; + + SccContainer current_scc; /* Container of nodes + * forming the maximal + * strongly connected + * graph component + * currently `pointed to' + * by the iterator. + */ + + Filter cond; /* Function object for + * filtering out a subset + * of nodes in the + * strongly connected + * components. + */ + + void reset(); /* Initializes the + * iterator to point to + * the first strongly + * connected component of + * the graph. + */ + + void computeNextScc(); /* Updates the iterator to + * point to the next + * strongly connected + * component. + */ +}; + + + +/****************************************************************************** + * + * Default test for collecting the nodes in a strongly connected component. + * (See documentation on class SccIterator for information about the purpose + * of the class.) + * + *****************************************************************************/ + +template +class NullSccFilter +{ +public: + bool operator()(const typename Graph::Node*) const; +}; + + + +/****************************************************************************** + * + * Inline function definitions for template class + * SccIterator. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline SccIterator::SccIterator + (const Graph& g) : + graph(g), dfs_ordering(graph.size()), lowlink(graph.size()) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class + * SccIterator. + * Initializes a new iterator for scanning the maximal strongly + * connected components of a graph. + * + * Arguments: g -- The graph with which the iterator is to be associated + * (a Graph object). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + reset(); + computeNextScc(); +} + +/* ========================================================================= */ +template +inline SccIterator::~SccIterator() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class + * SccIterator. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +template +inline bool SccIterator::operator== + (const SccIterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: Equality test for two SccIterators. Two SccIterators are + * `equal' if and only if both of them are associated with + * exactly the same graph object in memory and the iterators + * have processed the same amount of graph nodes. + * + * Arguments: it -- A constant reference to another SccIterator. + * + * Returns: A truth value according to the result of the equality test. + * + * ------------------------------------------------------------------------- */ +{ + return (&graph == &(it.graph) && dfs_number == it.dfs_number); +} + +/* ========================================================================= */ +template +inline bool SccIterator::operator!= + (const SccIterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: Inequality test for two SccIterators. Two SccIterators are + * not equal if and only if they are associated with different + * graphs or they are associated with the same graph object in + * memory but the iterators have processed a different number of + * graph nodes. + * + * Arguments: it -- A constant reference to another SccIterator. + * + * Returns: A truth value according to the result of the inequality test. + * + * ------------------------------------------------------------------------- */ +{ + return (&graph != &(it.graph) || dfs_number != it.dfs_number); +} + +/* ========================================================================= */ +template +inline bool SccIterator::operator< + (const SccIterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: `Less than' relation between two SccIterators. An + * SccIterator is `less than' another if and only if the + * iterators relate to the same graph object in memory and + * the first iterator has processed a smaller number of nodes + * than the second one. + * + * Arguments: it -- A constant reference to another SccIterator. + * + * Returns: A truth value according to the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + return (&graph == &(it.graph) && dfs_number < it.dfs_number); +} + +/* ========================================================================= */ +template +inline bool SccIterator::operator<= + (const SccIterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: `Less than or equal' relation between two SccIterators. An + * SccIterator is `less than or equal to' another if and only + * if the iterators relate to the same graph object in memory + * and the first iterator has processed a number of nodes not + * exceeding the number of nodes the second iterator has + * processed. + * + * Arguments: it -- A constant reference to another SccIterator. + * + * Returns: A truth value according to the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + return (&graph == &(it.graph) && dfs_number <= it.dfs_number); +} + +/* ========================================================================= */ +template +inline bool SccIterator::operator> + (const SccIterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: `Greater than' relation between two SccIterators. An + * SccIterator is `greater than' another if and only if the + * iterators relate to the same graph object in memory and + * the first iterator has processed a greater number of nodes + * than the second one. + * + * Arguments: it -- A constant reference to another SccIterator. + * + * Returns: A truth value according to the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + return (&graph == &(it.graph) && dfs_number > it.dfs_number); +} + +/* ========================================================================= */ +template +inline bool SccIterator::operator>= + (const SccIterator& it) const +/* ---------------------------------------------------------------------------- + * + * Description: `Greater than or equal' relation between two SccIterators. An + * SccIterator is `greater than or equal to' another if and + * only if the iterators relate to the same graph object in + * memory and the first iterator has processed at least as many + * nodes as the second iterator has processed. + * + * Arguments: it -- A constant reference to another SccIterator. + * + * Returns: A truth value according to the result of the test. + * + * ------------------------------------------------------------------------- */ +{ + return (&graph == &(it.graph) && dfs_number >= it.dfs_number); +} + +/* ========================================================================= */ +template +inline const SccContainer& +SccIterator::operator*() const +/* ---------------------------------------------------------------------------- + * + * Description: Dereferencing operator for a SccIterator. Returns the + * collection of nodes which belong to the maximal strongly + * connected component that the iterator currently points to. + * + * Arguments: None. + * + * Returns: A collection of nodes representing some maximal strongly + * connected component. + * + * ------------------------------------------------------------------------- */ +{ + return current_scc; +} + +/* ========================================================================= */ +template +inline const SccContainer* +SccIterator::operator->() const +/* ---------------------------------------------------------------------------- + * + * Description: Dereferencing operator for a SccIterator. Returns the + * collection of nodes which belong to the maximal strongly + * connected component that the iterator currently points to. + * + * Arguments: None. + * + * Returns: A collection of nodes representing some maximal strongly + * connected component. + * + * ------------------------------------------------------------------------- */ +{ + return ¤t_scc; +} + +/* ========================================================================= */ +template +inline const SccContainer& +SccIterator::operator++() +/* ---------------------------------------------------------------------------- + * + * Description: Prefix increment operator for a SccIterator. Computes the + * next maximal strongly connected component of the graph and + * then returns it. + * + * Arguments: None. + * + * Returns: A collection of nodes representing some maximal strongly + * connected component. + * + * ------------------------------------------------------------------------- */ +{ + computeNextScc(); + return current_scc; +} + +/* ========================================================================= */ +template +inline const SccContainer +SccIterator::operator++(int) +/* ---------------------------------------------------------------------------- + * + * Description: Postfix increment operator for a SccIterator. Effectively + * returns the maximal strongly connected component of the graph + * currently pointed to by the iterator and then updates the + * iterator to point to the next strongly connected component. + * + * Arguments: None (the `int' is only required to distinguish this operator + * from the prefix increment operator). + * + * Returns: A collection of nodes representing some maximal strongly + * connected component. + * + * ------------------------------------------------------------------------- */ +{ + SccContainer old_scc = current_scc; + computeNextScc(); + return old_scc; +} + +/* ========================================================================= */ +template +inline bool SccIterator::atEnd() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells whether there are still more strongly connected + * components in the graph for the iterator to process. + * + * Arguments: None. + * + * Returns: A truth value. + * + * ------------------------------------------------------------------------- */ +{ + return (current_node == graph.size()); +} + + + +/****************************************************************************** + * + * Function definitions for template class + * SccIterator. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +void SccIterator::reset() +/* ---------------------------------------------------------------------------- + * + * Description: Initializes the iterator to point to the first maximal + * strongly connected component of the graph with which the + * iterator it associated. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + dfs_number = 0; + + for (typename vector::size_type, + ALLOC(typename Graph::size_type) > + ::iterator node = dfs_ordering.begin(); + node != dfs_ordering.end(); + ++node) + *node = 0; + + while (!node_stack.empty()) + node_stack.pop(); + + while (!scc_stack.empty()) + scc_stack.pop(); + + current_scc.clear(); +} + +/* ========================================================================= */ +template +void SccIterator::computeNextScc() +/* ---------------------------------------------------------------------------- + * + * Description: Updates the state of the iterator to `point to' the next + * maximal strongly connected component of the graph, using the + * algorithm due to Tarjan [R. Tarjan. Depth-first search and + * linear graph algorithms. SIAM Journal on Computing, + * 1(2):146--160, June 1972] for computing the next maximal + * strongly connected component of the graph. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + current_scc.clear(); + + if (scc_stack.empty() && node_stack.empty()) + { + /* + * If both `scc_stack' and `node_stack' are empty (this holds if we have + * recently finished processing some component of the graph), try to find + * a graph node that has not yet been visited. If no such node is found, + * all nodes have been visited and there are no more strongly connected + * components to be found in the graph. + */ + + current_node = 0; + for (typename vector::size_type, + ALLOC(typename Graph::size_type) > + ::const_iterator node = dfs_ordering.begin(); + node != dfs_ordering.end() && (*node) != 0; + ++node) + ++current_node; + + if (current_node == graph.size()) + return; + + /* + * Prepare to continue the depth-first search in the unvisited node. + */ + + edge = graph[current_node].edges().begin(); + + scc_stack.push(current_node); + ++dfs_number; + dfs_ordering[current_node] = lowlink[current_node] = dfs_number; + } + + typename Graph::size_type child_node; + + while (1) + { + /* + * If there are still nodes left in the depth-first search backtracking + * stack, pop a node and its next unprocessed outgoing edge off the stack. + * Before continuing the depth-first search in the popped node, update + * its lowlink value if necessary. (This has to be done if the lowlink of + * the current node---a successor of the popped node---is less than the + * lowlink of the popped node but not equal to zero.) + */ + + if (!node_stack.empty()) + { + typename Graph::size_type father_node + = node_stack.top().first; + edge = node_stack.top().second; + node_stack.pop(); + + if (lowlink[current_node] < lowlink[father_node] + && lowlink[current_node] != 0) + lowlink[father_node] = lowlink[current_node]; + + current_node = father_node; + } + + /* + * Scan through the successors of the current node. + * + * If the current nodes has an unvisited successor node (a successor i + * with dfs_ordering[i] == 0), push the current node and its next + * unprocessed edge onto the backtracking stack and then continue the + * search in the successor node. Push also the successor node onto the + * strongly connected component stack. + * + * Otherwise, update the lowlink of the current node to the lowlink of + * its already visited successor if necessary. + */ + + while (edge != graph[current_node].edges().end()) + { + child_node = (*edge)->targetNode(); + ++edge; + + if (dfs_ordering[child_node] == 0) + { + node_stack.push(make_pair(current_node, edge)); + scc_stack.push(child_node); + + ++dfs_number; + dfs_ordering[child_node] = lowlink[child_node] = dfs_number; + + current_node = child_node; + edge = graph[current_node].edges().begin(); + } + else if (lowlink[child_node] < lowlink[current_node] + && lowlink[child_node] != 0) + lowlink[current_node] = lowlink[child_node]; + } + + /* + * If the least node in the depth-first search order reachable from the + * current node is the current node itself at the end of the previous + * loop, we have found a maximal strongly connected component of the + * graph. In this case, collect the states satisfying `cond' in the + * strongly connected component stack to form the component and exit. + * (Otherwise, return to the start of the outermost while loop and + * continue by popping a state off the depth-first search backtracking + * stack.) + */ + + if (dfs_ordering[current_node] == lowlink[current_node]) + { + do + { + child_node = scc_stack.top(); + scc_stack.pop(); + if (cond(&graph[child_node])) + current_scc.insert(child_node); + lowlink[child_node] = 0; + } + while (child_node != current_node); + + break; + } + } +} + + + +/****************************************************************************** + * + * Inline function definitions for template class NullSccFilter. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +inline bool NullSccFilter::operator() + (const typename Graph::Node*) const +/* ---------------------------------------------------------------------------- + * + * Description: Default test for filtering the nodes in a strongly connected + * graph component. The default is to simply include all nodes + * in the result. + * + * Arguments: A constant pointer to a Graph::Node (required + * only to satisfy the class interface requirements). + * + * Returns: true, so the test will succeed for every node in the + * component. + * + * ------------------------------------------------------------------------- */ +{ + return true; +} + +} + +#endif /* !SCCITERATOR_H */ diff --git a/lbtt/src/SharedTestData.h b/lbtt/src/SharedTestData.h new file mode 100644 index 000000000..ff4cd9209 --- /dev/null +++ b/lbtt/src/SharedTestData.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SHAREDTESTDATA_H +#define SHAREDTESTDATA_H + +#include +#include "Alloc.h" +#include "Exception.h" +#include "TestRoundInfo.h" +#include "TestStatistics.h" + +/****************************************************************************** + * + * Declarations of variables for storing test results and maintaining test + * state information. + * + *****************************************************************************/ + +namespace SharedTestData +{ + +extern TestRoundInfo round_info; /* Data structure for + * storing information + * about the current test + * round. + */ + +extern vector /* implementation. */ + test_results; + +extern vector /* statistics for each */ + final_statistics; /* implementation. */ + +} + +#endif /* !SHAREDTESTDATA_H */ diff --git a/lbtt/src/SpinWrapper.cc b/lbtt/src/SpinWrapper.cc new file mode 100644 index 000000000..1b21aa68c --- /dev/null +++ b/lbtt/src/SpinWrapper.cc @@ -0,0 +1,123 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#ifdef HAVE_SSTREAM +#include +#else +#include +#endif /* HAVE_SSTREAM */ +#include "Exception.h" +#include "FormulaWriter.h" +#include "NeverClaimAutomaton.h" +#include "SpinWrapper.h" + +/****************************************************************************** + * + * Definitions for operator symbols specific to Spin. + * + *****************************************************************************/ + +const char SpinWrapper::SPIN_AND[] = "&&"; +const char SpinWrapper::SPIN_OR[] = "||"; + + + +/****************************************************************************** + * + * Function definitions for class SpinWrapper. + * + *****************************************************************************/ + +/* ========================================================================= */ +void SpinWrapper::translateFormula + (const ::Ltl::LtlFormula& formula, string& translated_formula) +/* ---------------------------------------------------------------------------- + * + * Description: Translates an LtlFormula into a string which contains the + * formula in the input syntax of Spin. + * + * Arguments: formula -- The LtlFormula to be translated. + * translated_formula -- A reference to a string for storing + * the results. + * + * Returns: Nothing. The result of the translation can be found in + * the string `translated_formula'. + * + * ------------------------------------------------------------------------- */ +{ + using namespace Ltl; + +#ifdef HAVE_SSTREAM + ostringstream translated_formula_stream; +#else + ostrstream translated_formula_stream; +#endif /* HAVE_SSTREAM */ + Exceptional_ostream estream(&translated_formula_stream, ios::goodbit); + + FormulaWriter, + ConstantWriter, + AtomWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + UnaryOperatorWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + WriterErrorReporter, + BinaryOperatorInfixWriter, + BinaryOperatorInfixWriter, + WriterErrorReporter, + WriterErrorReporter, + WriterErrorReporter> + fw(estream); + + formula.traverse(fw, LTL_PREORDER | LTL_INORDER | LTL_POSTORDER); + + translated_formula = translated_formula_stream.str(); +#ifndef HAVE_SSTREAM + translated_formula_stream.freeze(0); +#endif /* HAVE_SSTREAM */ +} + +/* ========================================================================= */ +void SpinWrapper::parseAutomaton + (const string& input_filename, const string& output_filename) +/* ---------------------------------------------------------------------------- + * + * Description: Parses the never claim in the output returned by Spin and + * converts it into lbtt format. + * + * Arguments: input_filename -- Name of the input file. + * output_filename -- Name of the output file. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + NeverClaimAutomaton automaton; + automaton.read(input_filename.c_str()); + automaton.write(output_filename.c_str()); +} diff --git a/lbtt/src/SpinWrapper.h b/lbtt/src/SpinWrapper.h new file mode 100644 index 000000000..233d62b06 --- /dev/null +++ b/lbtt/src/SpinWrapper.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef SPINWRAPPER_H +#define SPINWRAPPER_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include "ExternalTranslator.h" +#include "LtlFormula.h" + +/****************************************************************************** + * + * Interface class for Spin. + * + *****************************************************************************/ + +class SpinWrapper : public ExternalTranslator +{ +public: + SpinWrapper(); /* Constructor. */ + + ~SpinWrapper(); /* Destructor. */ + + void translateFormula /* Translates a formula */ + (const ::Ltl::LtlFormula& formula, /* into a Büchi */ + string& translated_formula); /* automaton. */ + + /* `formatInput' inherited from ExternalTranslator */ + + string commandLine /* Prepares the command */ + (const string& input_filename, /* line for executing */ + const string& output_filename); /* Spin. */ + + /* `execSuccess' inherited from ExternalTranslator */ + + void parseAutomaton /* Translates the output */ + (const string& input_filename, /* of the translation */ + const string& output_filename); /* algorithm into lbtt + * format. + */ + +private: + SpinWrapper(const SpinWrapper&); /* Prevent copying and */ + SpinWrapper& operator=(const SpinWrapper&); /* assignment of + * SpinWrapper objects. + */ + + static const char SPIN_AND[]; /* Symbols for */ + static const char SPIN_OR[]; /* operators. */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class SpinWrapper. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline SpinWrapper::SpinWrapper() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class SpinWrapper. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline SpinWrapper::~SpinWrapper() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class SpinWrapper. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline string SpinWrapper::commandLine + (const string& input_filename, const string& output_filename) +/* ---------------------------------------------------------------------------- + * + * Description: Prepares the command line for Spin. + * + * Arguments: input_filename -- Name of the input file. + * output_filename -- Name of the output file. + * + * Returns: The command line string. + * + * ------------------------------------------------------------------------- */ +{ + return string(" -F ") + input_filename + " >" + output_filename; +} + +#endif /* !SPINWRAPPER_H */ diff --git a/lbtt/src/StatDisplay.cc b/lbtt/src/StatDisplay.cc new file mode 100644 index 000000000..6da2df696 --- /dev/null +++ b/lbtt/src/StatDisplay.cc @@ -0,0 +1,1455 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include "DispUtil.h" +#include "Exception.h" +#include "SharedTestData.h" +#include "StatDisplay.h" +#include "StringUtil.h" +#include "TestRoundInfo.h" + +namespace StatDisplay +{ + +using namespace ::DispUtil; +using namespace ::SharedTestData; +using namespace ::StringUtil; + +/* ========================================================================= */ +void printBuchiAutomatonStats + (ostream& stream, int indent, + vector::size_type + algorithm, + int result_id) +/* ---------------------------------------------------------------------------- + * + * Description: Displays information about a Büchi automaton, extracting the + * information from a TestStatistics structure stored in the + * UserInterface object. + * + * Arguments: stream -- A reference to the output stream to which the + * information should be written. + * indent -- Number of spaces to leave on the left of the + * output. + * algorithm -- Identifier of the algorithm used for + * generating the automaton. + * result_id -- Selects between the automata constructed from + * a formula and its negation. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + const AutomatonStats& automaton_stats = + test_results[algorithm].automaton_stats[result_id]; + + estream << string(indent, ' '); + + if (!automaton_stats.buchiAutomatonComputed()) + estream << "not computed"; + else + { + estream << "number of states:" + string(6, ' ') + + toString(automaton_stats.number_of_buchi_states) + + '\n' + string(indent, ' ') + "number of transitions: " + + toString(automaton_stats.number_of_buchi_transitions) + + '\n' + string(indent, ' ') + "acceptance sets:" + + string(7, ' ') + + toString(automaton_stats.number_of_acceptance_sets) + + '\n' + string(indent, ' ') + "computation time:" + + string(6, ' '); + + if (automaton_stats.buchi_generation_time != -1.0) + { + changeStreamFormatting(stream, 9, 2, ios::fixed | ios::left); + estream << automaton_stats.buchi_generation_time; + restoreStreamFormatting(stream); + + estream << " seconds (user time)"; + } + else + estream << "N/A"; + } + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void printProductAutomatonStats + (ostream& stream, int indent, + vector::size_type + algorithm, + int result_id) +/* ---------------------------------------------------------------------------- + * + * Description: Displays information about a product automaton, extracting + * the information from a TestStatistics structure stored in the + * UserInterface object. + * + * Arguments: stream -- A reference to the output stream to which the + * information should be written. + * indent -- Number of spaces to leave on the left of the + * output. + * algorithm -- Identifier of the algorithm used for + * generating the product automaton. + * result_id -- Selects between the automata constructed from + * a formula and its negation. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + const AutomatonStats& automaton_stats = + test_results[algorithm].automaton_stats[result_id]; + + estream << string(indent, ' '); + + if (!automaton_stats.productAutomatonComputed()) + estream << "not computed"; + else + { + estream << "number of states:" + string(6, ' '); + + changeStreamFormatting(stream, 9, 0, ios::left); + estream << automaton_stats.number_of_product_states; + restoreStreamFormatting(stream); + + estream << " ["; + + if (automaton_stats.number_of_product_states != 0) + { + changeStreamFormatting(stream, 0, 2, ios::fixed); + estream << static_cast + (automaton_stats.number_of_product_states) + / static_cast(automaton_stats.number_of_buchi_states) + / static_cast(round_info.statespace->size()) + * 100.0; + restoreStreamFormatting(stream); + + estream << "% of worst case (" + << automaton_stats.number_of_buchi_states + * round_info.statespace->size() + << ')'; + } + else + estream << "empty automaton"; + + estream << "]\n" + string(indent, ' ') + "number of transitions: " + + toString(automaton_stats.number_of_product_transitions); + } + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void printAcceptanceCycleStats + (ostream& stream, int indent, + vector::size_type + algorithm, + int result_id) +/* ---------------------------------------------------------------------------- + * + * Description: Displays information about the number of system states from + * which begins an execution accepted by a Büchi automaton. The + * information is extracted from a TestStatistics structure + * stored in the UserInterface object. + * + * Arguments: stream -- A reference to the output stream to which the + * information should be written. + * indent -- Number of spaces to leave on the left of the + * output. + * algorithm -- Identifier of the algorithm used for + * computing the Büchi automaton whose accepting + * cycles are to be considered. + * result_id -- Selects between the result computed for a + * formula and its negation. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + const AutomatonStats& automaton_stats = + test_results[algorithm].automaton_stats[result_id]; + + estream << string(indent, ' '); + + if (!automaton_stats.emptiness_check_performed) + estream << "not computed"; + else if (configuration.global_options.product_mode == Configuration::LOCAL) + { + estream << string("cycle "); + + if (automaton_stats.emptiness_check_result[0]) + estream << "reachable "; + else + estream << "not reachable"; + + estream << " (from the initial state)"; + } + else + { + estream << "cycle reachable from "; + + changeStreamFormatting(stream, 9, 0, ios::left); + estream << automaton_stats.emptiness_check_result.count(); + restoreStreamFormatting(stream); + + estream << " states\n" + string(indent, ' ') + + "not reachable from "; + + changeStreamFormatting(stream, 9, 0, ios::left); + estream << (round_info.real_emptiness_check_size + - automaton_stats.emptiness_check_result.count()); + restoreStreamFormatting(stream); + + estream << " states"; + } + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void printConsistencyCheckStats + (ostream& stream, int indent, + vector::size_type + algorithm) +/* ---------------------------------------------------------------------------- + * + * Description: Displays information about the consistency check result for + * a given algorithm, extracting the information from a + * TestStatistics structure stored in the UserInterface object. + * + * Arguments: stream -- A reference to an output stream to which the + * information should be written. + * indent -- Number of spaces to leave on the left of the + * output. + * algorithm -- Identifier of the algorithm whose consistency + * check result should be displayed. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + const AlgorithmTestResults& test_result = test_results[algorithm]; + + estream << string(indent, ' '); + + if (test_result.consistency_check_result == -1) + estream << "not performed"; + else + { + estream << "result:" + string(18, ' '); + + if (test_result.consistency_check_result == 0) + { + estream << "failed [" + + toString(test_result.failed_consistency_check_comparisons) + + " ("; + + changeStreamFormatting(stream, 0, 2, ios::fixed); + estream << ((test_result.consistency_check_comparisons == 0) + ? 0.0 + : static_cast + (test_result.failed_consistency_check_comparisons) + / test_result.consistency_check_comparisons * 100.0); + restoreStreamFormatting(stream); + + estream << "%) of " + + toString(test_result.consistency_check_comparisons) + + " test cases]"; + } + else + estream << "passed"; + } + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void printCrossComparisonStats + (ostream& stream, int indent, + vector::size_type + algorithm) +/* ---------------------------------------------------------------------------- + * + * Description: Displays information about the model checking result cross- + * comparison check, extracting the information from a vector of + * TestStatistics structures stored in the UserInterface object. + * + * Arguments: stream -- A reference to an output stream to which the + * information should be written. + * indent -- Number of spaces to leave on the left of the + * output. + * algorithm -- If less than the number of algorithms, show + * only the results for the algorithm with this + * identifier. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + bool no_errors_to_report = true; + + if (algorithm < configuration.algorithms.size() + && !configuration.algorithms[algorithm].enabled) + { + estream << string(indent, ' ') + "not performed\n\n"; + estream.flush(); + return; + } + + estream << string(indent, ' ') + "result:"; + + const AutomatonStats* alg_1_pos_results; + const AutomatonStats* alg_1_neg_results; + + for (vector::size_type + alg_1 = 0; + alg_1 < test_results.size(); + alg_1++) + { + alg_1_pos_results = &test_results[alg_1].automaton_stats[0]; + alg_1_neg_results = &test_results[alg_1].automaton_stats[1]; + + for (vector::size_type + alg_2 = alg_1 + 1; + alg_2 < test_results.size(); + alg_2++) + { + if ((algorithm >= configuration.algorithms.size() + || alg_1 == algorithm + || alg_2 == algorithm) + && configuration.algorithms[alg_1].enabled + && configuration.algorithms[alg_2].enabled) + { + bool pos_test, neg_test; + + for (int counter = 0; counter < 2; counter++) + { + if (counter == 0) + { + pos_test = !alg_1_pos_results->crossComparisonPerformed(alg_2); + neg_test = !alg_1_neg_results->crossComparisonPerformed(alg_2); + } + else + { + pos_test = (alg_1_pos_results->cross_comparison_stats[alg_2]. + second > 0); + neg_test = (alg_1_neg_results->cross_comparison_stats[alg_2]. + second > 0); + } + + if (pos_test || neg_test) + { + estream << '\n' + string(indent + 2, ' '); + no_errors_to_report = false; + + estream << string(counter == 0 ? "N/A " : "failed") + " ("; + + if (pos_test) + estream << string("+") + (neg_test ? "-)" : ") "); + else + estream << "-) "; + + estream << ' '; + if (alg_1 == round_info.number_of_translators) + estream << "lbtt"; + else + estream << configuration.algorithmString(alg_1); + estream << ", "; + if (alg_2 == round_info.number_of_translators) + estream << "lbtt"; + else + estream << configuration.algorithmString(alg_2); + } + } + } + } + } + + if (no_errors_to_report) + estream << string(20, ' ') + "no failures detected"; + + estream << "\n\n"; + estream.flush(); +} + +/* ========================================================================= */ +void printBuchiIntersectionCheckStats + (ostream& stream, int indent, + vector::size_type + algorithm) +/* ---------------------------------------------------------------------------- + * + * Description: Displays information about the Büchi automaton intersection + * emptiness check results, extracting the information from a + * TestStatistics structure stored in the UserInterface object. + * + * Arguments: stream -- A reference to an output stream to which the + * information should be written. + * indent -- Number of spaces to leave on the left of the + * output. + * algorithm -- If less than the number of algorithms, show + * only the results for the algorithm with this + * identifier. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + bool no_errors_to_report = true; + + if (algorithm < round_info.number_of_translators + && !configuration.algorithms[algorithm].enabled) + { + estream << string(indent, ' ') + "not performed\n\n"; + estream.flush(); + return; + } + + estream << string(indent, ' ') + "result:"; + + const AutomatonStats* alg_1_pos_results; + const AutomatonStats* alg_1_neg_results; + + for (vector::size_type + alg_1 = 0; + alg_1 < round_info.number_of_translators; + alg_1++) + { + alg_1_pos_results = &test_results[alg_1].automaton_stats[0]; + alg_1_neg_results = &test_results[alg_1].automaton_stats[1]; + + for (vector::size_type + alg_2 = alg_1; + alg_2 < round_info.number_of_translators; + alg_2++) + { + if ((algorithm >= round_info.number_of_translators + || alg_1 == algorithm + || alg_2 == algorithm) + && configuration.algorithms[alg_1].enabled + && configuration.algorithms[alg_2].enabled) + { + bool pos_test, neg_test; + + for (int counter = -1; counter < 1; counter++) + { + pos_test = (alg_1_pos_results->buchi_intersection_check_stats[alg_2] + == counter); + neg_test = (alg_1_neg_results->buchi_intersection_check_stats[alg_2] + == counter); + + if (pos_test || neg_test) + { + estream << '\n' + string(indent + 2, ' '); + no_errors_to_report = false; + + estream << string(counter == -1 ? "N/A " : "failed") + ' '; + + if (alg_1 != alg_2) + { + estream << '('; + if (pos_test) + estream << string("+") + (neg_test ? "-)" : ") "); + else + estream << "-) "; + } + else + estream << " "; + + estream << ' ' + configuration.algorithmString(alg_1); + + if (alg_1 != alg_2) + { + estream << ", ("; + if (pos_test) + estream << string("-") + (neg_test ? "+" : ""); + else + estream << '+'; + + estream << ") " + configuration.algorithmString(alg_2); + } + } + } + } + } + } + + if (no_errors_to_report) + estream << string(20, ' ') + "no failures detected"; + + estream << "\n\n"; + estream.flush(); +} + +/* ========================================================================= */ +void printAllStats + (ostream& stream, int indent, + vector::size_type + algorithm) +/* ---------------------------------------------------------------------------- + * + * Description: Displays all test information (Büchi automaton and product + * automaton statistics, acceptance cycle information, + * consistency check result) for an algorithm. + * + * Arguments: stream -- A reference to an output stream to which the + * information should be written. + * indent -- Number of spaces to leave on the left of the + * output. + * algorithm -- Identifier of an algorithm. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + estream << string(indent, ' ') + configuration.algorithmString(algorithm) + + '\n'; + estream.flush(); + + for (int counter = 0; counter < 2; counter++) + { + estream << string(indent + 2, ' ') + + (counter == 0 ? "Positive" : "Negated") + " formula:\n" + + string(indent + 4, ' ') + "Büchi automaton:\n"; + printBuchiAutomatonStats(stream, indent + 6, algorithm, counter); + + if (configuration.global_options.do_comp_test + || configuration.global_options.do_cons_test) + { + estream << string(indent + 4, ' ') + "Product automaton:\n"; + printProductAutomatonStats(stream, indent + 6, algorithm, counter); + estream << string(indent + 4, ' ') + "Accepting cycles:\n"; + printAcceptanceCycleStats(stream, indent + 6, algorithm, counter); + } + } + + if (configuration.global_options.do_cons_test) + { + estream << string(indent + 2, ' ') + "Result consistency check:\n"; + printConsistencyCheckStats(stream, indent + 4, algorithm); + } + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void printCollectiveCrossComparisonStats + (ostream& stream, + vector::size_type algorithm_y, + vector::size_type algorithm_x, + int data_type) +/* ---------------------------------------------------------------------------- + * + * Description: Called by printCollectiveStats in order to fill a single cell + * of the result cross-comparison table. + * + * Arguments: stream -- A reference to an output + * stream. + * algorithm_x, algorithm_y -- Identifiers of the algorithms + * whose cross-comparison results + * should be displayed. + * data_type -- Determines the type of data to + * be displayed in the cell: + * 0 -- Model checking result + * cross-comparison + * statistics. + * 1 -- Model checking result + * cross-comparison + * statistics (initial + * state only). + * 2 -- Büchi automaton + * intersection check + * statistics. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + estream << ' '; + + if (algorithm_x == algorithm_y && data_type != 2) + estream << string(21, ' '); + else + { + unsigned long int num_comparisons, num_mismatches; + const TestStatistics& stats = final_statistics[algorithm_y]; + + switch (data_type) + { + case 0 : + num_comparisons = stats.cross_comparisons_performed[algorithm_x]; + num_mismatches = stats.cross_comparison_mismatches[algorithm_x]; + break; + + case 1 : + num_comparisons = stats.cross_comparisons_performed[algorithm_x]; + num_mismatches + = stats.initial_cross_comparison_mismatches[algorithm_x]; + break; + + default : + if (configuration.global_options.statespace_generation_mode + & Configuration::PATH + && (algorithm_x == round_info.number_of_translators + || algorithm_y == round_info.number_of_translators)) + { + estream << string(21, ' '); + return; + } + + num_comparisons + = stats.buchi_intersection_checks_performed[algorithm_x]; + num_mismatches = stats.buchi_intersection_check_failures[algorithm_x]; + break; + } + + if (num_comparisons > 0) + { + changeStreamFormatting(stream, 5, 0, ios::right); + estream << num_mismatches; + restoreStreamFormatting(stream); + + estream << '/'; + + changeStreamFormatting(stream, 5, 0, ios::left); + estream << num_comparisons; + restoreStreamFormatting(stream); + + estream << " ("; + + double percentage = static_cast(num_mismatches) + / static_cast(num_comparisons) + * 100.0; + + changeStreamFormatting(stream, 0, 2, ios::fixed); + estream << percentage; + restoreStreamFormatting(stream); + + estream << "%)"; + + if (percentage < 100.0) + estream << ' '; + if (percentage < 10.0) + estream << ' '; + } + else + estream << " N/A" + string(14, ' '); + } +} + +/* ========================================================================= */ +void printCollectiveStats(ostream& stream, int indent) +/* ---------------------------------------------------------------------------- + * + * Description: Displays average information about a series of tests for each + * tested algorithm: + * - average sizes of the Büchi and product automata + * - number of failed attempts to generate a Büchi automaton + * - number of failed consistency checks + * - number of failed path checks + * - cross-comparison mismatches + * - Büchi automata intersection emptiness check failures + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of the + * output. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + const string ind(indent, ' '); + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Display state space statistics. + */ + + estream << '\n' + ind + "Statistics after round " + + toString(round_info.current_round) + '\n' + + ind + string(toString(round_info.current_round).length() + 23, + '*') + + "\n\n\n"; + + if (configuration.global_options.do_comp_test + || configuration.global_options.do_cons_test) + { + estream << ind + " State space statistics\n" + + ind + " " + string(22, '=') + "\n\n" + + ind + " " + toString(round_info.num_generated_statespaces) + + " state spaces generated\n" + + ind + " " + toString(round_info.total_statespace_states) + + " states generated"; + + if (round_info.num_generated_statespaces > 0) + { + estream << " (" + + toString(static_cast + (round_info.total_statespace_states) + / static_cast + (round_info.num_generated_statespaces), + 2) + + " states per state space)"; + } + + estream << '\n' + ind + " " + + toString(round_info.total_statespace_transitions) + + " transitions generated"; + + if (round_info.num_generated_statespaces > 0) + { + estream << " (" + + toString(static_cast + (round_info.total_statespace_transitions) + / static_cast + (round_info.num_generated_statespaces), + 2) + + " transitions per state space)"; + } + + estream << "\n\n\n"; + } + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Display LTL formula statistics. + */ + + estream << ind + " LTL formula statistics\n" + + ind + " " + string(22, '=') + "\n\n" + + ind + " " + toString(round_info.num_processed_formulae) + + " LTL formulas " + + (!configuration.global_options.formula_input_filename.empty() + ? "process" + : "generat") + + "ed\n"; + + if (round_info.num_processed_formulae > 0 + && configuration.global_options.formula_input_filename.empty()) + { + const map, + ALLOC(unsigned long int) >& + proposition_statistics + = configuration.formula_options.formula_generator. + propositionStatistics(); + + const map, ALLOC(unsigned long int) > + symbol_statistics + = configuration.formula_options.formula_generator.symbolStatistics(); + + estream << '\n' + ind + " Atomic symbol distribution:\n"; + string symbol_name_string; + string symbol_number_string; + string symbol_distribution_string; + int number_of_symbols_printed = 0; + + if (symbol_statistics.find(::Ltl::LTL_TRUE) != symbol_statistics.end()) + { + const unsigned long int num + = symbol_statistics.find(::Ltl::LTL_TRUE)->second; + symbol_name_string += "true "; + const string number_string = toString(num); + symbol_number_string += number_string + + string(12 - number_string.length(), ' '); + const string distribution_string + = toString(static_cast(num) + / static_cast(round_info.num_processed_formulae), + 3); + symbol_distribution_string += distribution_string + + string(12 - distribution_string.length(), + ' '); + number_of_symbols_printed++; + } + + if (symbol_statistics.find(::Ltl::LTL_FALSE) != symbol_statistics.end()) + { + const unsigned long int num + = symbol_statistics.find(::Ltl::LTL_FALSE)->second; + symbol_name_string += "false "; + const string number_string = toString(num); + symbol_number_string += number_string + + string(12 - number_string.length(), ' '); + const string distribution_string + = toString(static_cast(num) + / static_cast(round_info.num_processed_formulae), + 3); + symbol_distribution_string += distribution_string + + string(12 - distribution_string.length(), + ' '); + number_of_symbols_printed++; + } + + for (map, ALLOC(unsigned long int) > + ::const_iterator proposition = proposition_statistics.begin(); + proposition != proposition_statistics.end(); + ++proposition) + { + const string name_string = "p" + toString(proposition->first); + symbol_name_string += name_string; + const string number_string = toString(proposition->second); + symbol_number_string += number_string; + const string distribution_string + = toString(static_cast(proposition->second) + / static_cast(round_info.num_processed_formulae), + 3); + symbol_distribution_string += distribution_string; + + number_of_symbols_printed++; + + if (number_of_symbols_printed % 5 == 0) + { + estream << ind + " symbol " + symbol_name_string + '\n' + + ind + " # " + symbol_number_string + '\n' + + ind + " #/formula " + symbol_distribution_string + + "\n\n"; + symbol_name_string = symbol_number_string = symbol_distribution_string + = ""; + } + else + { + symbol_name_string += string(12 - name_string.length(), ' '); + symbol_number_string += string(12 - number_string.length(), ' '); + symbol_distribution_string += string(12 - distribution_string.length(), + ' '); + } + } + + if (number_of_symbols_printed % 5 != 0) + { + estream << ind + " symbol " + symbol_name_string + '\n' + + ind + " # " + symbol_number_string + '\n' + + ind + " #/formula " + symbol_distribution_string + + "\n\n"; + } + + estream << ind + " Operator distribution:\n"; + symbol_name_string = symbol_number_string = symbol_distribution_string + = ""; + number_of_symbols_printed = 0; + + for (map, ALLOC(unsigned long int) > + ::const_iterator op = symbol_statistics.begin(); + op != symbol_statistics.end(); + ++op) + { + if (op->first == ::Ltl::LTL_ATOM || op->first == ::Ltl::LTL_TRUE + || op->first == ::Ltl::LTL_FALSE) + continue; + + const string name_string = ::Ltl::infixSymbol(op->first); + symbol_name_string += name_string; + const string number_string = toString(op->second); + symbol_number_string += number_string; + const string distribution_string + = toString(static_cast(op->second) + / static_cast(round_info.num_processed_formulae), + 3); + symbol_distribution_string += distribution_string; + + number_of_symbols_printed++; + + if (number_of_symbols_printed % 5 == 0) + { + if (number_of_symbols_printed > 5) + estream << '\n'; + estream << ind + " operator " + symbol_name_string + '\n' + + ind + " # " + symbol_number_string + '\n' + + ind + " #/formula " + symbol_distribution_string + + '\n'; + symbol_name_string = symbol_number_string = symbol_distribution_string + = ""; + } + else + { + symbol_name_string += string(12 - name_string.length(), ' '); + symbol_number_string += string(12 - number_string.length(), ' '); + symbol_distribution_string += string(12 - distribution_string.length(), + ' '); + } + } + + if (number_of_symbols_printed % 5 != 0) + { + estream << ind + " operator " + symbol_name_string + '\n' + + ind + " # " + symbol_number_string + '\n' + + ind + " #/formula " + symbol_distribution_string + + '\n'; + } + } + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Display the following information for each algorithm: + * 0. Automata statistics. + * 1. Number of failures to compute Büchi automata. + * 2. Number of model checking result consistency check failures. + */ + + string algorithm_name; + + for (int i = 0; i <= 2; i++) + { + estream << '\n' + string(2 + indent, ' '); + + switch (i) + { + case 1 : + estream << "Failures to compute Büchi automaton\n" + + string(2 + indent, ' ') + string(35, '=') + '\n'; + break; + case 2 : + if (!configuration.global_options.do_cons_test) + continue; + + estream << "Model checking result consistency check failures\n" + + string(2 + indent, ' ') + string(48, '=') + '\n'; + break; + default : + break; + } + + for (unsigned long int algorithm = 0; + algorithm < round_info.number_of_translators; + ++algorithm) + { + estream << '\n' + string((i > 0 ? 4 : 2) + indent, ' ') + + *(configuration.algorithms[algorithm].name) + '\n'; + + switch (i) + { + /* + * Display a table of automaton statistics. + */ + + case 0 : + { + unsigned long int failures_to_compute_automaton; + unsigned long int automaton_count; + unsigned long int number_of_successful_instances; + unsigned long int total_number_of_states; + unsigned long int total_number_of_transitions; + + const TestStatistics& stats = final_statistics[algorithm]; + + estream << string(2 + indent, ' ') + + string(configuration.algorithms[algorithm].name + ->length(), '='); + + for (int k = 0; k < 2; k++) + { + if (k == 1 && !configuration.global_options.do_comp_test + && !configuration.global_options.do_cons_test) + continue; + + estream << "\n\n" + string(8 + indent, ' ') + + (k == 0 ? "BÜCHI " : "PRODUCT") + string(7, ' ') + + "| Number of | Number of |" + " Number of |\n" + + string(8 + indent, ' ') + + "AUTOMATA" + string(6, ' ') + + "| automata | states |" + " transitions |\n" + + string(7 + indent, ' ') + string(15, '-') + '+'; + + for (int j = 0; j < 3; j++) + estream << string(17, '-') + '+'; + + estream << '\n'; + + for (int j = 0; j < 3; j++) + { + estream << string(8 + indent, ' '); + switch (j) + { + case 0 : estream << "Pos. formulae"; break; + case 1 : estream << "Neg. formulae"; break; + default : estream << "All formulae "; break; + } + + estream << " | "; + + if (j < 2) + { + if (k == 0) + { + failures_to_compute_automaton + = stats.failures_to_compute_buchi_automaton[j]; + automaton_count = stats.buchi_automaton_count[j]; + total_number_of_states + = stats.total_number_of_buchi_states[j]; + total_number_of_transitions + = stats.total_number_of_buchi_transitions[j]; + } + else + { + failures_to_compute_automaton + = stats.failures_to_compute_product_automaton[j]; + automaton_count = stats.product_automaton_count[j]; + total_number_of_states + = stats.total_number_of_product_states[j]; + total_number_of_transitions + = stats.total_number_of_product_transitions[j]; + } + } + else + { + if (k == 0) + { + failures_to_compute_automaton = + stats.failures_to_compute_buchi_automaton[0] + + stats.failures_to_compute_buchi_automaton[1]; + automaton_count = stats.buchi_automaton_count[0] + + stats.buchi_automaton_count[1]; + total_number_of_states + = stats.total_number_of_buchi_states[0] + + stats.total_number_of_buchi_states[1]; + total_number_of_transitions + = stats.total_number_of_buchi_transitions[0] + + stats.total_number_of_buchi_transitions[1]; + } + else + { + failures_to_compute_automaton = + stats.failures_to_compute_product_automaton[0] + + stats.failures_to_compute_product_automaton[1]; + automaton_count = stats.product_automaton_count[0] + + stats.product_automaton_count[1]; + total_number_of_states + = stats.total_number_of_product_states[0] + + stats.total_number_of_product_states[1]; + total_number_of_transitions + = stats.total_number_of_product_transitions[0] + + stats.total_number_of_product_transitions[1]; + } + } + + number_of_successful_instances = + automaton_count - failures_to_compute_automaton; + + for (int z = 0; z < 2; z++) + { + if (z == 0) + { + changeStreamFormatting(stream, 15, 2, ios::right); + estream << number_of_successful_instances; + restoreStreamFormatting(stream); + } + else + estream << string(indent + 15, ' ') + "(avg.) |" + + string(16, ' '); + + estream << " | "; + + if (number_of_successful_instances == 0) + estream << string(15, ' '); + else + { + changeStreamFormatting(stream, 15, 2, + ios::fixed | ios::right); + if (z == 0) + estream << total_number_of_states; + else + estream << total_number_of_states + / static_cast + (number_of_successful_instances); + restoreStreamFormatting(stream); + } + + estream << " | "; + + if (number_of_successful_instances == 0) + estream << string(15, ' '); + else + { + changeStreamFormatting(stream, 15, 2, + ios::fixed | ios::right); + if (z == 0) + estream << total_number_of_transitions; + else + estream << total_number_of_transitions + / static_cast + (number_of_successful_instances); + restoreStreamFormatting(stream); + } + + estream << " |\n"; + } + } + + if (k == 0) + { + unsigned long int total_number_of_acceptance_sets; + double buchi_generation_time; + + estream << '\n' + string(22 + indent, ' ') + + "| Number of | Time consumed |\n" + + string(22 + indent, ' ') + + "| acceptance sets | (seconds) |\n" + + string(7 + indent, ' ') + string(15, '-') + '+'; + + for (int j = 0; j < 2; j++) + estream << string(17, '-') + '+'; + + for (int j = 0; j < 3; j++) + { + estream << '\n' + string(8 + indent, ' '); + switch (j) + { + case 0 : estream << "Pos. formulae"; break; + case 1 : estream << "Neg. formulae"; break; + default : estream << "All formulae "; break; + } + + estream << " | "; + + if (j < 2) + { + failures_to_compute_automaton = + stats.failures_to_compute_buchi_automaton[j]; + + automaton_count = stats.buchi_automaton_count[j]; + + buchi_generation_time = stats.total_buchi_generation_time[j]; + total_number_of_acceptance_sets + = stats.total_number_of_acceptance_sets[j]; + } + else + { + failures_to_compute_automaton = + stats.failures_to_compute_buchi_automaton[0] + + stats.failures_to_compute_buchi_automaton[1]; + + automaton_count = stats.buchi_automaton_count[0] + + stats.buchi_automaton_count[1]; + + buchi_generation_time = + (stats.total_buchi_generation_time[0] >= 0.0 + && stats.total_buchi_generation_time[1] >= 0.0 + ? stats.total_buchi_generation_time[0] + + stats.total_buchi_generation_time[1] + : -1.0); + total_number_of_acceptance_sets + = stats.total_number_of_acceptance_sets[0] + + stats.total_number_of_acceptance_sets[1]; + } + + number_of_successful_instances = + automaton_count - failures_to_compute_automaton; + + for (int z = 0; z < 2; z++) + { + if (number_of_successful_instances == 0) + estream << string(15, ' '); + else + { + changeStreamFormatting(stream, 15, 2, + ios::fixed | ios::right); + if (z == 0) + estream << total_number_of_acceptance_sets; + else + estream << total_number_of_acceptance_sets + / static_cast + (number_of_successful_instances); + restoreStreamFormatting(stream); + } + + estream << " | "; + + if (number_of_successful_instances == 0 + || buchi_generation_time < 0.0) + estream << string(15, ' '); + else + { + changeStreamFormatting(stream, 15, 2, + ios::fixed | ios::right); + if (z == 0) + estream << buchi_generation_time; + else + estream << buchi_generation_time + / number_of_successful_instances; + restoreStreamFormatting(stream); + } + + estream << " |"; + if (z == 0) + estream << '\n' + string(indent + 15, ' ') + "(avg.) | "; + } + } + + estream << '\n'; + } + } + + if (algorithm + 1 < round_info.number_of_translators) + estream << '\n'; + + break; + } + + /* + * Display the number of automaton computation failures. + */ + + case 1 : + { + unsigned long int number_of_failures; + unsigned long int total_count; + + const TestStatistics& stats = final_statistics[algorithm]; + + for (int j = 0; j < 3; j++) + { + estream << string(8 + indent, ' '); + switch (j) + { + case 0 : estream << "Positive formulae: "; break; + case 1 : estream << "Negative formulae: "; break; + default : estream << "Total:" + string(13, ' '); break; + } + + if (j < 2) + { + number_of_failures + = stats.failures_to_compute_buchi_automaton[j]; + total_count = stats.buchi_automaton_count[j]; + } + else + { + number_of_failures + = stats.failures_to_compute_buchi_automaton[0] + + stats.failures_to_compute_buchi_automaton[1]; + total_count = stats.buchi_automaton_count[0] + + stats.buchi_automaton_count[1]; + } + + changeStreamFormatting(stream, 5, 0, ios::left); + estream << number_of_failures; + restoreStreamFormatting(stream); + + estream << " ["; + + if (total_count > 0) + { + changeStreamFormatting(stream, 0, 2, ios::fixed); + estream << static_cast(number_of_failures) / total_count + * 100.0; + restoreStreamFormatting(stream); + + estream << "% of " << total_count; + } + else + estream << "no"; + + estream << " attempts]\n"; + } + + break; + } + + /* + * Display the number of consistency check failures. + */ + + case 2 : + { + const TestStatistics& stats = final_statistics[algorithm]; + + estream << string(8 + indent, ' '); + + changeStreamFormatting(stream, 5, 0, ios::left); + estream << stats.consistency_check_failures; + restoreStreamFormatting(stream); + + estream << " ["; + + if (stats.consistency_checks_performed > 0) + { + changeStreamFormatting(stream, 0, 2, ios::fixed); + estream << static_cast(stats.consistency_check_failures) + / stats.consistency_checks_performed + * 100.0; + restoreStreamFormatting(stream); + + estream << "% of " << stats.consistency_checks_performed; + } + else + estream << "no"; + + estream << " checks performed]\n"; + + break; + } + } + } + estream << '\n'; + } + + estream << '\n'; + + if ((configuration.algorithms.size() > 1 + && configuration.global_options.do_comp_test) + || configuration.global_options.do_intr_test) + { + vector::size_type + number_of_algorithms = configuration.algorithms.size(); + + int legend; + + estream << ind + " Result inconsistency statistics\n" + + ind + " " + string(31, '=') + '\n'; + + vector::size_type + algorithm_x, algorithm_y; + + for (algorithm_x = 0; algorithm_x < number_of_algorithms; + algorithm_x += 2) + { + estream << '\n' + string(30 + indent, ' '); + + for (int i = 0; i < 2; ++i) + { + if (algorithm_x + i < number_of_algorithms) + { + algorithm_name = + (*(configuration.algorithms[algorithm_x + i].name)).substr(0, 20); + estream << "| " + algorithm_name + + string(21 - algorithm_name.length(), ' '); + } + } + estream << '|'; + + for (algorithm_y = 0; algorithm_y < number_of_algorithms; + algorithm_y++) + { + estream << "\n " + ind + string(26, '-'); + + for (int i = 0; i < 2; ++i) + { + if (algorithm_x + i < number_of_algorithms) + estream << '+' + string(22, '-'); + } + estream << '+'; + + algorithm_name + = (*(configuration.algorithms[algorithm_y].name)).substr(0, 20); + + bool algorithm_name_printed = false; + legend = 1; + + for (int data_type = 0; data_type < 3; ++data_type) + { + if ((data_type < 2 + && configuration.global_options.do_comp_test + && number_of_algorithms > 1 + && (data_type == 0 + || configuration.global_options.product_mode + == Configuration::GLOBAL)) + || (data_type == 2 + && configuration.global_options.do_intr_test)) + { + estream << "\n " + ind + + (!algorithm_name_printed + ? algorithm_name + + string(21 - algorithm_name.length(), ' ') + : string(21, ' ')) + + " [" + + toString(legend) + + "] "; + + algorithm_name_printed = true; + legend++; + + for (int i = 0; i < 2; ++i) + { + if (algorithm_x + i < number_of_algorithms) + { + estream << '|'; + printCollectiveCrossComparisonStats(stream, algorithm_y, + algorithm_x + i, + data_type); + } + } + estream << '|'; + } + } + } + + estream << '\n'; + } + + legend = 1; + if (number_of_algorithms > 1 && configuration.global_options.do_comp_test) + { + if (configuration.global_options.product_mode == Configuration::GLOBAL) + { + estream << '\n' + string(indent + 4, ' ') + + "[1] Model checking result cross-comparison failures\n" + + string(indent + 9, ' ') + + "(number of failures / number of global " + "cross-comparisons)\n"; + legend++; + } + + estream << '\n' + string(indent + 4, ' ') + '[' + toString(legend) + + "] Model checking result cross-comparison failures " + "(initial state only)\n" + + string(indent + 9, ' ') + + "(number of failures / number of cross-comparisons)\n"; + legend++; + } + + if (configuration.global_options.do_intr_test) + estream << '\n' + string(indent + 4, ' ') + '[' + toString(legend) + + "] Büchi automata intersection emptiness check " + "failures\n" + + string(indent + 9, ' ') + + "(number of failures / number of checks performed)"; + + estream << "\n\n"; + } + + estream.flush(); +} + +} diff --git a/lbtt/src/StatDisplay.h b/lbtt/src/StatDisplay.h new file mode 100644 index 000000000..83469a460 --- /dev/null +++ b/lbtt/src/StatDisplay.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef STATDISPLAY_H +#define STATDISPLAY_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include "Alloc.h" +#include "Configuration.h" +#include "TestStatistics.h" + +using namespace std; + +extern Configuration configuration; + + + +/****************************************************************************** + * + * Functions for displaying test statistics. + * + *****************************************************************************/ + +namespace StatDisplay +{ + +void printBuchiAutomatonStats /* Displays information */ + (ostream& stream, /* about a Büchi */ + int indent, /* automaton. */ + vector::size_type + algorithm, + int result_id); + +void printProductAutomatonStats /* Displays information */ + (ostream& stream, /* about a product */ + int indent, /* automaton. */ + vector::size_type + algorithm, + int result_id); + +void printAcceptanceCycleStats /* Displays information */ + (ostream& stream, /* about the acceptance */ + int indent, /* cycles of a product */ + vector::size_type + algorithm, + int result_id); + +void printConsistencyCheckStats /* Displays the result */ + (ostream& stream, /* of the consistency */ + int indent, /* check for a given */ + vector::size_type + algorithm); + +void printCrossComparisonStats /* Displays information */ + (ostream& stream, /* about the model */ + int indent, /* checking result */ + vector::size_type /* check. */ + algorithm); + +void printBuchiIntersectionCheckStats /* Displays the results */ + (ostream& stream, int indent, /* of the Büchi automata */ + vector::size_type /* emptiness checks. */ + algorithm); + +void printAllStats /* A shorthand for */ + (ostream& stream, /* showing all the */ + int indent, /* information displayed */ + vector::size_type /* functions. */ + algorithm); + +void printCollectiveCrossComparisonStats /* Displays a single */ + (ostream& stream, /* `cell' of the final */ + vector::size_type /* comparison table. */ + algorithm_y, + vector::size_type + algorithm_x, + int data_type); + +void printCollectiveStats /* Displays average test */ + (ostream& stream, int indent); /* data over all the + * test rounds + * performed so far. + */ +} + +#endif /* !STATDISPLAY_H */ diff --git a/lbtt/src/StateSpace.cc b/lbtt/src/StateSpace.cc new file mode 100644 index 000000000..90a155f3a --- /dev/null +++ b/lbtt/src/StateSpace.cc @@ -0,0 +1,459 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include "DispUtil.h" +#include "Exception.h" +#include "StateSpace.h" +#include "StringUtil.h" + +namespace Graph +{ + +/****************************************************************************** + * + * Function definitions for class StateSpace. + * + *****************************************************************************/ + +/* ========================================================================= */ +StateSpace::StateSpace + (const unsigned long int propositions_per_state, + const size_type initial_number_of_states) : + atoms_per_state(propositions_per_state), initial_state(0) +#ifdef HAVE_OBSTACK_H +, store() +#endif /* HAVE_OBSTACK_H */ +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class StateSpace. Initializes a state space + * with a given number of states and with a given number of + * atomic propositions per state. + * + * Arguments: propositions_per_state -- Atomic propositions per state. + * initial_number_of_states -- Initial size of the state space + * (can be grown later). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + expand(initial_number_of_states); +} + +/* ========================================================================= */ +StateSpace::StateSpace(const StateSpace& statespace) : + Graph(), atoms_per_state(statespace.atoms_per_state), + initial_state(statespace.initial_state) +#ifdef HAVE_OBSTACK_H +, store() +#endif /* HAVE_OBSTACK_H */ +/* ---------------------------------------------------------------------------- + * + * Description: Copy constructor for class StateSpace. Creates a copy of a + * StateSpace object. + * + * Argument: statespace -- StateSpace to be copied. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + expand(statespace.size()); + + for (size_type state = 0; state < size(); ++state) + { + for (GraphEdgeContainer::const_iterator transition + = statespace[state].edges().begin(); + transition != statespace[state].edges().end(); + ++transition) + connect(state, (*transition)->targetNode()); + + operator[](state).positiveAtoms().copy(statespace[state].positiveAtoms(), + atoms_per_state); + } +} + +/* ========================================================================= */ +StateSpace& StateSpace::operator=(const StateSpace& statespace) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class StateSpace. Assigns the + * contents of a state space to another one. + * + * Argument: statespace -- A reference to the constant StateSpace whose + * contents are to be copied. + * + * Returns: A reference to the StateSpace assigned to. + * + * ------------------------------------------------------------------------- */ +{ + if (&statespace != this) + { + clear(); + expand(statespace.size()); + atoms_per_state = statespace.atoms_per_state; + initial_state = statespace.initial_state; + + for (size_type state = 0; state < size(); ++state) + { + for (GraphEdgeContainer::const_iterator transition + = statespace[state].edges().begin(); + transition != statespace[state].edges().end(); + ++transition) + connect(state, (*transition)->targetNode()); + + operator[](state).positiveAtoms().copy(statespace[state].positiveAtoms(), + atoms_per_state); + } + } + + return *this; +} + +/* ========================================================================= */ +void StateSpace::clear() +/* ---------------------------------------------------------------------------- + * + * Description: Makes the automaton empty. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + atoms_per_state = 0; + initial_state = 0; + +#ifdef HAVE_OBSTACK_H + for (vector::iterator state = nodes.begin(); + state != nodes.end(); + ++state) + static_cast(*state)->~State(); + + if (!nodes.empty()) + { + store.free(*nodes.begin()); + nodes.clear(); + nodes.reserve(0); + } +#endif /* HAVE_OBSTACK_H */ + + Graph::clear(); +} + +/* ========================================================================= */ +StateSpace::size_type StateSpace::expand(size_type node_count) +/* ---------------------------------------------------------------------------- + * + * Description: Inserts a given number of states to a StateSpace. + * + * Argument: node_count -- Number of states to be inserted. + * + * Returns: The index of the last inserted state. + * + * ------------------------------------------------------------------------- */ +{ + nodes.reserve(nodes.size() + node_count); + + while (node_count > 0) + { +#ifdef HAVE_OBSTACK_H + void* state_storage = store.alloc(sizeof(State)); + State* new_state = new(state_storage) State(atoms_per_state); +#else + State* new_state = new State(atoms_per_state); +#endif /* HAVE_OBSTACK_H */ + + try + { + nodes.push_back(new_state); + } + catch (...) + { +#ifdef HAVE_OBSTACK_H + new_state->~State(); + store.free(state_storage); +#else + delete new_state; +#endif /* HAVE_OBSTACK_H */ + throw; + } + node_count--; + } + + return size() - 1; +} + +/* ========================================================================= */ +void StateSpace::connect(const size_type father, const size_type child) +/* ---------------------------------------------------------------------------- + * + * Description: Connects two states of the state space. + * + * Arguments: father, child -- Identifiers of two states. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Edge* edge = operator[](child).incoming_edge; + + if (edge != 0) + { + nodes[father]->outgoing_edges.insert(edge); + return; + } + +#ifdef HAVE_OBSTACK_H + void* edge_storage = store.alloc(sizeof(Edge)); + edge = new(edge_storage) Edge(child); +#else + edge = new Edge(child); +#endif /* HAVE_OBSTACK_H */ + + try + { + nodes[father]->outgoing_edges.insert(edge); + } + catch (...) + { +#ifdef HAVE_OBSTACK_H + edge->~Edge(); + store.free(edge_storage); +#else + delete edge; +#endif /* HAVE_OBSTACK_H */ + throw; + } + + operator[](child).incoming_edge = edge; +} + +/* ========================================================================= */ +void StateSpace::disconnect(const size_type father, const size_type child) +/* ---------------------------------------------------------------------------- + * + * Description: Disconnects two states of the state space. + * + * Arguments: father, child -- Identifiers for two states. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Edge e(child); + + /* + * Scan the set of `father''s outgoing transitions for a transition to the + * given target state and remove it if such a transition exists. + */ + + GraphEdgeContainer::iterator search_edge + = nodes[father]->outgoing_edges.find(&e); + + if (search_edge != nodes[father]->outgoing_edges.end()) + nodes[father]->outgoing_edges.erase(search_edge); +} + +/* ========================================================================= */ +void StateSpace::print + (ostream& stream, const int indent, const GraphOutputFormat fmt) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a StateSpace to a stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * fmt -- Determines the output format. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + if (fmt == DOT) + estream << string(indent, ' ') + "digraph G {\n"; + + if (nodes.empty()) + { + if (fmt == NORMAL) + estream << string(indent, ' ') + "The state space is empty.\n"; + } + else + { + if (fmt == NORMAL) + { + pair statistics = stats(); + pair reachable_part_statistics = + subgraphStats(initial_state); + + estream << string(indent, ' ') + "The state space consists of\n" + + string(indent + 4, ' ') + << statistics.first + << " states and\n" + string(indent + 4, ' ') + << statistics.second + << " transitions.\n" + string(indent, ' ') + + "The reachable part of the state space contains\n" + + string(indent + 4, ' ') + << reachable_part_statistics.first + << " states and\n" + string(indent + 4, ' ') + << reachable_part_statistics.second + << " transitions.\n" + string(indent, ' ') + "Initial state: " + << initial_state << '\n'; + } + + size_type s = nodes.size(); + for (size_type state = 0; state < s; ++state) + { + estream << string(indent, ' '); + if (fmt == NORMAL) + { + estream << "State " << state << ":\n"; + operator[](state).print(stream, indent + 4, NORMAL, atoms_per_state); + } + else if (fmt == DOT) + { + GraphEdgeContainer::const_iterator transition; + + estream << " n" << state << " ["; + if (state == 0) + estream << "style=filled,"; + estream << "shape=ellipse,label=\"" << state << ": "; + operator[](state).print(stream, 0, DOT, atoms_per_state); + estream << "\",fontsize=12];\n"; + + for (transition = nodes[state]->edges().begin(); + transition != nodes[state]->edges().end(); + ++transition) + { + estream << string(indent + 2, ' ') + 'n' << state; + (*transition)->print(stream, indent, fmt); + estream << ";\n"; + } + } + } + } + + if (fmt == DOT) + estream << string(indent, ' ') + "}\n"; + + estream.flush(); +} + + + +/****************************************************************************** + * + * Function definitions for class StateSpace::State. + * + *****************************************************************************/ + +/* ========================================================================= */ +void StateSpace::State::print + (ostream& stream, const int indent, const GraphOutputFormat fmt, + const unsigned long int number_of_atoms) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a state of a state space. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of + * output. + * fmt -- Determines the format of output. + * number_of_atoms -- Number of atoms associated with the + * state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + if (positive_atoms.count(number_of_atoms) == 0) + { + if (fmt == NORMAL) + estream << string(indent, ' ') + "No true propositions in this state.\n"; + else if (fmt == DOT) + estream << "{}"; + } + else + { + bool first_printed = false; + + if (fmt == NORMAL) + estream << string(indent, ' ') + "True propositions:\n"; + + string text = "{"; + + for (unsigned long int atom = 0; atom < number_of_atoms; ++atom) + { + if (positive_atoms[atom]) + { + if (first_printed) + text += ", "; + else + first_printed = true; + text += 'p' + ::StringUtil::toString(atom); + } + } + text += '}'; + if (fmt == NORMAL) + ::DispUtil::printTextBlock(stream, indent + 2, text, 78); + else + estream << text; + } + + if (fmt == NORMAL) + { + if (edges().empty()) + estream << string(indent, ' ') + "No transitions to other states.\n"; + else + { + bool first_printed = false; + estream << string(indent, ' ') + "Transitions to states\n"; + + string text = "{"; + + for (GraphEdgeContainer::const_iterator edge = edges().begin(); + edge != edges().end(); + ++edge) + { + if (first_printed) + text += ", "; + else + first_printed = true; + text += ::StringUtil::toString((*edge)->targetNode()); + } + text += '}'; + ::DispUtil::printTextBlock(stream, indent + 2, text, 78); + } + } + + estream.flush(); +} + +} diff --git a/lbtt/src/StateSpace.h b/lbtt/src/StateSpace.h new file mode 100644 index 000000000..fc356ba1b --- /dev/null +++ b/lbtt/src/StateSpace.h @@ -0,0 +1,454 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef STATESPACE_H +#define STATESPACE_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include "Alloc.h" +#include "BitArray.h" +#include "EdgeContainer.h" +#include "Graph.h" + +using namespace std; + +extern bool user_break; + +namespace Graph +{ + +/****************************************************************************** + * + * A class for representing state spaces. + * + *****************************************************************************/ + +class StateSpace : public Graph +{ +private: + unsigned long int atoms_per_state; /* Number of propositional + * variables per state in + * the state space. + */ + + size_type initial_state; /* Index of the initial + * state of the state + * space. + */ + +#ifdef HAVE_OBSTACK_H /* Storage for states */ + ObstackAllocator store; /* and transitions. */ +#endif /* HAVE_OBSTACK_H */ + +public: + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + class State : /* A class for */ + public Graph::Node /* representing the */ + { /* states of the state + * space. + */ + public: + explicit State /* Constructs a state */ + (const unsigned long int /* with a false truth */ + number_of_atoms = 0); /* assignment for the + * atomic propositions. + */ + + State /* Constructs a state */ + (const BitArray& atoms, /* from a given truth */ + const unsigned long int number_of_atoms); /* assignment. */ + + ~State(); /* Destructor. */ + + /* `edges' inherited from Graph::Node */ + + bool holds /* Test whether a given */ + (const unsigned long int atom, /* atomic proposition */ + const unsigned long int number_of_atoms) /* holds in the state. */ + const; + + const BitArray& positiveAtoms() const; /* Get or set the truth */ + BitArray& positiveAtoms(); /* assignment for the + * propositional atoms in + * a state. + */ + + void print /* Writes information */ + (ostream& stream, /* about the state to a */ + const int indent, /* stream. */ + const GraphOutputFormat fmt) const; + + void print /* Writes information */ + (ostream& stream, /* about the state to a */ + const int indent, /* stream. */ + const GraphOutputFormat fmt, + const unsigned long int number_of_atoms) + const; + + private: + friend class StateSpace; + + State(const State&); /* Prevent copying and */ + State& operator=(const State&); /* assignment of State + * objects. + */ + + BitArray positive_atoms; /* The set of propositions + * holding in the state. + */ + + Edge* incoming_edge; /* The unique edge pointing + * to `this' state. + */ + }; + + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + explicit StateSpace /* Constructor. */ + (const unsigned long int + propositions_per_state = 0, + const size_type initial_number_of_states = 0); + + StateSpace(const StateSpace& statespace); /* Copy constructor. */ + + ~StateSpace(); /* Destructor. */ + + StateSpace& operator= /* Assignment operator. */ + (const StateSpace& statespace); + + State& operator[](const size_type index) const; /* Indexing operator. No + * range checks are + * performed on the + * argument. + */ + + State& node(const size_type index) const; /* Synonym for the indexing + * operator. This function + * will also check the + * range of its argument. + */ + + /* `size' inherited from Graph */ + + /* `empty' inherited from Graph */ + + void clear(); /* Makes the state space + * empty. + */ + + size_type expand(size_type node_count = 1); /* Inserts states to the + * state space. + */ + + void connect /* Connects two states */ + (const size_type father, /* of the state space. */ + const size_type child); + + void disconnect /* Disconnects two */ + (const size_type father, /* states of the state */ + const size_type child); /* space. */ + + /* `connected' inherited from Graph */ + + /* `stats' inherited from Graph */ + + /* `subgraphStats' inherited from Graph */ + + unsigned long int numberOfPropositions() const; /* Get the number of atomic + * propositions associated + * with each state of the + * state space. + */ + + size_type initialState() const; /* Get or set the */ + size_type& initialState(); /* initial state of the + * state space. + */ + + void print /* Writes information */ + (ostream& stream = cout, /* about the state space */ + const int indent = 0, /* to a stream. */ + const GraphOutputFormat fmt = NORMAL) const; +}; + + + +/****************************************************************************** + * + * Inline function definitions for class StateSpace. + * + *************************************************************************** */ + +/* ========================================================================= */ +inline StateSpace::~StateSpace() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class StateSpace. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + clear(); +} + +/* ========================================================================= */ +inline StateSpace::State& StateSpace::operator[](const size_type index) const +/* ---------------------------------------------------------------------------- + * + * Description: Indexing operator for class StateSpace. This function can + * be used to refer to the individual states of the state space + * This function does not perform any range checks on its + * argument. + * + * Argument: index -- Index of a state. + * + * Returns: A reference to a state of the state space. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast(*nodes[index]); +} + +/* ========================================================================= */ +inline StateSpace::State& StateSpace::node(const size_type index) const +/* ---------------------------------------------------------------------------- + * + * Description: Function for referring to a single state of a StateSpace. + * This function also performs a range check on its argument. + * + * Argument: index -- Index of a state. + * + * Returns: A reference to a state of the state space. + * + * ------------------------------------------------------------------------- */ +{ + return static_cast(Graph::node(index)); +} + +/* ========================================================================= */ +inline unsigned long int StateSpace::numberOfPropositions() const +/* ---------------------------------------------------------------------------- + * + * Description: Tells the number of atomic propositions associated with each + * state of the state space. + * + * Arguments: None. + * + * Returns: Number of propositions associated with each state of the + * state space. + * + * ------------------------------------------------------------------------- */ +{ + return atoms_per_state; +} + +/* ========================================================================= */ +inline StateSpace::size_type StateSpace::initialState() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the number of the initial state of the StateSpace by + * value. + * + * Arguments: None. + * + * Returns: Index of the initial state of the state space. + * + * ------------------------------------------------------------------------- */ +{ + return initial_state; +} + +/* ========================================================================= */ +inline StateSpace::size_type& StateSpace::initialState() +/* ---------------------------------------------------------------------------- + * + * Description: Returns the number of the initial state of the StateSpace by + * reference. This function can therefore be used to change the + * initial state. + * + * Arguments: None. + * + * Returns: A reference to the index of the initial state. + * + * ------------------------------------------------------------------------- */ +{ + return initial_state; +} + + + +/****************************************************************************** + * + * Inline function definitions for class StateSpace::State. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline StateSpace::State::State(const unsigned long int number_of_atoms) : + Graph::Node(), positive_atoms(number_of_atoms), + incoming_edge(0) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class StateSpace::State. Creates a new state + * with a given number of atomic propositions, all of which are + * initially false. + * + * Argument: number_of_atoms -- Number of atomic propositions in the + * state. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + positive_atoms.clear(number_of_atoms); +} + +/* ========================================================================= */ +inline StateSpace::State::State + (const BitArray& atoms, const unsigned long int number_of_atoms) : + Graph::Node() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class StateSpace::State. Creates a new state, + * using a given truth assignment for propositional variables + * for initialization. + * + * Argument: atoms -- A truth assignment for atomic + * propositions. + * number_of_atoms -- Number of atomic propositions in the + * truth assignment. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + positive_atoms.copy(atoms, number_of_atoms); + incoming_edge = 0; +} + +/* ========================================================================= */ +inline StateSpace::State::~State() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class StateSpace::State. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (incoming_edge != 0) + { +#ifdef HAVE_OBSTACK_H + incoming_edge->~Edge(); +#else + delete incoming_edge; +#endif /* HAVE_OBSTACK_H */ + } + outgoing_edges.clear(); +} + +/* ========================================================================= */ +inline bool StateSpace::State::holds + (const unsigned long int atom, const unsigned long int number_of_atoms) const +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether a given proposition holds in a state. + * + * Arguments: atom -- Identifier of the proposition. + * number_of_atoms -- Number of atomic propositions associated + * with the state. + * + * Returns: Truth value of the proposition in the state. + * + * ------------------------------------------------------------------------- */ +{ + if (atom >= number_of_atoms) + return false; + + return positive_atoms[atom]; +} + +/* ========================================================================= */ +inline const BitArray& StateSpace::State::positiveAtoms() const +/* ---------------------------------------------------------------------------- + * + * Description: Returns the truth assignment for the propositions in a state. + * + * Arguments: None. + * + * Returns: A reference to the truth assignment, represented as a + * constant BitArray. + * + * ------------------------------------------------------------------------- */ +{ + return positive_atoms; +} + +/* ========================================================================= */ +inline BitArray& StateSpace::State::positiveAtoms() +/* ---------------------------------------------------------------------------- + * + * Description: Returns the truth assignment for the propositions in a state. + * This function can be also used to change the truth + * assignment. + * + * Arguments: None. + * + * Returns: A reference to the truth assignment, represented as a + * BitArray. + * + * ------------------------------------------------------------------------- */ +{ + return positive_atoms; +} + +/* ========================================================================= */ +inline void StateSpace::State::print + (ostream& stream, const int indent, const GraphOutputFormat fmt) const +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a state of a state space, assuming + * that there are no propositions associated with the state. + * [Note: This function is used to override the `print' function + * defined in the base class `Graph::node'.] + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * fmt -- Determines the format of output. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + this->print(stream, indent, fmt, 0); +} + +} + +#endif /* !STATESPACE_H */ diff --git a/lbtt/src/StateSpaceRandomizer.cc b/lbtt/src/StateSpaceRandomizer.cc new file mode 100644 index 000000000..550f418c4 --- /dev/null +++ b/lbtt/src/StateSpaceRandomizer.cc @@ -0,0 +1,291 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include +#include "Alloc.h" +#include "BitArray.h" +#include "Exception.h" +#include "StateSpaceRandomizer.h" + +namespace Graph +{ + +/****************************************************************************** + * + * Function definitions for class StateSpaceRandomizer. + * + *****************************************************************************/ + +/* ========================================================================= */ +void StateSpaceRandomizer::reset() +/* ---------------------------------------------------------------------------- + * + * Description: (Re)initializes the StateSpaceRandomizer object with default + * state space generation parameters. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + min_size = max_size = 20; + atoms_per_state = 5; + edge_probability = 0.2; + truth_probability = 0.5; +} + +/* ========================================================================= */ +StateSpace* StateSpaceRandomizer::generateGraph() const +/* ---------------------------------------------------------------------------- + * + * Description: Generates a random state space with the current graph + * generation parameters. + * + * Arguments: None. + * + * Returns: A pointer to a newly allocated StateSpace object. + * + * ------------------------------------------------------------------------- */ +{ + StateSpace::size_type size = chooseSize(); + + StateSpace* statespace = new StateSpace(atoms_per_state, size); + + statespace->initialState() = 0; + + if (size > 0) + { + try + { + bool has_successor; + + for (StateSpace::size_type i = 0; i < size; i++) + { + if (::user_break) + throw UserBreakException(); + + has_successor = false; + + for (StateSpace::size_type j = 0; j < size; j++) + { + if (DRAND() < edge_probability) + { + statespace->connect(i, j); + has_successor = true; + } + } + + if (!has_successor) + statespace->connect(i, LRAND(0, size)); + + for (unsigned long int j = 0; j < atoms_per_state; j++) + { + if (DRAND() < truth_probability) + (*statespace)[i].positiveAtoms().setBit(j); + } + } + } + catch (...) + { + delete statespace; + throw; + } + } + + return statespace; +} + +/* ========================================================================= */ +StateSpace* StateSpaceRandomizer::generateConnectedGraph() const +/* ---------------------------------------------------------------------------- + * + * Description: Initializes a random connected state space whose each state + * is reachable from its initial state 0. + * + * Arguments: None. + * + * Returns: A pointer to a newly allocated StateSpace object. + * + * ------------------------------------------------------------------------- */ +{ + StateSpace::size_type size = chooseSize(); + + StateSpace* statespace = new StateSpace(atoms_per_state, size); + + statespace->initialState() = 0; + + if (size > 0) + { + try + { + /* Random graph generation algorithm: + ---------------------------------- + + allocate number_of_states nodes; + + insert node 0 to the set of `reachable but unprocessed' nodes (nodes + that are reachable from the root node but whose children have not yet + been selected) + + while the set of `reachable but unprocessed' nodes in nonempty + { + select a (random) node X from the set; + + if there exists a node Y that is not yet reachable from root node + { + connect X to Y by making X the parent node of Y; + insert Y to the set of `reachable but unprocessed' nodes; + } + + for each node Z (excluding node Y) in the graph + { + randomly connect X to Z (making X the parent of Z); + + if Z was not previously reachable from the root node + insert Z to the set of `reachable but unprocessed' nodes; + } + + remove X from the set of `reachable but unprocessed' nodes; + } + + In the following implementation, the random truth value allocation + for propositions that hold in a state is interleaved with the + previous algorithm. + */ + + StateSpace::size_type first_unreachable_state = 1, random_node; + + multimap, + ALLOC(StateSpace::size_type) > + reachable_but_unprocessed; + + reachable_but_unprocessed.insert(make_pair(0, 0)); + + while (!reachable_but_unprocessed.empty()) + { + if (::user_break) + throw UserBreakException(); + + random_node = (*(reachable_but_unprocessed.begin())).second; + reachable_but_unprocessed.erase(reachable_but_unprocessed.begin()); + + for (StateSpace::size_type n = 0; n < first_unreachable_state; ++n) + { + if (DRAND() < edge_probability) + statespace->connect(random_node, n); + } + + if (first_unreachable_state < size) + { + statespace->connect(random_node, first_unreachable_state); + reachable_but_unprocessed.insert(make_pair(LRAND(0, LONG_MAX), + first_unreachable_state)); + ++first_unreachable_state; + + for (StateSpace::size_type i = first_unreachable_state; i < size; + ++i) + { + if (DRAND() < edge_probability) + { + statespace->connect(random_node, first_unreachable_state); + reachable_but_unprocessed.insert + (make_pair(LRAND(0, LONG_MAX), first_unreachable_state)); + ++first_unreachable_state; + } + } + } + + if ((*statespace)[random_node].edges().empty()) + statespace->connect(random_node, random_node); + + BitArray& atoms = (*statespace)[random_node].positiveAtoms(); + for (unsigned long int i = 0; i < atoms_per_state; ++i) + { + if (DRAND() < truth_probability) + atoms.setBit(i); + } + } + } + catch (...) + { + delete statespace; + throw; + } + } + + return statespace; +} + +/* ========================================================================= */ +StateSpace* StateSpaceRandomizer::generatePath() const +/* ---------------------------------------------------------------------------- + * + * Description: Generates a random Kripke structure that consists of a finite + * prefix of states that ends in a loop. The initial state of + * the generated structure will be at index 0. + * + * Arguments: None. + * + * Returns: A pointer to a newly allocated StateSpace object. + * + * ------------------------------------------------------------------------- */ +{ + StateSpace::size_type size = chooseSize(); + + StateSpace* statespace = new StateSpace(atoms_per_state, size); + + statespace->initialState() = 0; + + if (size > 0) + { + try + { + for (StateSpace::size_type i = 0; i + 1 < size; i++) + { + if (::user_break) + throw UserBreakException(); + + statespace->connect(i, i + 1); + for (unsigned long int j = 0; j < atoms_per_state; j++) + { + if (DRAND() < truth_probability) + (*statespace)[i].positiveAtoms().setBit(j); + } + } + + statespace->connect(size - 1, LRAND(0, size)); + } + catch (...) + { + delete statespace; + throw; + } + } + + return statespace; +} + +} diff --git a/lbtt/src/StateSpaceRandomizer.h b/lbtt/src/StateSpaceRandomizer.h new file mode 100644 index 000000000..03dd3ee07 --- /dev/null +++ b/lbtt/src/StateSpaceRandomizer.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef STATESPACERANDOMIZER_H +#define STATESPACERANDOMIZER_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include "Random.h" +#include "StateSpace.h" + +namespace Graph +{ + +/****************************************************************************** + * + * A class for generating random state spaces. + * + *****************************************************************************/ + +class StateSpaceRandomizer +{ +public: + StateSpaceRandomizer(); /* Constructor. */ + + ~StateSpaceRandomizer(); /* Destructor. */ + + StateSpace* generateGraph() const; /* Generates a random + * state space. + */ + + StateSpace* generateConnectedGraph() const; /* Generates a random + * state space whose all + * states are reachable + * from its initial state. + */ + + StateSpace* generatePath() const; /* Generates a random path. + */ + + void reset(); /* (Re)initializes the + * object with default + * state space generation + * parameters. + */ + + StateSpace::size_type min_size; /* Minimum size for the + * generated state spaces. + */ + + StateSpace::size_type max_size; /* Maximum size for the + * generated state spaces. + */ + + unsigned long int atoms_per_state; /* Number of atomic + * propositions associated + * with each state of the + * generated state spaces. + */ + + double edge_probability; /* Probability for adding + * random edges between + * state space states. + */ + + double truth_probability; /* Probability of assigning + * the value `true' for a + * proposition in a state. + */ + +private: + StateSpace::size_type chooseSize() const; /* Chooses a size for a + * state space to be + * generated. + */ + + StateSpaceRandomizer /* Prevent copying and */ + (const StateSpaceRandomizer&); /* assignment of */ + StateSpaceRandomizer& operator= /* StateSpaceRandomizer */ + (const StateSpaceRandomizer&); /* objects. */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class StateSpaceRandomizer. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline StateSpaceRandomizer::StateSpaceRandomizer() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class StateSpaceRandomizer. Initializes a new + * StateSpaceRandomizer object with default parameters. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + reset(); +} + +/* ========================================================================= */ +inline StateSpaceRandomizer::~StateSpaceRandomizer() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class StateSpaceRandomizer. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline StateSpace::size_type StateSpaceRandomizer::chooseSize() const +/* ---------------------------------------------------------------------------- + * + * Description: Chooses a random size for a state space to be generated. + * + * Arguments: None. + * + * Returns: A random integer in the interval + * [this->min_size, this->max_size]. + * + * ------------------------------------------------------------------------- */ +{ + return (min_size + LRAND(0, max_size - min_size + 1)); +} + +} + +#endif /* STATESPACERANDOMIZER_H */ diff --git a/lbtt/src/StringUtil.cc b/lbtt/src/StringUtil.cc new file mode 100644 index 000000000..e60b354fe --- /dev/null +++ b/lbtt/src/StringUtil.cc @@ -0,0 +1,227 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include "StringUtil.h" + +namespace StringUtil +{ + +/* ========================================================================= */ +string toString(const double d, const int precision, const ios::fmtflags flags) +/* ---------------------------------------------------------------------------- + * + * Description: Converts a double to a string with a given precision and + * format. The function defaults to fixed-point format with a + * precision of two decimals. + * + * Arguments: d -- The double to be converted. + * precision -- Precision. + * flags -- Formatting flags. + * + * Returns: The double as a string. + * + * ------------------------------------------------------------------------- */ +{ +#ifdef HAVE_SSTREAM + ostringstream stream; + stream.precision(precision); + stream.flags(flags); + stream << d; + return stream.str(); +#else + ostrstream stream; + stream.precision(precision); + stream.flags(flags); + stream << d << ends; + string result(stream.str()); + stream.freeze(0); + return result; +#endif /* HAVE_SSTREAM */ +} + +/* ========================================================================= */ +void sliceString + (const string& s, const char* slice_chars, + vector& slices) +/* ---------------------------------------------------------------------------- + * + * Description: Slices a string into a vector of strings, using a given set + * of characters as separators. + * + * Arguments: s -- A reference to the constant original string. + * slice_chars -- A C-style string containing the characters + * to be used as separators. + * slices -- A reference to a vector for storing the + * string components. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + string::size_type last_non_slicechar_pos = 0; + string::size_type last_slicechar_pos = 0; + + slices.clear(); + + do + { + last_non_slicechar_pos = + s.find_first_not_of(slice_chars, last_slicechar_pos); + if (last_non_slicechar_pos != s.npos) + { + last_slicechar_pos = s.find_first_of(slice_chars, + last_non_slicechar_pos); + if (last_slicechar_pos == s.npos) + slices.push_back(s.substr(last_non_slicechar_pos)); + else + slices.push_back(s.substr(last_non_slicechar_pos, + last_slicechar_pos + - last_non_slicechar_pos)); + } + } + while (last_non_slicechar_pos != s.npos && last_slicechar_pos != s.npos); +} + +/* ========================================================================= */ +unsigned long int parseNumber(const string& number_string) +/* ---------------------------------------------------------------------------- + * + * Description: Converts a string to an unsigned long integer. + * + * Argument: number_string -- A reference to a constant string. + * + * Returns: The number contained in the string, unless the string could + * not be converted to a number, in which case an exception is + * thrown. + * + * ------------------------------------------------------------------------- */ +{ + char* endptr; + unsigned long int number = strtoul(number_string.c_str(), &endptr, 10); + + if (*endptr != '\0') + throw NotANumberException("expected a nonnegative integer, got `" + + number_string + "'"); + + return number; +} + +/* ========================================================================= */ +void parseInterval + (const string& token, + set, ALLOC(unsigned long int) >& + number_set, + unsigned long int min, unsigned long int max) +/* ---------------------------------------------------------------------------- + * + * Description: Parses a string for a list of number intervals, storing all + * the numbers into a set. + * + * Arguments: token -- A reference to a constant string containing + * the list of intervals. A legal list of + * intervals has the following syntax: + * + * + * ::= + * | '*' // all numbers in the + * // interval + * // [min, max] + * | '-' // all numbers in the + * // interval + * // [min, number] + * | '-' // all numbers in the + * // interval + * // [number, max] + * | '-' + * | ',' + * + * number_set -- A reference to a set of unsigned long + * integers for storing the result. + * min -- Minimum bound for the numbers. + * max -- Maximum bound for the numbers. + * + * Note: `min' and `max' are only used to determine the limits + * for only partially defined intervals. The check that + * the explicitly specified values in the interval list + * are within these bounds is left to the caller when + * examining the result set. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + vector intervals; + + sliceString(token, ",", intervals); + string::size_type dash_pos; + + number_set.clear(); + + for (vector::const_iterator + interval = intervals.begin(); + interval != intervals.end(); + ++interval) + { + if (*interval == "*") + { + for (unsigned long int i = min; i <= max; i++) + number_set.insert(i); + break; + } + + dash_pos = (*interval).find_first_of("-"); + + if (dash_pos == (*interval).npos) + number_set.insert(parseNumber(*interval)); + else + { + if (dash_pos == 0) + { + unsigned long int number = parseNumber((*interval).substr(1)); + for (unsigned long int i = min; i <= number; i++) + number_set.insert(i); + } + else if (dash_pos == (*interval).length() - 1) + { + unsigned long int number = + parseNumber((*interval).substr(0, (*interval).length() - 1)); + for (unsigned long int i = number; i <= max; i++) + number_set.insert(i); + } + else + { + unsigned long int min_bound = + parseNumber((*interval).substr(0, dash_pos)); + unsigned long int max_bound = + parseNumber((*interval).substr(dash_pos + 1)); + + for (unsigned long int i = min_bound; i <= max_bound; i++) + number_set.insert(i); + } + } + } +} + +} diff --git a/lbtt/src/StringUtil.h b/lbtt/src/StringUtil.h new file mode 100644 index 000000000..8b1623352 --- /dev/null +++ b/lbtt/src/StringUtil.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef STRINGUTIL_H +#define STRINGUTIL_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#ifdef HAVE_SSTREAM +#include +#else +#include +#endif /* HAVE_SSTREAM */ +#include +#include "Alloc.h" +#include "Exception.h" + +using namespace std; + +/****************************************************************************** + * + * Miscellaneous routines extracting data from strings or converting data types + * to strings. + * + *****************************************************************************/ + +namespace StringUtil +{ + +string toString /* Function for */ + (const double d, /* converting a double */ + const int precision = 2, /* to a string. */ + const ios::fmtflags flags = ios::fixed); + +template string toString(const T& t); /* Template function for + * converting data types + * supporting stream output + * operations into strings. + */ + +void sliceString /* Breaks a string into */ + (const string& s, const char* slice_chars, /* `slices', using a */ + vector& slices); /* given set of + * characters as + * separators. + */ + +unsigned long int parseNumber /* Converts a string to */ + (const string& number_string); /* an unsigned long + * integer. + */ + +void parseInterval /* Converts a string of */ + (const string& token, /* number intervals to */ + set, /* the corresponding set */ + ALLOC(unsigned long int) >& number_set, /* of unsigned long */ + unsigned long int min, /* integers. */ + unsigned long int max); + + + +/****************************************************************************** + * + * Template function definitions in namespace StringUtil. + * + *****************************************************************************/ + +/* ========================================================================= */ +template +string toString(const T& t) +/* ---------------------------------------------------------------------------- + * + * Description: Converts any data type supporting stream output (the << + * operator) into a string. + * + * Arguments: t -- The object to be converted into a string. + * + * Returns: The object as a string. + * + * ------------------------------------------------------------------------- */ +{ +#ifdef HAVE_SSTREAM + ostringstream stream; + stream << t; + return stream.str(); +#else + ostrstream stream; + stream << t << ends; + string result(stream.str()); + stream.freeze(0); + return result; +#endif /* HAVE_SSTREAM */ +} + + + +/****************************************************************************** + * + * A class for reporting number conversion errors. + * + *****************************************************************************/ + +class NotANumberException : public Exception +{ +public: + NotANumberException /* Constructor. */ + (const string& message = "not a number"); + + /* default copy constructor */ + + ~NotANumberException() throw(); /* Destructor. */ + + NotANumberException& operator= /* Assignment operator. */ + (const NotANumberException& e); +}; + + + +/****************************************************************************** + * + * Inline function definitions for class NotANumberException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline NotANumberException::NotANumberException + (const string& message) : + Exception(message) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class NotANumberException. Creates an + * exception object and initializes it with an error message. + * + * Arguments: message -- A reference to a constant string containing the + * error message. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline NotANumberException::~NotANumberException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class NotANumberException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline NotANumberException& NotANumberException::operator= + (const NotANumberException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class NotANumberException. Copies + * the contents of an exception object to another. + * + * Arguments: e -- A reference to a constant NotANumberException. + * + * Returns: A reference to the object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + +} + +#endif /* !STRINGUTIL_H */ diff --git a/lbtt/src/TestOperations.cc b/lbtt/src/TestOperations.cc new file mode 100644 index 000000000..790237858 --- /dev/null +++ b/lbtt/src/TestOperations.cc @@ -0,0 +1,1668 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include "BitArray.h" +#include "BuchiAutomaton.h" +#include "DispUtil.h" +#include "LtlFormula.h" +#include "PathEvaluator.h" +#include "ProductAutomaton.h" +#include "Random.h" +#include "SccIterator.h" +#include "SharedTestData.h" +#include "PathIterator.h" +#include "StateSpace.h" +#include "StatDisplay.h" +#include "StringUtil.h" +#include "TestOperations.h" +#include "TestRoundInfo.h" +#include "TestStatistics.h" + +/****************************************************************************** + * + * Implementations for the operations used in the main test loop. + * + *****************************************************************************/ + +namespace TestOperations +{ + +using namespace ::SharedTestData; +using namespace ::StatDisplay; +using namespace ::StringUtil; +using namespace ::DispUtil; + +/* ========================================================================= */ +void openFile + (const char* filename, ifstream& stream, ios::openmode mode, int indent) +/* ---------------------------------------------------------------------------- + * + * Description: Function for opening a file for input. + * + * Arguments: filename -- A pointer to a constant C-style string + * containing the name of the file to be opened. + * stream -- A reference to the input stream that should be + * associated with the file. + * mode -- A constant of type `ios::openmode' determining + * the open mode. + * indent -- Number of spaces to leave to the left of + * messages given to the user. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + printText(string("", 5, indent); + + stream.open(filename, mode); + + if (!stream.good()) + { + printText(" error\n", 5); + throw FileOpenException(string("`") + filename + "'"); + } + else + printText(" ok\n", 5); +} + +/* ========================================================================= */ +void openFile + (const char* filename, ofstream& stream, ios::openmode mode, int indent) +/* ---------------------------------------------------------------------------- + * + * Description: Function for opening a file for output. + * + * Arguments: filename -- A pointer to a constant C-style string with + * the name of the file to be opened. + * stream -- A reference to the output stream that should be + * associated with the file. + * mode -- A constant of type `ios::openmode' determining + * the open mode. + * indent -- Number of spaces to leave to the left of + * messages given to the user. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + printText(string("<") + (mode & ios::trunc ? "creat" : "open") + "ing `" + + filename + "'>", + 5, + indent); + + stream.open(filename, mode); + + if (!stream.good()) + { + printText(" error\n", 5); + + if (mode & ios::trunc) + throw FileCreationException(string("`") + filename + "'"); + else + throw FileOpenException(string("`") + filename + "'"); + } + else + printText(" ok\n", 5); +} + +/* ========================================================================= */ +void removeFile(const char* filename, int indent) +/* ---------------------------------------------------------------------------- + * + * Description: Removes a file. + * + * Arguments: filename -- A pointer to a constant C-style string with + * the name of the file to be removed. + * indent -- Number of spaces to leave to the left of + * messages given to the user. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + printText(string("", 5, indent); + + if (remove(filename) == 0) + printText(" ok\n", 5); + else + printText(" error\n", 5); +} + +/* ========================================================================= */ +void printFileContents + (ostream& stream, const char* message, const char* filename, int indent, + const char* line_prefix) +/* ---------------------------------------------------------------------------- + * + * Description: Outputs the contents of a file into a stream. + * + * Arguments: stream -- A reference to the output stream into which + * the file contents should be outputted. + * message -- A pointer to a constant C-style string + * containing a message to be outputted to the + * stream before the contents of the file. (To + * display only the file contents, use a null + * pointer for the message; an empty string + * results in an empty line to be outputted + * before the file contents.) + * filename -- A pointer to a constant C-style string + * containing the name of the input file. + * indent -- Number of spaces to leave to the left of + * output. + * line_prefix -- A pointer to a constant C-style string + * that will be prepended to each outputted + * line. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + ifstream file; + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + openFile(filename, file, ios::in, indent); + + bool first_line_printed = false; + string message_line; + + printText(string("\n", 5, indent); + + while (file.good()) + { + message_line = ""; + + getline(file, message_line, '\n'); + + if (!file.eof()) + { + if (!first_line_printed && message != 0) + { + first_line_printed = true; + estream << string(indent, ' ') + message + '\n'; + } + + estream << string(indent, ' ') + line_prefix + message_line + '\n'; + } + } + + estream.flush(); + + file.close(); +} + +/* ========================================================================= */ +void writeToTranscript(const string& message, bool show_formula_in_header) +/* ---------------------------------------------------------------------------- + * + * Description: Writes a message into the transcript file. (If this is the + * first message to be written to the file in the current test + * round, the message is preceded with a header showing the + * round number and the LTL formulae used in the round.) + * + * Argument: message -- A message to be written to the + * file. + * show_formula_in_header -- If false, prevents displaying + * an LTL formula in the error + * report header (e.g., when it is + * the formula generation that + * failed). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (round_info.error_report_round != round_info.current_round) + { + round_info.error_report_round = round_info.current_round; + + const string roundstring = "Round " + toString(round_info.current_round); + + round_info.transcript_file << roundstring + '\n' + + string(roundstring.length(), '-') + + "\n\n"; + + if (show_formula_in_header) + { + const int formula + = (configuration.formula_options.output_mode == Configuration::NNF + ? 0 + : 2); + + try + { + round_info.transcript_file << " Formula (+): "; + round_info.formulae[formula]->print(round_info.transcript_file); + round_info.transcript_file << "\n Negated (-): "; + round_info.formulae[formula + 1]->print(round_info.transcript_file); + round_info.transcript_file << endl << endl; + } + catch (const IOException&) + { + } + } + } + + round_info.transcript_file << " " + message + '\n'; + round_info.transcript_file.flush(); +} + +/* ========================================================================= */ +void generateStateSpace() +/* ---------------------------------------------------------------------------- + * + * Description: Generates a random state space. The global variable + * `configuration.global_options.statespace_generation_mode' + * determines the type of state space to be generated. + * + * Arguments: None. + * + * Returns: Nothing. The result can be accessed through + * `round_info.statespace'. + * + * ------------------------------------------------------------------------- */ +{ + using ::Graph::StateSpace; + + if (configuration.global_options.statespace_generation_mode + == Configuration::ENUMERATEDPATH) + { + StateSpace::size_type current_size + = configuration.statespace_generator.min_size; + + /* + * If the state spaces are enumerated paths, generate the next path. + */ + + printText("Generating next state space\n", 2, 4); + + if (round_info.path_iterator != 0) + { + current_size = (*round_info.path_iterator)->size(); + ++(*round_info.path_iterator); + if (round_info.path_iterator->atEnd()) + { + delete round_info.path_iterator; + round_info.path_iterator = 0; + + if (current_size < configuration.statespace_generator.max_size) + { + current_size++; + printText("[Increasing state space size to " + toString(current_size) + + "]\n", + 2, + 6); + } + else + { + current_size = configuration.statespace_generator.min_size; + printText("[All state spaces have been enumerated. Staring over]\n", + 2, + 6); + } + } + } + + if (round_info.path_iterator == 0) + { + round_info.path_iterator + = new Graph::PathIterator + (configuration.statespace_generator.atoms_per_state, + current_size); + } + + printText("\n", 2); + + round_info.statespace = &(**round_info.path_iterator); + } + else + { + /* + * Otherwise generate a random state space. + */ + + if (round_info.statespace != 0) + { + delete round_info.statespace; + round_info.statespace = 0; + } + + if (printText("Generating random state space\n", 2, 4)) + printText("", 4, 6); + + /* + * Determine the type of the state space to be generated according to the + * configuration options, then generate it. + */ + + StateSpace* statespace; + + try + { + switch (configuration.global_options.statespace_generation_mode) + { + case Configuration::RANDOMGRAPH : + statespace = configuration.statespace_generator.generateGraph(); + break; + + case Configuration::RANDOMCONNECTEDGRAPH : + statespace = configuration.statespace_generator. + generateConnectedGraph(); + break; + + default : /* Configuration::RANDOMPATH */ + statespace = configuration.statespace_generator.generatePath(); + break; + } + } + catch (const UserBreakException&) + { + printText(" user break\n\n", 4); + + if (round_info.transcript_file.is_open()) + writeToTranscript("User break while generating state space. No tests " + "performed.\n", false); + + throw; + } + catch (const bad_alloc&) + { + if (!printText(" out of memory\n\n", 4)) + printText("[Out of memory]\n\n", 2, 6); + + if (round_info.transcript_file.is_open()) + writeToTranscript("Out of memory while generating state space. No " + "tests performed.\n", false); + + throw StateSpaceGenerationException(); + } + + round_info.statespace = statespace; + + printText(" ok\n", 4); + + if (configuration.statespace_generator.max_size + > configuration.statespace_generator.min_size) + printText("number of states: " + + toString(round_info.statespace->size()) + + '\n', + 3, + 6); + + printText("\n", 2); + } + + round_info.num_generated_statespaces++; + + pair + statespace_stats(round_info.statespace->stats()); + + round_info.total_statespace_states += statespace_stats.first; + round_info.total_statespace_transitions += statespace_stats.second; + + round_info.real_emptiness_check_size + = (configuration.global_options.product_mode == Configuration::GLOBAL + ? round_info.statespace->size() + : 1); +} + +/* ========================================================================= */ +void generateFormulae(istream* formula_input_stream) +/* ---------------------------------------------------------------------------- + * + * Description: Generates a random LTL formula according to the current + * configuration and stores the formula, its negation and their + * negated normal forms into `round_info.formulae'. + * + * Argument: formula_input_stream -- A pointer to an input stream. If + * the pointer is nonzero, no random + * formula will be generated; instead, + * a formula will be read from the + * stream. + * + * Returns: Nothing. The generated formulae can be found in the global + * array `round_info.formulae'. + * + * ------------------------------------------------------------------------- */ +{ + for (int f = 0; f < 4; f++) + { + if (round_info.formulae[f] != 0) + { + ::Ltl::LtlFormula::destruct(round_info.formulae[f]); + round_info.formulae[f] = static_cast(0); + } + } + + if (printText(string(formula_input_stream == 0 ? "Random " : "") + + "LTL formula:\n", + 3, + 4)) + printText(string("<") + (formula_input_stream == 0 ? "generat" : "read") + + "ing>", + 4, + 6); + else + printText(string(formula_input_stream == 0 + ? "Generating random" + : "Reading") + + " LTL formula\n", + 2, + 4); + + if (formula_input_stream != 0) + { + /* + * If a valid pointer to a stream was given as a parameter, try to read a + * formula from the stream. + */ + + try + { + try + { + round_info.formulae[2] + = ::Ltl::LtlFormula::read(*formula_input_stream); + } + catch (...) + { + printText(" error\n", 4); + throw; + } + } + catch (const ::Ltl::LtlFormula::ParseErrorException&) + { + printText(string("[Error parsing the ") + + (formula_input_stream == &round_info.formula_input_file + ? "formula file" + : "input formula") + + ". Aborting]\n", + 2, + 6); + + if (round_info.transcript_file.is_open()) + writeToTranscript("Error parsing input formula. Testing aborted.\n", + false); + + throw FormulaGenerationException(); + } + catch (const IOException&) + { + bool fatal_io_error + = (configuration.global_options.formula_input_filename.empty() + || !round_info.formula_input_file.eof()); + + printText(string("[") + (fatal_io_error + ? "Error reading formula" + : "No more input formulae") + + ". Aborting]\n", + 2, + 6); + + if (round_info.transcript_file.is_open()) + writeToTranscript(fatal_io_error + ? "Error reading input formula. Testing " + "aborted.\n" + : "No more input formulae. Testing aborted.\n", + false); + + throw FormulaGenerationException(); + } + + printText(" ok\n", 4); + } + else + { + /* + * Otherwise generate a random formula. + */ + + try + { + round_info.formulae[2] + = configuration.formula_options.formula_generator.generate(); + } + catch (...) + { + printText(" error\n", 4); + throw; + } + + printText(" ok\n", 4); + + if (configuration.formula_options.formula_generator.max_size + > configuration.formula_options.formula_generator.size) + printText("parse tree size:" + string(11, ' ') + + toString(round_info.formulae[2]->size()) + '\n', + 3, + 6); + } + + ++round_info.num_processed_formulae; + + printText("", 4, 6); + + round_info.formulae[0] = round_info.formulae[2]->nnfClone(); + + if (printText(" ok\n", 4)) + printText("", 4, 6); + + round_info.formulae[3] = &(::Ltl::Not::construct(*round_info.formulae[2])); + + if (printText(" ok\n", 4)) + printText("", 4, 6); + + round_info.formulae[1] = round_info.formulae[3]->nnfClone(); + + if (configuration.global_options.verbosity >= 3) + { + printText(" ok\n", 4); + + for (int f = 0; f <= 1; f++) + { + round_info.cout << string(6, ' ') + (f == 0 ? "" : "negated ") + + "formula:" + string(19 - 8 * f, ' '); + round_info.formulae[f + 2]->print(round_info.cout); + round_info.cout << '\n'; + + if (configuration.formula_options.output_mode == Configuration::NNF) + { + round_info.cout << string(8, ' ') + "in negation normal form: "; + round_info.formulae[f]->print(round_info.cout); + round_info.cout << '\n'; + } + } + } + + printText("\n", 2); +} + +/* ========================================================================= */ +void verifyFormulaOnPath() +/* ---------------------------------------------------------------------------- + * + * Description: Model checks an LTL formula (accessed through the global + * data structure `round_info' on a path directly and stores the + * result into the test result data structure. + * + * Arguments: None. + * + * Returns: Nothing. The model checking results are stored into the test + * result data structure. + * + * ------------------------------------------------------------------------- */ +{ + if (printText("Model checking formula using internal algorithm\n", 2, 4)) + printText("", 4, 6); + + test_results[round_info.number_of_translators].automaton_stats[0]. + emptiness_check_result.clear(); + test_results[round_info.number_of_translators].automaton_stats[1]. + emptiness_check_result.clear(); + + try + { + int formula = (configuration.formula_options.output_mode + == Configuration::NNF + ? 0 + : 2); + + Ltl::PathEvaluator path_evaluator; + path_evaluator.evaluate(*round_info.formulae[formula], + *(round_info.statespace)); + + for (unsigned long int s = 0; s < round_info.real_emptiness_check_size; + s++) + { + if (path_evaluator.getResult(s)) + test_results[round_info.number_of_translators].automaton_stats[0]. + emptiness_check_result.setBit(s); + else + test_results[round_info.number_of_translators].automaton_stats[1]. + emptiness_check_result.setBit(s); + } + } + catch (const UserBreakException&) + { + if (!printText(" user break\n\n", 4)) + printText("[User break]\n\n", 2, 6); + + if (round_info.transcript_file.is_open()) + writeToTranscript("User break while model checking formulas. No tests " + "performed.\n"); + + throw; + } + catch (const bad_alloc&) + { + if (!printText(" out of memory\n\n", 4)) + printText("[Out of memory]\n\n", 2, 6); + + if (round_info.transcript_file.is_open()) + writeToTranscript("Out of memory while model checking formulas. No " + "tests performed.\n"); + + round_info.error = true; + return; + } + + printText(" ok\n", 4); + printText("\n", 2); + + test_results[round_info.number_of_translators].automaton_stats[0]. + emptiness_check_performed = true; + test_results[round_info.number_of_translators].automaton_stats[1]. + emptiness_check_performed = true; +} + +/* ========================================================================= */ +void writeFormulaeToFiles() +/* ---------------------------------------------------------------------------- + * + * Description: Writes the LTL formulae used in the tests into files in the + * output mode specified in the program configuration. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + ofstream formula_file; + + for (int f = 0; f < 2; f++) + { + Exceptional_ostream eformula_file(&formula_file, + ios::failbit | ios::badbit); + + if (!round_info.formula_in_file[f]) + { + printText(string("\n", + 5, + 6); + + try + { + openFile(round_info.formula_file_name[f], formula_file, + ios::out | ios::trunc, 6); + + round_info.formulae[configuration.formula_options. + output_mode == Configuration::NNF + ? f + : f + 2]->print(eformula_file, ::Ltl::LTL_PREFIX); + + eformula_file << '\n'; + + formula_file.close(); + round_info.formula_in_file[f] = true; + } + catch (const IOException& e) + { + if (formula_file.is_open()) + formula_file.close(); + + printText(string("Error: ") + e.what() + "\n\n", 2, 6); + + round_info.error = true; + return; + } + } + } +} + +/* ========================================================================= */ +void generateBuchiAutomaton + (int f, + vector::size_type algorithm_id) +/* ---------------------------------------------------------------------------- + * + * Description: Constructs a BuchiAutomaton by invoking an external program + * that will perform the conversion of a LTL formula (stored + * into a file) into a Büchi automaton. + * + * Arguments: f -- Indicates the formula to be converted into + * an automaton. 0 corresponds to the positive + * and 1 to the negated formula. + * algorithm_id -- Identifier of the LTL-to-Büchi translator + * to use. + * + * Returns: Nothing. The result is stored in + * `test_results[algorithm_id].automaton_stats[f]. + * buchi_automaton'. + * + * ------------------------------------------------------------------------- */ +{ + using ::Graph::BuchiAutomaton; + + AutomatonStats& automaton_stats + = test_results[algorithm_id].automaton_stats[f]; + + if (automaton_stats.buchiAutomatonComputed()) + printText("Büchi automaton (cached):\n", 2, 8); + else + { + if (!printText("Büchi automaton:\n", 3, 8)) + printText("Computing Büchi automaton\n", 2, 8); + + const Configuration::AlgorithmInformation& algorithm + = configuration.algorithms[algorithm_id]; + + final_statistics[algorithm_id].buchi_automaton_count[f]++; + + BuchiAutomaton* buchi_automaton = new BuchiAutomaton(); + + struct tms timing_information_begin, timing_information_end; + + int exitcode; + string command_line; + + try + { + /* + * Generate the command line to be used for executing the program that + * is supposed to generate the automaton from a formula. The name of the + * executable and any extra command line parameters are obtained from + * the program configuration data structures. The command line will be + * of the form + * [path to executable] [additional parameters] [input file] + * [output file] 1>[stdout capture file] + * 2>[stderr capture file], + * so the program used for generating the automaton is assumed to follow + * this format for passing the necessary input and output filenames. + * Note: This kind of output redirection requires that the call to + * `system' invokes a POSIX compatible shell! + * + * The output of stdout and stderr will be redirected to two temporary + * files. + */ + + command_line = *(algorithm.path_to_program) + ' '; + + if (algorithm.extra_parameters != 0) + command_line += *(algorithm.extra_parameters) + ' '; + + command_line += string(round_info.formula_file_name[f]) + + ' ' + round_info.automaton_file_name + + " 1>" + round_info.cout_capture_file + + " 2>" + round_info.cerr_capture_file; + + if (!printText("", 5, 10)) + printText("", 4, 10); + + /* + * Execute the translator and record its running time (user time) into + * `automaton_stats.buchi_generation_time'. (The variable will have a + * negative value if the time could not be determined.) + */ + + automaton_stats.buchi_generation_time = -1.0; + + times(&timing_information_begin); + + exitcode = system(command_line.c_str()); + + times(&timing_information_end); + + if (timing_information_begin.tms_utime != static_cast(-1) + && timing_information_begin.tms_cutime != static_cast(-1) + && timing_information_end.tms_utime != static_cast(-1) + && timing_information_end.tms_cutime != static_cast(-1)) + automaton_stats.buchi_generation_time + = static_cast(timing_information_end.tms_utime + + timing_information_end.tms_cutime + - timing_information_begin.tms_utime + - timing_information_begin.tms_cutime) + / sysconf(_SC_CLK_TCK); + + /* + * Nonzero exit codes from the external program are interpreted as + * errors. In this case, throw an exception indicating that the program + * execution failed. + */ + + if (exitcode != 0) + { + ExecFailedException e; + e.changeMessage("Execution of `" + *(algorithm.path_to_program) + + "' failed" + + (automaton_stats.buchi_generation_time >= 0.0 + ? " (" + + toString(automaton_stats.buchi_generation_time, + 2) + + " seconds elapsed)" + : string("")) + + " with exit status " + toString(exitcode)); + throw e; + } + + printText(" ok\n", 5); + + /* + * Read the automaton description into memory from the result file. + */ + + ifstream automaton_file; + openFile(round_info.automaton_file_name, automaton_file, ios::in, 10); + + printText("", 5, 10); + + automaton_file >> *buchi_automaton; + + printText(" ok\n", 4); + + automaton_file.close(); + + automaton_stats.buchi_automaton = buchi_automaton; + } + catch (...) + { + delete buchi_automaton; + + printText(" error\n", 4); + printText("Error", 2, 10); + + if (round_info.transcript_file.is_open()) + { + writeToTranscript("Büchi automaton generation failed (" + + configuration.algorithmString(algorithm_id) + + ", " + + (f == 0 ? "posi" : "nega") + + "tive formula)"); + + if (automaton_stats.buchi_generation_time >= 0.0) + round_info.transcript_file << string(8, ' ') + "Elapsed time: " + + toString(automaton_stats. + buchi_generation_time, + 2) + + " seconds (user time)\n"; + } + + try + { + throw; + } + catch (const ExecFailedException& e) + { + printText(string(": ") + e.what(), 2); + if (round_info.transcript_file.is_open()) + round_info.transcript_file << string(8, ' ') + + "Program execution failed with exit " + "status " + + toString(exitcode); + } + catch (const BuchiAutomaton::AutomatonParseException& e) + { + printText(string(" parsing input: ") + e.what(), 2); + if (round_info.transcript_file.is_open()) + round_info.transcript_file << string(8, ' ') + + "Error reading automaton: " + + e.what(); + } + catch (const Exception& e) + { + printText(string(": ") + e.what(), 2); + if (round_info.transcript_file.is_open()) + round_info.transcript_file << string(8, ' ') + + "lbtt internal error: " + + e.what(); + } + catch (const bad_alloc&) + { + printText(": out of memory", 2); + if (round_info.transcript_file.is_open()) + round_info.transcript_file << string(8, ' ') + + "Out of memory while reading " + "automaton"; + } + + printText("\n", 2); + + if (round_info.transcript_file.is_open()) + round_info.transcript_file << '\n'; + + removeFile(round_info.automaton_file_name, 10); + + try + { + const char* msg = "Contents of stdout"; + + if (configuration.global_options.verbosity >= 3) + printFileContents(cout, msg, round_info.cout_capture_file, 10, "> "); + if (round_info.transcript_file.is_open()) + printFileContents(round_info.transcript_file, msg, + round_info.cout_capture_file, 8, "> "); + } + catch (const IOException&) + { + } + + try + { + const char* msg = "Contents of stderr:"; + + if (configuration.global_options.verbosity >= 3) + printFileContents(cout, msg, round_info.cerr_capture_file, 10, "> "); + if (round_info.transcript_file.is_open()) + printFileContents(round_info.transcript_file, msg, + round_info.cerr_capture_file, 8, "> "); + } + catch (const IOException&) + { + } + + if (round_info.transcript_file.is_open()) + round_info.transcript_file << '\n'; + + removeFile(round_info.cout_capture_file, 10); + removeFile(round_info.cerr_capture_file, 10); + + throw BuchiAutomatonGenerationException(); + } + + removeFile(round_info.automaton_file_name, 10); + + if (configuration.global_options.verbosity >= 3) + { + printFileContents(cout, "Contents of stdout:", + round_info.cout_capture_file, 10, "> "); + printFileContents(cout, "Contents of stderr:", + round_info.cerr_capture_file, 10, "> "); + } + + removeFile(round_info.cout_capture_file, 10); + removeFile(round_info.cerr_capture_file, 10); + + printText("", 4, 10); + + pair buchi_stats + = automaton_stats.buchi_automaton->stats(); + + automaton_stats.number_of_buchi_states = buchi_stats.first; + automaton_stats.number_of_buchi_transitions = buchi_stats.second; + automaton_stats.number_of_acceptance_sets + = automaton_stats.buchi_automaton->numberOfAcceptanceSets(); + + /* + * Update Büchi automaton statistics for the given algorithm. + */ + + final_statistics[algorithm_id].total_number_of_buchi_states[f] + += automaton_stats.number_of_buchi_states; + final_statistics[algorithm_id].total_number_of_buchi_transitions[f] + += automaton_stats.number_of_buchi_transitions; + final_statistics[algorithm_id].total_number_of_acceptance_sets[f] + += automaton_stats.number_of_acceptance_sets; + + if (final_statistics[algorithm_id].total_buchi_generation_time[f] < 0.0 + || automaton_stats.buchi_generation_time < 0.0) + final_statistics[algorithm_id].total_buchi_generation_time[f] = -1.0; + else + final_statistics[algorithm_id].total_buchi_generation_time[f] + += automaton_stats.buchi_generation_time; + + printText(" ok\n", 4); + } + + if (configuration.global_options.verbosity >= 3) + printBuchiAutomatonStats(cout, 10, algorithm_id, f); +} + +/* ========================================================================= */ +void generateProductAutomaton + (int f, + vector::size_type algorithm_id) +/* ---------------------------------------------------------------------------- + * + * Description: Computes the product of a Büchi automaton with a state + * space. + * + * Arguments: f -- Indicates the Büchi automaton with which + * the product should be computed. 0 + * corresponds to the automaton obtained from + * the positive, 1 corresponds to the one + * obtained from the negated formula. + * algorithm_id -- Identifier of the LTL-to-Büchi translator + * used for generating the Büchi automata. + * + * Returns: Nothing. The result is stored in + * 'round_info.product_automaton'. + * + * ------------------------------------------------------------------------- */ +{ + using ::Graph::ProductAutomaton; + + AutomatonStats& automaton_stats + = test_results[algorithm_id].automaton_stats[f]; + + if (automaton_stats.emptiness_check_performed) + return; + + if (printText("Product automaton:\n", 3, 8)) + printText("", 4, 10); + else + printText("Computing product automaton\n", 2, 8); + + final_statistics[algorithm_id].product_automaton_count[f]++; + + ProductAutomaton* product_automaton = 0; + + /* + * Initialize the product automaton. The variable + * `configuration.global_options.product_mode' is used to determine whether + * the product is to be computed with respect to all the states of the + * state space or only the initial state of the state space. + */ + + try + { + product_automaton = new ProductAutomaton(); + product_automaton->computeProduct + (*automaton_stats.buchi_automaton, *round_info.statespace, + configuration.global_options.product_mode == Configuration::GLOBAL); + } + catch (const ProductAutomaton::ProductSizeException&) + { + if (!printText(" error (product may be too large)\n\n", 4)) + printText("[Product may be too large]\n\n", 2, 10); + + if (round_info.transcript_file.is_open()) + writeToTranscript("Product automaton generation aborted (" + + configuration.algorithmString(algorithm_id) + + ", " + + (f == 0 ? "posi" : "nega") + "tive formula)" + + ". Product may be too large.\n"); + + delete product_automaton; + + throw ProductAutomatonGenerationException(); + } + catch (const UserBreakException&) + { + if (!printText(" user break\n\n", 4)) + printText("[User break]\n\n", 2, 10); + + if (round_info.transcript_file.is_open()) + writeToTranscript("User break while generating product automaton (" + + configuration.algorithmString(algorithm_id) + + ", " + + (f == 0 ? "posi" : "nega") + "tive formula)\n"); + + delete product_automaton; + + throw; + } + catch (const bad_alloc&) + { + if (product_automaton != 0) + delete product_automaton; + + if (!printText(" out of memory\n", 4)) + printText("[Out of memory]\n", 2, 10); + + if (round_info.transcript_file.is_open()) + writeToTranscript("Out of memory while generating product " + "automaton (" + + configuration.algorithmString(algorithm_id) + + ", " + + (f == 0 ? "posi" : "nega") + "tive formula)\n"); + + throw ProductAutomatonGenerationException(); + } + + round_info.product_automaton = product_automaton; + + /* + * Determine the number of states and transitions in the product. + */ + + if (printText(" ok\n", 4)) + printText("", 4, 10); + + pair product_stats = + product_automaton->stats(); + + automaton_stats.number_of_product_states = product_stats.first; + automaton_stats.number_of_product_transitions = product_stats.second; + + /* + * Update product automaton statistics for the given algorithm. + */ + + final_statistics[algorithm_id].total_number_of_product_states[f] + += automaton_stats.number_of_product_states; + final_statistics[algorithm_id].total_number_of_product_transitions[f] + += automaton_stats.number_of_product_transitions; + + printText(" ok\n", 4); + + if (configuration.global_options.verbosity >= 3) + printProductAutomatonStats(cout, 10, algorithm_id, f); +} + +/* ========================================================================= */ +void performEmptinessCheck + (int f, + vector::size_type + algorithm_id) +/* ---------------------------------------------------------------------------- + * + * Description: Performs the emptiness check on a ProductAutomaton, i.e., + * finds the states of the original state space from which an + * accepting cycle of the Büchi automaton can be reached. + * + * Arguments: f -- Indicates the formula originally used for + * constructing the given product automaton. + * 0 corresponds to the automaton obtained + * from the positive, 1 to the one obtained + * from the negated formula. + * algorithm_id -- Identifier of the LTL-to-Büchi translator + * originally used for generating the given + * product automaton. + * + * Returns: Nothing. The test state variables are updated according to + * the results. + * + * ------------------------------------------------------------------------- */ +{ + AutomatonStats& automaton_stats + = test_results[algorithm_id].automaton_stats[f]; + + if (automaton_stats.emptiness_check_performed) + printText("Accepting cycles (cached):\n", 2, 8); + else + { + if (printText("Accepting cycles:\n", 3, 8)) + printText("", 4, 10); + else + printText("Searching for accepting cycles\n", 2, 8); + + try + { + round_info.product_automaton->emptinessCheck + (automaton_stats.emptiness_check_result); + + automaton_stats.emptiness_check_performed = true; + } + catch (const UserBreakException&) + { + if (!printText(" user break\n\n", 4)) + printText("[User break]\n\n", 2, 10); + + if (round_info.transcript_file.is_open()) + writeToTranscript("User break while searching for accepting cycles (" + + configuration.algorithmString(algorithm_id) + + ", " + + (f == 0 ? "posi" : "nega") + "tive formula)\n"); + + throw; + } + catch (const bad_alloc&) + { + if (!printText(" out of memory\n", 4)) + printText("[Out of memory]\n", 2, 10); + + if (round_info.transcript_file.is_open()) + writeToTranscript("Out of memory while searching for accepting cycles " + "(" + + configuration.algorithmString(algorithm_id) + + ", " + + (f == 0 ? "posi" : "nega") + "tive formula)\n"); + + throw EmptinessCheckFailedException(); + } + + printText(" ok\n", 4); + } + + if (configuration.global_options.verbosity >= 3) + printAcceptanceCycleStats(cout, 10, algorithm_id, f); +} + +/* ========================================================================= */ +void performConsistencyCheck + (vector::size_type + algorithm_id) +/* ---------------------------------------------------------------------------- + * + * Description: Checks the model checking results for consistency for a + * particular LTL-to-Büchi conversion algorithm implementation, + * i.e., verifies that the model checking results for a formula + * and its negation are not contradictory. + * + * Arguments: algorithm_id -- Identifier of an algorithm for which the + * model checking result consistency check + * should be performed. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + bool result = true; + + if (test_results[algorithm_id].consistency_check_result == -1) + { + StateSpace::size_type state; + const Bitset& acceptance_vector_for_formula + = test_results[algorithm_id].automaton_stats[0].emptiness_check_result; + const Bitset& acceptance_vector_for_negation + = test_results[algorithm_id].automaton_stats[1].emptiness_check_result; + + if (printText("Result consistency check:\n", 3, 6)) + printText("", 4, 8); + else + printText("Checking consistency of the model checking results\n", 2, 6); + + /* + * The consistency check will succeed if `result' is still true at the end + * of the following loop. + * + * The consistency check fails if there is a state in which both the + * formula and its negation are claimed to be false. + */ + + final_statistics[algorithm_id].consistency_checks_performed++; + + for (state = 0; state < round_info.real_emptiness_check_size; ++state) + { + ++test_results[algorithm_id].consistency_check_comparisons; + if (!acceptance_vector_for_formula[state] + && !acceptance_vector_for_negation[state]) + { + ++test_results[algorithm_id].failed_consistency_check_comparisons; + result = false; + } + } + + test_results[algorithm_id].consistency_check_result = (result ? 1 : 0); + } + else + result = (test_results[algorithm_id].consistency_check_result == 1); + + if (!result) + { + round_info.error = true; + + if (round_info.transcript_file.is_open()) + writeToTranscript("Model checking result consistency check failed (" + + configuration.algorithmString(algorithm_id) + ")\n"); + + final_statistics[algorithm_id].consistency_check_failures++; + } + + printText((result ? " ok\n" : " failed\n"), 4); + + if (configuration.global_options.verbosity >= 3) + printConsistencyCheckStats(cout, 8, algorithm_id); +} + +/* ========================================================================= */ +void compareResults() +/* ---------------------------------------------------------------------------- + * + * Description: Compares the model checking results obtained using different + * LTL->Büchi conversion algorithm implementations with each + * other. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (printText("Model checking result cross-comparison:\n", 3, 4)) + printText("", 4, 6); + else + printText("Comparing model checking results\n", 2, 4); + + bool result = true; + AutomatonStats* alg_1_stats; + AutomatonStats* alg_2_stats; + + for (vector::size_type + alg_1 = 0; + alg_1 < test_results.size(); + ++alg_1) + { + for (int counter = 0; counter < 2; counter++) + { + alg_1_stats = &test_results[alg_1].automaton_stats[counter]; + + for (vector + ::size_type alg_2 = alg_1 + 1; + alg_2 < test_results.size(); + ++alg_2) + { + alg_2_stats = &test_results[alg_2].automaton_stats[counter]; + + if (configuration.algorithms[alg_1].enabled + && configuration.algorithms[alg_2].enabled + && alg_1_stats->emptiness_check_performed + && alg_2_stats->emptiness_check_performed) + { + if (!alg_1_stats->cross_comparison_stats[alg_2].first) + { + (final_statistics[alg_1].cross_comparisons_performed[alg_2])++; + (final_statistics[alg_2].cross_comparisons_performed[alg_1])++; + + unsigned long int dist + = alg_1_stats->emptiness_check_result.hammingDistance + (alg_2_stats->emptiness_check_result); + + alg_1_stats->cross_comparison_stats[alg_2].first + = alg_2_stats->cross_comparison_stats[alg_1].first + = true; + + alg_1_stats->cross_comparison_stats[alg_2].second + = alg_2_stats->cross_comparison_stats[alg_1].second + = dist; + + if (dist > 0) + { + (final_statistics[alg_1].cross_comparison_mismatches[alg_2])++; + (final_statistics[alg_2].cross_comparison_mismatches[alg_1])++; + + if (alg_1_stats->emptiness_check_result[0] + != alg_2_stats->emptiness_check_result[0]) + { + (final_statistics[alg_1]. + initial_cross_comparison_mismatches[alg_2])++; + (final_statistics[alg_2]. + initial_cross_comparison_mismatches[alg_1])++; + } + + result = false; + } + } + else if (alg_1_stats->cross_comparison_stats[alg_2].second != 0) + result = false; + } + } + } + } + + if (!result) + { + round_info.error = true; + + if (round_info.transcript_file.is_open()) + { + writeToTranscript("Model checking result cross-comparison check failed"); + printCrossComparisonStats(round_info.transcript_file, 8, + configuration.algorithms.size()); + } + } + + printText((result ? " ok\n" : " failed\n"), 4); + if (configuration.global_options.verbosity >= 3) + printCrossComparisonStats(cout, 6, test_results.size()); +} + +/* ========================================================================= */ +void performBuchiIntersectionCheck() +/* ---------------------------------------------------------------------------- + * + * Description: Tests the intersection of the Büchi automata constructed for + * the formula and its negation for emptiness. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + using ::Graph::SccIterator; + + if (printText("Büchi automata intersection emptiness check:\n", 3, 4)) + printText("\n", 4, 6); + else + printText("Checking Büchi automata intersections for emptiness\n", 2, 4); + + bool result = true; + BuchiAutomaton* automaton_intersection; + + for (vector::size_type + alg_1 = 0; + alg_1 < round_info.number_of_translators; + ++alg_1) + { + for (vector::size_type + alg_2 = 0; + alg_2 < round_info.number_of_translators; + ++alg_2) + { + try + { + if (test_results[alg_1].automaton_stats[0]. + buchi_intersection_check_stats[alg_2] == -1) + { + printText("(+) " + configuration.algorithmString(alg_1) + ", (-) " + + configuration.algorithmString(alg_2), + 4, + 8); + + /* + * Compute the intersection of two Büchi automata constructed for + * the positive and the negative formula, respectively. + */ + + if (test_results[alg_1].automaton_stats[0].buchiAutomatonComputed() + && test_results[alg_2].automaton_stats[1]. + buchiAutomatonComputed()) + { + automaton_intersection = 0; + + automaton_intersection + = BuchiAutomaton::intersect + (*(test_results[alg_1].automaton_stats[0].buchi_automaton), + *(test_results[alg_2].automaton_stats[1].buchi_automaton)); + + /* + * Scan the nontrivial maximal strongly connected components of + * the intersection automaton to check whether the automaton has + * any accepting executions. If an MSCC with a state from every + * acceptance set is found, the intersection emptiness check + * fails. + */ + + for (SccIterator scc(*automaton_intersection); + !scc.atEnd(); ++scc) + { + /* + * MSCC nontriviality check (an MSCC is nontrivial iff it has at + * least two states or if it consists of a single state + * connected to itself). + */ + + if (scc->size() > 1 + || (scc->size() == 1 + && automaton_intersection->connected(*(scc->begin()), + *(scc->begin())))) + { + const unsigned long int number_of_acceptance_sets + = automaton_intersection->numberOfAcceptanceSets(); + BitArray acceptance_sets(number_of_acceptance_sets); + acceptance_sets.clear(number_of_acceptance_sets); + + unsigned long int accept_set; + unsigned long int acceptance_set_counter = 0; + + for (set, + ALLOC(GraphEdgeContainer::size_type) >::const_iterator + state = scc->begin(); + state != scc->end() + && acceptance_set_counter < number_of_acceptance_sets; + ++state) + { + accept_set = acceptance_set_counter; + while (accept_set < number_of_acceptance_sets) + { + if ((*automaton_intersection)[*state].acceptanceSets(). + test(accept_set)) + { + acceptance_sets.setBit(accept_set); + if (accept_set == acceptance_set_counter) + { + do + acceptance_set_counter++; + while (acceptance_set_counter + < number_of_acceptance_sets + && acceptance_sets[acceptance_set_counter]); + accept_set = acceptance_set_counter; + continue; + } + } + accept_set++; + } + } + + if (acceptance_set_counter == number_of_acceptance_sets) + { + test_results[alg_1].automaton_stats[0]. + buchi_intersection_check_stats[alg_2] = 0; + test_results[alg_2].automaton_stats[1]. + buchi_intersection_check_stats[alg_1] = 0; + + final_statistics[alg_1]. + buchi_intersection_check_failures[alg_2]++; + if (alg_1 != alg_2) + final_statistics[alg_2]. + buchi_intersection_check_failures[alg_1]++; + + result = false; + + printText(": failed\n", 4); + + break; + } + } + } + + delete automaton_intersection; + automaton_intersection = 0; + + if (test_results[alg_1].automaton_stats[0]. + buchi_intersection_check_stats[alg_2] == -1) + { + test_results[alg_1].automaton_stats[0]. + buchi_intersection_check_stats[alg_2] = 1; + test_results[alg_2].automaton_stats[1]. + buchi_intersection_check_stats[alg_1] = 1; + + printText(": ok\n", 4); + } + + final_statistics[alg_1]. + buchi_intersection_checks_performed[alg_2]++; + if (alg_1 != alg_2) + final_statistics[alg_2]. + buchi_intersection_checks_performed[alg_1]++; + } + else + printText(": not performed\n", 4); + } + else if (test_results[alg_1].automaton_stats[0]. + buchi_intersection_check_stats[alg_2] == 0) + result = false; + } + catch (const UserBreakException&) + { + if (!printText(" user break\n\n", 4)) + printText("[User break]\n\n", 2, 6); + + if (round_info.transcript_file.is_open()) + writeToTranscript("User break during Büchi automata intersection " + "emptiness check"); + round_info.transcript_file << string(8, ' ') + "(+) " + + configuration.algorithmString(alg_1) + + ", (-) " + + configuration.algorithmString(alg_2) + + "\n\n"; + + if (automaton_intersection != 0) + { + delete automaton_intersection; + automaton_intersection = 0; + } + + throw; + } + catch (const bad_alloc&) + { + if (automaton_intersection != 0) + { + delete automaton_intersection; + automaton_intersection = 0; + } + + if (!printText(" out of memory\n", 4)) + printText("[Out of memory: (+) " + + configuration.algorithmString(alg_1) + + ", (-) " + + configuration.algorithmString(alg_2) + + "]\n", + 2, + 6); + + if (round_info.transcript_file.is_open()) + writeToTranscript("Out of memory during Büchi automata " + "intersection emptiness check"); + round_info.transcript_file << string(8, ' ') + "(+) " + + configuration.algorithmString(alg_1) + + ", (-) " + + configuration.algorithmString(alg_2) + + "\n\n"; + } + } + } + + if (!result) + { + round_info.error = true; + + if (round_info.transcript_file.is_open()) + { + writeToTranscript("Büchi automata intersection emptiness check failed"); + printBuchiIntersectionCheckStats + (round_info.transcript_file, 8, round_info.number_of_translators); + } + } + + if (configuration.global_options.verbosity >= 3) + printBuchiIntersectionCheckStats + (cout, 6, round_info.number_of_translators); +} + +} diff --git a/lbtt/src/TestOperations.h b/lbtt/src/TestOperations.h new file mode 100644 index 000000000..314690614 --- /dev/null +++ b/lbtt/src/TestOperations.h @@ -0,0 +1,539 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef TESTOPERATIONS_H +#define TESTOPERATIONS_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include +#include "Alloc.h" +#include "Configuration.h" +#include "Exception.h" +#include "StateSpace.h" + +using namespace std; + +extern Configuration configuration; + + + +/****************************************************************************** + * + * Functions for various test operations. + * + *****************************************************************************/ + +namespace TestOperations +{ + +void openFile + (const char* filename, ifstream& stream, /* Opens a file for */ + ios::openmode mode, int indent = 0); /* reading. */ + +void openFile /* Opens a file for */ + (const char* filename, ofstream& stream, /* writing. */ + ios::openmode mode, int indent = 0); + +void removeFile /* Removes a file. */ + (const char* filename, int indent = 0); + +void printFileContents /* Displays the contents */ + (ostream& stream, const char* message, /* of a file. */ + const char* filename, int indent, + const char* line_prefix = ""); + +void writeToTranscript /* Writes a message */ + (const string& message, /* into the transcript */ + bool show_formula_in_header = true); /* file. */ + +void generateStateSpace(); /* Generates a state space. + */ + +void generateFormulae /* Generates a random */ + (istream* formula_input_stream = 0); /* LTL formula and + * stores the formula, + * its negation and the + * NNFs of the two + * formulae into a + * given array. + */ + +void verifyFormulaOnPath(); /* Evaluates the LTL + * formula (accessed + * through `round_info') + * on a path directly + * (if using paths as + * state spaces). + */ + +void writeFormulaeToFiles(); /* Writes LTL formulas */ + /* into a file. */ + +void generateBuchiAutomaton /* Generates a Büchi */ + (int f, /* automaton from a LTL */ + vector /* given file, using a */ + ::size_type /* given LTL-to-Büchi */ + algorithm_id); /* translation algorithm + * for the conversion. + */ + +void generateProductAutomaton /* Computes the */ + (int f, /* synchronous product */ + vector /* and a state space. */ + ::size_type + algorithm_id); + +void performEmptinessCheck /* Performs an emptiness */ + (int f, /* check on a product */ + vector + ::size_type + algorithm_id); + +void performConsistencyCheck /* Performs a */ + (vector /* the test results */ + ::size_type /* for a formula and its */ + algorithm_id); /* negation. */ + +void compareResults(); /* Compares the model + * checking results + * obtained using some + * LTL-to-Büchi conversion + * algorithm with the + * results given by the + * other algorithms. + */ + +void performBuchiIntersectionCheck(); /* Performs pairwise + * emptiness checks on the + * Büchi automata + * constructed by the + * different algorithms + * from a formula and its + * negation. + */ + + + +/****************************************************************************** + * + * A class for reporting state space generation errors. + * + *****************************************************************************/ + +class StateSpaceGenerationException : public Exception +{ +public: + StateSpaceGenerationException(); /* Constructor. */ + + /* default copy constructor */ + + ~StateSpaceGenerationException() throw(); /* Destructor. */ + + StateSpaceGenerationException& operator= /* Assignment operator. */ + (const StateSpaceGenerationException& e); +}; + + + +/****************************************************************************** + * + * A class for reporting LTL formula generation errors. + * + *****************************************************************************/ + +class FormulaGenerationException : public Exception +{ +public: + FormulaGenerationException(); /* Constructor. */ + + /* default copy constructor */ + + ~FormulaGenerationException() throw(); /* Destructor. */ + + FormulaGenerationException& operator= /* Assignment operator. */ + (const FormulaGenerationException& e); +}; + + + +/****************************************************************************** + * + * A class for reporting Büchi automaton generation errors. + * + *****************************************************************************/ + +class BuchiAutomatonGenerationException : public Exception +{ +public: + BuchiAutomatonGenerationException(); /* Constructor. */ + + /* default copy constructor */ + + ~BuchiAutomatonGenerationException() throw(); /* Destructor. */ + + BuchiAutomatonGenerationException& operator= /* Assignment operator. */ + (const BuchiAutomatonGenerationException& e); +}; + + + +/****************************************************************************** + * + * A class for reporting product automaton generation errors. + * + *****************************************************************************/ + +class ProductAutomatonGenerationException : public Exception +{ +public: + ProductAutomatonGenerationException(); /* Constructor. */ + + /* default copy constructor */ + + ~ProductAutomatonGenerationException() throw(); /* Destructor. */ + + ProductAutomatonGenerationException& operator= /* Assignment operator. */ + (const ProductAutomatonGenerationException& e); +}; + + + +/****************************************************************************** + * + * A class for reporting errors during the emptiness check. + * + *****************************************************************************/ + +class EmptinessCheckFailedException : public Exception +{ +public: + EmptinessCheckFailedException(); /* Constructor. */ + + /* default copy constructor */ + + ~EmptinessCheckFailedException() throw(); /* Destructor. */ + + EmptinessCheckFailedException& operator= /* Assignment operator. */ + (const EmptinessCheckFailedException& e); +}; + + + +/****************************************************************************** + * + * Inline function definitions for class StateSpaceGenerationException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline StateSpaceGenerationException::StateSpaceGenerationException() : + Exception("state space generation failed") +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class StateSpaceGenerationException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline StateSpaceGenerationException::~StateSpaceGenerationException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class StateSpaceGenerationException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline StateSpaceGenerationException& +StateSpaceGenerationException::operator= + (const StateSpaceGenerationException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class StateSpaceGenerationException. + * Assigns the value of another StateSpaceGenerationException to + * `this' one. + * + * Arguments: e -- A reference to a constant StateSpaceException. + * + * Returns: A reference to the object whose values was changed. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class FormulaGenerationException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline FormulaGenerationException::FormulaGenerationException() : + Exception("LTL formula generation failed") +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class FormulaGenerationException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FormulaGenerationException::~FormulaGenerationException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class FormulaGenerationException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline FormulaGenerationException& +FormulaGenerationException::operator=(const FormulaGenerationException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class FormulaGenerationException. + * Assigns the value of another FormulaGenerationException to + * `this' one. + * + * Arguments: e -- A reference to a constant FormulaException. + * + * Returns: A reference to the object whose values was changed. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class BuchiAutomatonGenerationException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline BuchiAutomatonGenerationException::BuchiAutomatonGenerationException() : + Exception("Büchi automaton generation failed") +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class BuchiAutomatonGenerationException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline BuchiAutomatonGenerationException::~BuchiAutomatonGenerationException() + throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class BuchiAutomatonGenerationException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline BuchiAutomatonGenerationException& +BuchiAutomatonGenerationException::operator= + (const BuchiAutomatonGenerationException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class + * BuchiAutomatonGenerationException. Assigns the value of + * another BuchiAutomatonGenerationException to `this' one. + * + * Arguments: e -- A reference to a constant + * BuchiAutomatonGenerationException. + * + * Returns: A reference to the object whose value was changed. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class ProductAutomatonGenerationException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline +ProductAutomatonGenerationException::ProductAutomatonGenerationException() : + Exception("product automaton generation failed") +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class ProductAutomatonGenerationException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline +ProductAutomatonGenerationException::~ProductAutomatonGenerationException() + throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class ProductAutomatonGenerationException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline ProductAutomatonGenerationException& +ProductAutomatonGenerationException::operator= + (const ProductAutomatonGenerationException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class + * ProductAutomatonGenerationException. + * + * Arguments: e -- A reference to a constant + * ProductAutomatonGenerationException. + * + * Returns: A reference to the object whose value was changed. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + + + +/****************************************************************************** + * + * Inline function definitions for class EmptinessCheckFailedException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline +EmptinessCheckFailedException::EmptinessCheckFailedException() : + Exception("emptiness check failed") +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class EmptinessCheckFailedException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline EmptinessCheckFailedException::~EmptinessCheckFailedException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class EmptinessCheckFailedException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline EmptinessCheckFailedException& EmptinessCheckFailedException::operator= + (const EmptinessCheckFailedException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class EmptinessCheckFailedException. + * Assigns the value of another EmptinessCheckFailedException to + * `this' one. + * + * Arguments: e -- A reference to a constant + * EmptinessCheckFailedException. + * + * Returns: A reference to the object whose value was changed. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + +} + +#endif /* !TESTOPERATIONS_H */ diff --git a/lbtt/src/TestRoundInfo.h b/lbtt/src/TestRoundInfo.h new file mode 100644 index 000000000..b77e7f674 --- /dev/null +++ b/lbtt/src/TestRoundInfo.h @@ -0,0 +1,246 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef TESTROUNDINFO_H +#define TESTROUNDINFO_H + +#include +#include +#include +#include +#include +#include "Alloc.h" +#include "Exception.h" +#include "LtlFormula.h" +#include "ProductAutomaton.h" +#include "PathIterator.h" +#include "StateSpace.h" + +using namespace std; + +namespace SharedTestData +{ + +/****************************************************************************** + * + * A data structure for storing test round control information and some test- + * related data. + * + *****************************************************************************/ + +class TestRoundInfo +{ +public: + TestRoundInfo(); /* Constructor. */ + + ~TestRoundInfo(); /* Destructor. */ + + Exceptional_ostream cout; /* Exception-guarded output + * stream for messages. + */ + + ifstream formula_input_file; /* Stream for reading input + * formulae. + */ + + ofstream transcript_file; /* Output stream for + * logging operations. + */ + + unsigned long int number_of_translators; /* Number of translators. + */ + + unsigned long int current_round; /* Number of current round. + */ + + unsigned long int next_round_to_run; /* Indicates the next + * round to run. + */ + + unsigned long int next_round_to_stop; /* Indicates the next + * test round after which + * to pause and wait for + * user commands. + */ + + unsigned long int error_report_round; /* Number of the last round + * in which something was + * written to the error + * transcript file. + */ + + bool error; /* True if an error + * occurred during the + * current round. + */ + + bool skip; /* True if the current + * round is to be skipped. + */ + + bool abort; /* True if the testing is + * to be aborted. + */ + + unsigned long int num_generated_statespaces; /* State space */ + unsigned long int total_statespace_states; /* statistics. */ + unsigned long int total_statespace_transitions; + + unsigned long int num_processed_formulae; /* Number of processed LTL + * formulae. + */ + + bool fresh_statespace; /* True if a new state + * space was generated in + * the current test round. + */ + + const Graph::StateSpace* statespace; /* Pointer to the state + * space used in the + * current test round. + */ + + Graph::PathIterator* path_iterator; /* Pointer to a "path + * iterator" needed + * when using enumerated + * paths as state spaces. + */ + + const Graph::ProductAutomaton* product_automaton; /* Pointer to the product + * automaton used in the + * current test round. + */ + + unsigned long int real_emptiness_check_size; /* Number of states in the + * state space where the + * emptiness check should + * be performed. + */ + + unsigned long int /* Number of the round */ + next_round_to_change_statespace; /* in which to generate + * a new state space. + */ + + unsigned long int /* Number of the round */ + next_round_to_change_formula; /* in which to generate + * (or read) a new LTL + * formula. + */ + + bool fresh_formula; /* True is a new formula + * was generated (or read + * from a file) in the + * current round. + */ + + vector /* current round: */ + formulae; /* formulae[0]: + * positive formula in + * negation normal + * form + * formulae[1]: + * negated formula in + * negation normal + * form + * formulae[2]: + * positive formula as + * generated + * formulae[3]: + * negative formula as + * generated + */ + + vector formula_in_file; /* The values in this + * vector will be set to + * true when the + * corresponding + * formulae have been + * written to files + * successfully. Index + * 0 corresponds to the + * positive formula and + * 1 to the negative + * formula. + */ + + char formula_file_name[2][L_tmpnam + 1]; /* Storage space for the */ + char automaton_file_name[L_tmpnam + 1]; /* names of several */ + char cout_capture_file[L_tmpnam + 1]; /* temporary files. */ + char cerr_capture_file[L_tmpnam + 1]; + +private: + TestRoundInfo(const TestRoundInfo& info); /* Prevent copying and */ + TestRoundInfo& operator= /* assignment of */ + (const TestRoundInfo& info); /* TestRoundInfo + * objects. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class TestRoundInfo. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline TestRoundInfo::TestRoundInfo() : + cout(&std::cout, ios::failbit | ios::badbit), number_of_translators(0), + current_round(1), next_round_to_run(1), next_round_to_stop(1), + error_report_round(0), error(false), skip(false), abort(false), + num_generated_statespaces(0), total_statespace_states(0), + total_statespace_transitions(0), num_processed_formulae(0), + fresh_statespace(false), statespace(0), path_iterator(0), + product_automaton(0), real_emptiness_check_size(0), + next_round_to_change_statespace(1), next_round_to_change_formula(1), + fresh_formula(false), formulae(4, static_cast(0)), + formula_in_file(2, false) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class TestRoundInfo. Creates a new + * TestRoundInfo object. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline TestRoundInfo::~TestRoundInfo() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class TestRoundInfo. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +} + +#endif /* !TESTROUNDINFO_H */ diff --git a/lbtt/src/TestStatistics.cc b/lbtt/src/TestStatistics.cc new file mode 100644 index 000000000..b791e27b4 --- /dev/null +++ b/lbtt/src/TestStatistics.cc @@ -0,0 +1,100 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include "TestStatistics.h" + +/****************************************************************************** + * + * Function definitions for struct AlgorithmTestResults. + * + *****************************************************************************/ + +/* ========================================================================= */ +void AlgorithmTestResults::emptinessReset() +/* ---------------------------------------------------------------------------- + * + * Description: Resets the emptiness checking information in an + * AlgorithmTestResults structure. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + consistency_check_result = -1; + consistency_check_comparisons = 0; + failed_consistency_check_comparisons = 0; + + for (int i = 0; i < 2; i++) + { + automaton_stats[i].number_of_product_states = 0; + automaton_stats[i].number_of_product_transitions = 1; + automaton_stats[i].emptiness_check_result.clear(); + automaton_stats[i].emptiness_check_performed = false; + + for (vector::iterator it + = automaton_stats[i].cross_comparison_stats.begin(); + it != automaton_stats[i].cross_comparison_stats.end(); + ++it) + { + it->first = false; + it->second = 0; + } + } +} + +/* ========================================================================= */ +void AlgorithmTestResults::fullReset() +/* ---------------------------------------------------------------------------- + * + * Description: Resets the contents of an AlgorithmTestResults structure. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + emptinessReset(); + + for (int i = 0; i < 2; i++) + { + if (automaton_stats[i].buchi_automaton != 0) + delete automaton_stats[i].buchi_automaton; + automaton_stats[i].buchi_automaton = 0; + automaton_stats[i].number_of_buchi_states = 0; + automaton_stats[i].number_of_buchi_transitions = 0; + automaton_stats[i].number_of_acceptance_sets = 0; + automaton_stats[i].number_of_msccs = 0; + automaton_stats[i].buchi_generation_time = 0.0; + + for (vector::iterator it + = automaton_stats[i].buchi_intersection_check_stats.begin(); + it != automaton_stats[i].buchi_intersection_check_stats.end(); + ++it) + *it = -1; + } +} diff --git a/lbtt/src/TestStatistics.h b/lbtt/src/TestStatistics.h new file mode 100644 index 000000000..75a4c7c3f --- /dev/null +++ b/lbtt/src/TestStatistics.h @@ -0,0 +1,584 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef TESTSTATISTICS_H +#define TESTSTATISTICS_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include "Alloc.h" +#include "BuchiAutomaton.h" +#include "Configuration.h" +#include "ProductAutomaton.h" +#include "StateSpace.h" + +using namespace std; +using Graph::BuchiAutomaton; +using Graph::StateSpace; +using Graph::ProductAutomaton; + +/****************************************************************************** + * + * A data structure for storing test data for a single formula. + * + *****************************************************************************/ + +struct AutomatonStats +{ + explicit AutomatonStats /* Constructor. */ + (vector + ::size_type number_of_algorithms, + StateSpace::size_type max_statespace_size); + + /* default copy constructor */ + + ~AutomatonStats(); /* Destructor. */ + + /* default assignment operator */ + + bool buchiAutomatonComputed() const; /* Tests whether a Büchi + * automaton has been + * computed. + */ + + bool productAutomatonComputed() const; /* Tests whether a product + * automaton has been + * computed. + */ + + bool crossComparisonPerformed /* Tests whether the */ + (unsigned long int algorithm) const; /* result of the + * emptiness check has + * been compared against + * the result computed + * using some other + * implementation. + */ + + bool buchiIntersectionCheckPerformed /* Tests whether the */ + (unsigned long int algorithm) const; /* Büchi automata + * intersection check + * has been performed + * against a given + * implementation. + */ + + BuchiAutomaton* buchi_automaton; /* A pointer to a Büchi + * automaton. + */ + + BuchiAutomaton::size_type number_of_buchi_states; /* Number of states in the + * automaton. + */ + + unsigned long int number_of_buchi_transitions; /* Number of transitions in + * the automaton. + */ + + unsigned long int number_of_acceptance_sets; /* Number of acceptance + * sets in the automaton. + */ + + unsigned long int number_of_msccs; /* Number of maximal + * strongly connected + * components in the + * automaton. + */ + + double buchi_generation_time; /* Time used to generate a + * Büchi automaton. + */ + + ProductAutomaton::size_type /* Number of stats in a */ + number_of_product_states; /* product automaton. */ + + unsigned long int number_of_product_transitions; /* Number of transitions in + * a product automaton. + */ + + Bitset emptiness_check_result; /* Result of the emptiness + * check for the product + * automaton. + */ + + bool emptiness_check_performed; /* Tells whether the + * contents of the previous + * Bitset are valid. + */ + + typedef pair + CrossComparisonStats; + + vector /* cross-comparison */ + cross_comparison_stats; /* results. The `first' + * element of the pair + * tells whether a cross- + * comparison with a given + * algorithm has been + * performed, and the + * `second' element of the + * pair gives the number + * of system states in + * which the results + * differ. + */ + + vector /* Büchi automaton */ + buchi_intersection_check_stats; /* intersection + * emptiness check + * results. The elements + * of the vector tell + * whether the check has + * been performed + * against the automata + * constructed from the + * negated formula using + * the other algorithms, + * and if yes, the result + * of the check + * (-1 = check not + * performed, 0 = check + * failed, 1 = check + * was successful). + */ +}; + + + +/****************************************************************************** + * + * A data structure for storing test data for a single algorithm. + * + *****************************************************************************/ + +struct AlgorithmTestResults +{ + explicit AlgorithmTestResults /* Constructor. */ + (vector + ::size_type + number_of_algorithms, + StateSpace::size_type max_statespace_size); + + /* default copy constructor */ + + ~AlgorithmTestResults(); /* Destructor. */ + + /* default assignment operator */ + + void emptinessReset(); /* Resets the emptiness + * checking information. + */ + + void fullReset(); /* Resets the test results + * completely. + */ + + int consistency_check_result; /* Tells the consistency + * check status for an + * algorithm. The value + * -1 means the check has + * not been performed, a 0 + * stands for a failed + * check, and the value 1 + * denotes that the check + * was successful. + */ + + StateSpace::size_type /* Number of test cases */ + consistency_check_comparisons; /* in the consistency + * check. + */ + + StateSpace::size_type /* Number of failed test */ + failed_consistency_check_comparisons; /* cases in the consistency + * check. + */ + + vector /* A two-element vector */ + automaton_stats; /* storing test results + * for an algorithm. + */ +}; + + + +/****************************************************************************** + * + * A data structure for storing test statistics for a single algorithm over the + * whole test session. + * + *****************************************************************************/ + +struct TestStatistics +{ + explicit TestStatistics /* Constructor. */ + (vector::size_type + number_of_algorithms); + + /* default copy constructor */ + + ~TestStatistics(); + + /* default assignment operator */ + + unsigned long int /* Number of failed */ + failures_to_compute_buchi_automaton[2]; /* attempts to generate + * a Büchi automaton. + */ + + unsigned long int buchi_automaton_count[2]; /* Number of attempts to + * generate a Büchi + * automaton. + */ + + unsigned long int /* Number of failed */ + failures_to_compute_product_automaton[2]; /* attempts to generate + * a product automaton. + */ + + unsigned long int product_automaton_count[2]; /* Number of attempts to + * generate a product + * automaton. + */ + + unsigned long int consistency_check_failures; /* Number of failed + * consistency checks. + */ + + unsigned long int consistency_checks_performed; /* Number of consistency + * checks performed. + */ + + unsigned long int /* Total number of */ + total_number_of_buchi_states[2]; /* states in all the + * generated Büchi + * automata. + */ + + unsigned long int /* Total number of */ + total_number_of_buchi_transitions[2]; /* transitions in all + * the generated Büchi + * automata. + */ + + unsigned long int /* Total number of sets */ + total_number_of_acceptance_sets[2]; /* of accepting states + * in all the generated + * Büchi automata. + */ + + unsigned long int /* Total number of */ + total_number_of_msccs[2]; /* maximal strongly + * connected components + * in the generated + * Büchi automata. + */ + + double total_buchi_generation_time[2]; /* Total time used when + * generating Büchi + * automata. + */ + + unsigned long int /* Total number of */ + total_number_of_product_states[2]; /* states in all the + * generated product + * automata. + */ + + unsigned long int /* Total number of */ + total_number_of_product_transitions[2]; /* transitions in all the + * generated product + * automata. + */ + + vector /* result cross- */ + cross_comparison_mismatches; /* comparisons. */ + + vector /* result cross- */ + initial_cross_comparison_mismatches; /* comparisons in the + * initial state of the + * state space. + */ + + vector /* cross-comparisons */ + cross_comparisons_performed; /* performed. */ + + vector /* Büchi automaton */ + buchi_intersection_check_failures; /* emptiness checks + * against the automata + * constructed from the + * negated formula + * using the other + * algorithms. + */ + + vector /* automaton emptiness */ + buchi_intersection_checks_performed; /* checks performed + * against the automata + * constructed from the + * negated formula using + * the other algorithms. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for struct AutomatonStats. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline AutomatonStats::AutomatonStats + (vector::size_type + number_of_algorithms, + StateSpace::size_type max_statespace_size) : + buchi_automaton(0), number_of_buchi_states(0), + number_of_buchi_transitions(0), number_of_acceptance_sets(0), + number_of_msccs(0), buchi_generation_time(0.0), number_of_product_states(0), + number_of_product_transitions(1), + emptiness_check_result(max_statespace_size), + emptiness_check_performed(false), + cross_comparison_stats(number_of_algorithms, make_pair(false, 0)), + buchi_intersection_check_stats(number_of_algorithms, -1) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for struct AutomatonStats. + * + * Arguments: number_of_algorithms -- Number of implementations taking + * part in the tests. + * max_statespace_size -- Maximum size of the state spaces + * used in testing. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + emptiness_check_result.clear(); +} + +/* ========================================================================= */ +inline AutomatonStats::~AutomatonStats() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for struct AutomatonStats. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline bool AutomatonStats::buchiAutomatonComputed() const +/* ---------------------------------------------------------------------------- + * + * Description: Test whether a Büchi automaton has been computed (i.e., + * whether information about the automaton has been stored in + * the AutomatonStats structure). + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + return (buchi_automaton != 0); +} + +/* ========================================================================= */ +inline bool AutomatonStats::productAutomatonComputed() const +/* ---------------------------------------------------------------------------- + * + * Description: Test whether a product automaton has been computed (i.e., + * whether information about the automaton has been stored in + * the AutomatonStats structure). + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + return (number_of_product_states != 0 + || number_of_product_transitions <= number_of_product_states); +} + +/* ========================================================================= */ +inline bool +AutomatonStats::crossComparisonPerformed(unsigned long int algorithm) const +/* ---------------------------------------------------------------------------- + * + * Description: Test whether the emptiness check result for a product + * automaton has been compared with another result computed + * using a different implementation. + * + * Arguments: algorithm -- Implementation identifier. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + return cross_comparison_stats[algorithm].first; +} + +/* ========================================================================= */ +inline bool +AutomatonStats::buchiIntersectionCheckPerformed(unsigned long int algorithm) + const +/* ---------------------------------------------------------------------------- + * + * Description: Test whether the Büchi automata intersection check result + * (against some other implementation) is available in the data + * structure. + * + * Arguments: algorithm -- Implementation identifier. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + return (buchi_intersection_check_stats[algorithm] != -1); +} + + + +/****************************************************************************** + * + * Inline function definitions for struct AlgorithmTestResults. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline AlgorithmTestResults::AlgorithmTestResults + (vector::size_type + number_of_algorithms, + StateSpace::size_type max_statespace_size) : + consistency_check_result(-1), consistency_check_comparisons(0), + failed_consistency_check_comparisons(0), + automaton_stats(2, AutomatonStats(number_of_algorithms, + max_statespace_size)) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for struct AlgorithmTestResults. + * + * Arguments: number_of_algorithms -- Number of implementations taking + * part in the tests. + * max_statespace_size -- Maximum size of the state spaces + * used in testing. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline AlgorithmTestResults::~AlgorithmTestResults() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for struct AlgorithmTestResults. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + + + +/****************************************************************************** + * + * Inline function definitions for struct TestStatistics. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline TestStatistics::TestStatistics + (vector::size_type + number_of_algorithms) : + consistency_check_failures(0), consistency_checks_performed(0), + cross_comparison_mismatches(number_of_algorithms, 0), + initial_cross_comparison_mismatches(number_of_algorithms, 0), + cross_comparisons_performed(number_of_algorithms, 0), + buchi_intersection_check_failures(number_of_algorithms, 0), + buchi_intersection_checks_performed(number_of_algorithms, 0) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for struct TestStatistics. + * + * Arguments: number_of_algorithms -- Number of implementations taking + * part in the tests. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + for (int i = 0; i < 2; i++) + { + failures_to_compute_buchi_automaton[i] = 0; + buchi_automaton_count[i] = 0; + failures_to_compute_product_automaton[i] = 0; + product_automaton_count[i] = 0; + total_number_of_buchi_states[i] = 0; + total_number_of_buchi_transitions[i] = 0; + total_number_of_acceptance_sets[i] = 0; + total_number_of_msccs[i] = 0; + total_number_of_product_states[i] = 0; + total_number_of_product_transitions[i] = 0; + total_buchi_generation_time[i] = 0.0; + } +} + +/* ========================================================================= */ +inline TestStatistics::~TestStatistics() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for struct TestStatistics. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +#endif /* !TESTSTATISTICS_H */ diff --git a/lbtt/src/TranslatorInterface.h b/lbtt/src/TranslatorInterface.h new file mode 100644 index 000000000..8053c5bac --- /dev/null +++ b/lbtt/src/TranslatorInterface.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef TRANSLATORINTERFACE_H +#define TRANSLATORINTERFACE_H + +#include +#include "LtlFormula.h" + +/****************************************************************************** + * + * General interface class for an LTL-to-Büchi translator. The interface + * provides an abstract member function `translate' taking an LtlFormula + * and an output file name as arguments. The purpose of an actual + * implementation of the function is to translate the given LtlFormula into + * an automaton and store the results in the given file. + * + *****************************************************************************/ + +class TranslatorInterface +{ +public: + TranslatorInterface(); /* Constructor. */ + + virtual ~TranslatorInterface() = 0; /* Destructor. */ + + virtual void translate /* Interface for a */ + (const ::Ltl::LtlFormula& formula, /* translation */ + const char* filename) = 0; /* algorithm. */ + +private: + TranslatorInterface(const TranslatorInterface&); /* Prevent copying and */ + TranslatorInterface& operator= /* assignment of */ + (const TranslatorInterface&); /* TranslatorInterface + * objects. + */ +}; + + + +/****************************************************************************** + * + * Inline function definitions for class TranslatorInterface. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline TranslatorInterface::TranslatorInterface() +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class TranslatorInterface. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline TranslatorInterface::~TranslatorInterface() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class TranslatorInterface. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +#endif /* !TRANSLATORINTERFACE_H */ diff --git a/lbtt/src/UserCommandReader.cc b/lbtt/src/UserCommandReader.cc new file mode 100644 index 000000000..6365784f3 --- /dev/null +++ b/lbtt/src/UserCommandReader.cc @@ -0,0 +1,927 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include +#ifdef HAVE_SSTREAM +#include +#else +#include +#endif /* HAVE_SSTREAM */ +#include "DispUtil.h" +#include "ProductAutomaton.h" +#include "SharedTestData.h" +#include "StatDisplay.h" +#include "StringUtil.h" +#include "TestRoundInfo.h" +#include "TestStatistics.h" +#include "TestOperations.h" +#include "UserCommandReader.h" +#include "UserCommands.h" + +#ifdef HAVE_READLINE +#include +#include +#include +#include +#include +#endif /* HAVE_READLINE */ + + +/****************************************************************************** + * + * Functions for reading and parsing user commands. + * + *****************************************************************************/ + +namespace UserCommandInterface +{ + +using namespace ::SharedTestData; +using namespace ::StatDisplay; +using namespace ::StringUtil; +using namespace ::UserCommands; + +/* ========================================================================= */ +void executeUserCommands() +/* ---------------------------------------------------------------------------- + * + * Description: Loop for reading user commands and executing them after a + * test round. + * + * Arguments: None. + * + * Returns: Nothing. However, changes `round_info.current_round', + * `round_info.next_round_to_stop', + * `round_info.next_round_to_run' and `round_info.abort' as a + * side effect, depending on the user's wish to abort testing, + * skip some number of test rounds or continue testing for a + * number of rounds. + * + * ------------------------------------------------------------------------ */ +{ + string input_line; + vector input_tokens; + TokenType token; + + bool formula_type = true; + + pair redirection_info; + string external_command; + ofstream* output_file = 0; +#ifdef HAVE_SSTREAM + ostringstream* output_string = 0; +#else + ostrstream* output_string = 0; +#endif /* HAVE_SSTREAM */ + ostream* output_stream = 0; + + const string prompt = " ** [Round " + toString(round_info.current_round) + + " of " + toString(configuration.global_options. + number_of_rounds) + + "] >> "; + int indent; + +#ifdef HAVE_READLINE + char* prompt_c_str = new char[prompt.length() + 1]; + strcpy(prompt_c_str, prompt.c_str()); + + char* line; + + try + { +#endif /* HAVE_READLINE */ + + ProductAutomaton* product_automaton = 0; + pair last_computed_product_automaton; + + signal(SIGPIPE, SIG_IGN); + + while (1) + { + try + { + input_line = ""; +#ifdef HAVE_READLINE + line = readline(prompt_c_str); + if (line != static_cast(0)) + input_line = line; + else + { + round_info.cout << '\n'; + round_info.cout.flush(); + } +#else + round_info.cout << prompt; + round_info.cout.flush(); + getline(cin, input_line, '\n'); + if (cin.eof()) + { + round_info.cout << '\n'; + round_info.cout.flush(); + cin.clear(); + } +#endif /* HAVE_READLINE */ + + external_command = ""; + string::size_type pipe_pos = input_line.find_first_of('|'); + if (pipe_pos != string::npos) + { + string::size_type nonspace_pos + = input_line.find_first_not_of(" \t", pipe_pos + 1); + if (nonspace_pos != string::npos) + { + external_command = input_line.substr(nonspace_pos); + input_line = input_line.substr(0, pipe_pos); + } + } + + sliceString(input_line, " \t", input_tokens); + + user_break = false; + + if (!input_tokens.empty()) + { +#ifdef HAVE_READLINE + add_history(line); + free(line); +#endif /* HAVE_READLINE */ + round_info.cout << '\n'; + round_info.cout.flush(); + + token = parseCommand(input_tokens[0]); + + if (token == CONTINUE || token == SKIP) + { + verifyArgumentCount(input_tokens, 0, 1); + + unsigned long int rounds_to_continue; + + rounds_to_continue = (input_tokens.size() > 1 + ? parseNumber(input_tokens[1]) + : 1); + + if (rounds_to_continue == 0) + throw CommandErrorException("Argument of the command must be " + "positive."); + + if (token == CONTINUE) + { + bool all_algorithms_disabled = true; + + for (vector + ::const_iterator + algorithm = configuration.algorithms.begin(); + algorithm != configuration.algorithms.end(); + ++algorithm) + { + if (algorithm->enabled) + { + all_algorithms_disabled = false; + break; + } + } + + /* + * Show a warning if the `continue' command would result in no + * further tests in the case that none of the implementations is + * enabled for testing. + */ + + if (configuration.global_options.interactive + != Configuration::ALWAYS + && all_algorithms_disabled) + { + round_info.cout << " Warning! All algorithms are currently " + "disabled.\n Are you sure you wish to " + "continue? [y/n] "; + round_info.cout.flush(); + + input_line = ""; + getline(cin, input_line, '\n'); + sliceString(input_line, " \t", input_tokens); + + if (!input_tokens.empty() + && (input_tokens[0][0] == 'y' || input_tokens[0][0] == 'Y')) + round_info.next_round_to_stop + = configuration.global_options.number_of_rounds + 1; + else + { + round_info.cout << '\n'; + round_info.cout.flush(); + continue; + } + } + + if (round_info.next_round_to_run == round_info.current_round) + round_info.next_round_to_run++; + + if (configuration.global_options.interactive + == Configuration::ALWAYS + || input_tokens.size() > 1) + round_info.next_round_to_stop + = round_info.current_round + rounds_to_continue; + } + else /* token == SKIP */ + { + round_info.next_round_to_stop + = round_info.current_round + rounds_to_continue; + + round_info.next_round_to_run = round_info.next_round_to_stop + 1; + } + + break; + } + else if (token == ENABLE || token == DISABLE) + { + verifyArgumentCount(input_tokens, 0, 1); + changeAlgorithmState(input_tokens, token == ENABLE); + continue; + } + else if (token == QUIT) + { + verifyArgumentCount(input_tokens, 0, 0); + round_info.abort = true; + break; + } + else if (token == VERBOSITY) + { + verifyArgumentCount(input_tokens, 0, 1); + changeVerbosity(input_tokens); + continue; + } + + if (round_info.skip + && (token == BUCHI || token == BUCHIANALYZE + || token == CONSISTENCYANALYSIS || token == EVALUATE + || token == FORMULA || token == INCONSISTENCIES + || token == RESULTANALYZE || token == STATESPACE)) + throw CommandErrorException("This command is not available because " + "the current test round was skipped."); + + /* + * If the command expects a formula identifier as a parameter, + * determine the type of the formula to which the command refers. + */ + + if (token == BUCHI || token == EVALUATE + || token == FORMULA || token == RESULTANALYZE) + formula_type = parseFormulaType(input_tokens); + + if (!external_command.empty()) + { + /* + * If the command output should be piped to an external program, + * prepare to collect the output into a string. In this case no + * output redirection (> or >>) is allowed. + */ + +#ifdef HAVE_SSTREAM + output_string = new ostringstream(); +#else + output_string = new ostrstream(); +#endif /* HAVE_SSTREAM */ + output_stream = output_string; + indent = 0; + } + else + { + /* + * Determine whether the output of the command should be saved or + * appended to a file, instead of displaying it on the console. If + * output redirection is required, open a file for output. + */ + + redirection_info = parseRedirection(input_tokens); + + if (redirection_info.first.empty()) + { + output_stream = &cout; + indent = 2; + } + else + { + output_file = new ofstream(); + + try + { + TestOperations::openFile(redirection_info.first.c_str(), + *output_file, + ios::out | (redirection_info.second + ? ios::app + : ios::trunc), + 2); + } + catch (const IOException& e) + { + delete output_file; + output_file = 0; + throw CommandErrorException(e.what()); + } + + output_stream = output_file; + indent = 0; + } + } + + switch (token) + { + case ALGORITHMS : + verifyArgumentCount(input_tokens, 0, 0); + printAlgorithmList(*output_stream, indent); + if (output_file != 0) + round_info.cout << " List of algorithms"; + break; + + case BUCHI : + { + bool use_dot = (input_tokens.size() == 3 + && input_tokens[2] == "dot"); + + verifyArgumentCount(input_tokens, 1, 2); + printBuchiAutomaton(*output_stream, indent, + formula_type, + input_tokens, + (use_dot ? Graph::DOT : Graph::NORMAL)); + if (output_file != 0) + { + round_info.cout << " Büchi automaton information"; + if (use_dot) + round_info.cout << " (in dot format)"; + } + } + break; + + case BUCHIANALYZE : + verifyArgumentCount(input_tokens, 2, 2); + printAutomatonAnalysisResults(*output_stream, indent, + parseNumber(input_tokens[1]), + parseNumber(input_tokens[2])); + if (output_file != 0) + round_info.cout << " Büchi automaton intersection emptiness " + "check analysis"; + break; + + case CONSISTENCYANALYSIS : + verifyArgumentCount(input_tokens, 1, 2); + printConsistencyAnalysisResults(*output_stream, indent, + input_tokens); + if (output_file != 0) + round_info.cout << " Consistency check result analysis"; + break; + + case EVALUATE : + verifyArgumentCount(input_tokens, 0, 2); + evaluateFormula(*output_stream, indent, formula_type, + input_tokens); + if (output_file != 0) + round_info.cout << " Formula acceptance information"; + break; + + case FORMULA : + verifyArgumentCount(input_tokens, 0, 0); + printFormula(*output_stream, indent, formula_type); + if (output_file != 0) + round_info.cout << string(" ") + (formula_type + ? "Formula" + : "Negated formula"); + break; + + case HELP : + verifyArgumentCount(input_tokens, 0, 1); + printCommandHelp(*output_stream, indent, input_tokens); + if (output_file != 0) + round_info.cout << " Command help"; + break; + + case INCONSISTENCIES : + verifyArgumentCount(input_tokens, 0, 1); + printInconsistencies(*output_stream, indent, input_tokens); + if (output_file != 0) + round_info.cout << " Model checking result consistency check " + "results for round " + + toString(round_info.current_round) + + "\n "; + break; + + case RESULTANALYZE : + verifyArgumentCount(input_tokens, 2, 3); + printCrossComparisonAnalysisResults + (*output_stream, indent, formula_type, input_tokens, + product_automaton, last_computed_product_automaton); + if (output_file != 0) + round_info.cout << " Model checking result cross-comparison " + "analysis"; + break; + + case RESULTS : + verifyArgumentCount(input_tokens, 0, 1); + printTestResults(*output_stream, indent, input_tokens); + if (output_file != 0) + round_info.cout << " Test results for round " + + toString(round_info.current_round); + break; + + case STATESPACE : + { + bool use_dot = (input_tokens.size() == 2 + && input_tokens[1] == "dot"); + + verifyArgumentCount(input_tokens, 0, 1); + printStateSpace(*output_stream, indent, input_tokens, + (use_dot ? Graph::DOT : Graph::NORMAL)); + + if (output_file != 0) + { + round_info.cout << " State space information"; + if (use_dot) + round_info.cout << " (in dot format)"; + } + } + break; + + case STATISTICS : + verifyArgumentCount(input_tokens, 0, 0); + printCollectiveStats(*output_stream, indent); + + if (output_file != 0) + round_info.cout << " Test statistics after round " + + toString(round_info.current_round); + break; + + default : + throw CommandErrorException("Unknown command (`" + + input_tokens[0] + "')."); + } + + if (output_string != 0) + { + *output_stream << ends; + string outstring(output_string->str()); + + FILE* output_pipe = popen(external_command.c_str(), "w"); + if (output_pipe == NULL) + throw ExecFailedException(external_command); + int status = fputs(outstring.c_str(), output_pipe); + if (status != EOF) + fflush(output_pipe); + pclose(output_pipe); + round_info.cout << '\n'; + round_info.cout.flush(); + if (status == EOF) + throw IOException("Error writing to pipe."); + } + else if (output_file != 0) + { + round_info.cout << string(redirection_info.second + ? " appended" + : " written") + + " to `" + redirection_info.first + "'.\n\n"; + round_info.cout.flush(); + } + } + else if (!external_command.empty()) + { + system(external_command.c_str()); + round_info.cout << '\n'; + round_info.cout.flush(); + } + } + catch (const Exception& e) + { + ::DispUtil::printTextBlock(cout, 2, string("Error: ") + e.what() + '\n', + 78); + } + + if (output_string != 0) + { +#ifndef HAVE_SSTREAM + output_string->freeze(0); +#endif /* HAVE_SSTREAM */ + delete output_string; + output_string = 0; + } + else if (output_file != 0) + { + output_file->close(); + delete output_file; + output_file = 0; + } + } + + if (product_automaton != 0) + { + ::DispUtil::printText + ("", 4, 2); + + delete product_automaton; + + ::DispUtil::printText(" ok\n", 4); + } + +#ifdef HAVE_READLINE + } + catch (...) + { + delete[] prompt_c_str; + throw; + } + + delete[] prompt_c_str; +#endif /* HAVE_READLINE */ + + signal(SIGPIPE, SIG_DFL); +} + +/* ========================================================================= */ +TokenType parseCommand(const string& token) +/* ---------------------------------------------------------------------------- + * + * Description: Parses a user command by translating a command name into + * its corresponding TokenType identifier. + * + * Argument: token -- A reference to a string containing the command. + * + * Returns: A command identifier of the enumerated type TokenType. + * + * ------------------------------------------------------------------------- */ +{ + +/* + * gcc versions prior to version 3 do not conform to the C++ standard in their + * support for the string::compare functions. Use a macro to fix this if + * necessary. + */ + +#ifdef __GNUC__ +#if __GNUC__ < 3 +#define compare(start,end,str,dummy) compare(str,start,end) +#endif +#endif + + TokenType token_type = UNKNOWN; + string::size_type len = token.length(); + bool ambiguous = false; + + if (token.empty()) + return token_type; + + switch (token[0]) + { + case 'a' : + if (token.compare(1, len - 1, "lgorithms", len - 1) == 0) + token_type = ALGORITHMS; + break; + + case 'b' : + if (len < 2) + ambiguous = true; + else if (token[1] == 'u') + { + if (len < 3) + ambiguous = true; + else if (token[2] == 'c') + { + if (len < 4) + ambiguous = true; + else if (token[3] == 'h') + { + if (len < 5) + ambiguous = true; + else if (token[4] == 'i') + { + if (len < 6) + token_type = BUCHI; + else if (token[5] == 'a' + && token.compare(6, len - 6, "nalysis", len - 6) == 0) + token_type = BUCHIANALYZE; + } + } + } + } + break; + + case 'c' : + if (len < 2) + ambiguous = true; + else if (token[1] == 'o') + { + if (len < 3) + ambiguous = true; + else if (token[2] == 'n') + { + if (len < 4) + ambiguous = true; + else if (token[3] == 's' + && token.compare(4, len - 4, "istencyanalysis", len - 4) + == 0) + token_type = CONSISTENCYANALYSIS; + else if (token[3] == 't' + && token.compare(4, len - 4, "inue", len - 4) == 0) + token_type = CONTINUE; + } + } + break; + + case 'd' : + if (token.compare(1, len - 1, "isable", len - 1) == 0) + token_type = DISABLE; + break; + + case 'e' : + if (len < 2) + ambiguous = true; + else if (token[1] == 'n') + { + if (token.compare(2, len - 2, "able", len - 2) == 0) + token_type = ENABLE; + } + else if (token[1] == 'v') + { + if (token.compare(2, len - 2, "aluate", len - 2) == 0) + token_type = EVALUATE; + } + + break; + + case 'f' : + if (token.compare(1, len - 1, "ormula", len - 1) == 0) + token_type = FORMULA; + break; + + case 'h' : + if (token.compare(1, len - 1, "elp", len - 1) == 0) + token_type = HELP; + break; + + case 'i' : + if (token.compare(1, len - 1, "nconsistencies", len - 1) == 0) + token_type = INCONSISTENCIES; + break; + + case 'q' : + if (token.compare(1, len - 1, "uit", len - 1) == 0) + token_type = QUIT; + break; + + case 'r' : + if (len < 2) + ambiguous = true; + else if (token[1] == 'e') + { + if (len < 3) + ambiguous = true; + else if (token[2] == 's') + { + if (len < 4) + ambiguous = true; + else if (token[3] == 'u') + { + if (len < 5) + ambiguous = true; + else if (token[4] == 'l') + { + if (len < 6) + ambiguous = true; + else if (token[5] == 't') + { + if (len < 7) + ambiguous = true; + else if (token[6] == 's' && len == 7) + token_type = RESULTS; + else if (token[6] == 'a') + { + if (token.compare(7, len - 7, "nalysis", len - 7) == 0) + token_type = RESULTANALYZE; + } + } + } + } + } + } + break; + + case 's' : + if (len < 2) + ambiguous = true; + else if (token[1] == 'k') + { + if (token.compare(2, len - 2, "ip", len - 2) == 0) + token_type = SKIP; + } + else if (token[1] == 't') + { + if (len < 3) + ambiguous = true; + else if (token[2] == 'a') + { + if (len < 4) + ambiguous = true; + else if (token[3] == 't') + { + if (len < 5) + ambiguous = true; + else if (token[4] == 'e') + { + if (token.compare(5, len - 5, "space", len - 5) == 0) + token_type = STATESPACE; + } + else if (token[4] == 'i') + { + if (token.compare(5, len - 5, "stics", len - 5) == 0) + token_type = STATISTICS; + } + } + } + } + break; + + case 'v' : + if (token.compare(1, len - 1, "erbosity", len - 1) == 0) + token_type = VERBOSITY; + break; + } + + if (ambiguous) + throw CommandErrorException("Ambiguous command."); + + return token_type; + +#ifdef __GNUC__ +#if __GNUC__ < 3 +#undef compare +#endif +#endif +} + +/* ========================================================================= */ +void verifyArgumentCount + (const vector& command, + vector::size_type min_arg_count, + vector::size_type max_arg_count) +/* ---------------------------------------------------------------------------- + * + * Description: Verifies that the number of arguments given for a user + * command is between a given interval. + * + * Arguments: command -- A reference to a constant vector of + * strings (the user command and its + * arguments). + * min_arg_count -- Smallest allowed number of arguments. + * max_arg_count -- Largest allowed number of arguments. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (command.size() < min_arg_count + 1) + throw CommandErrorException("Too few arguments for command."); + else if (command.size() > max_arg_count + 1) + throw CommandErrorException("Too many arguments for command."); +} + +/* ========================================================================= */ +pair parseRedirection + (vector& input_tokens) +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether the last argument to a user command specifies + * output redirection. If redirection is requested, the + * "argument" specifying the redirection is removed from the + * vector of strings forming the command. + * + * Argument: input_tokens -- A reference to a vector of strings giving + * the user command and its arguments. + * + * Returns: A pair whose first component is the name of the output file + * (or the empty string if no redirection was specified) and + * whose second component determines whether the output should + * be appended to the file instead of creating a new file. + * + * ------------------------------------------------------------------------- */ +{ + string filename; + bool append = false; + + if (!input_tokens.empty()) + { + string& token = input_tokens.back(); + + if (token[0] == '>') + { + if (token.length() > 1) + { + if (token[1] == '>') + { + if (token.length() > 2) + { + append = true; + filename = token.substr(2); + input_tokens.pop_back(); + } + } + else + { + filename = token.substr(1); + input_tokens.pop_back(); + } + } + } + else if (input_tokens.size() >= 2) + { + string& token = *(input_tokens.rbegin() + 1); + + if (token[0] == '>' && (token.length() == 1 + || (token.length() == 2 && token[1] == '>'))) + { + filename = input_tokens.back(); + append = (token.length() == 2); + input_tokens.pop_back(); + input_tokens.pop_back(); + } + } + } + + return make_pair(filename, append); +} + +/* ========================================================================= */ +bool parseFormulaType(vector& input_tokens) +/* ---------------------------------------------------------------------------- + * + * Description: Tests whether the first argument of a command specifies a + * formula (i.e., whether the first argument of the command is + * either a `+' or a `-'). If it is, the argument is removed + * from the vector of strings forming the command. + * + * Argument: input_tokens -- A reference to a vector of strings giving + * the user command. + * + * Returns: A truth value according to whether a formula or its negation + * was specified; the effect of specifying no formula type is + * the same as giving a `+' as an argument (i.e. the formula + * type defaults to the positive formula). + * + * ------------------------------------------------------------------------- */ +{ + bool formula_type = true; + + if (input_tokens.size() >= 2) + { + formula_type = (input_tokens[1] != "-"); + + if (input_tokens[1].length() == 1 + && (input_tokens[1][0] == '+' || input_tokens[1][0] == '-')) + input_tokens.erase(input_tokens.begin()); + } + + return formula_type; +} + +/* ========================================================================= */ +void verifyNumber + (unsigned long int number, unsigned long int max, const char* error_message) +/* ---------------------------------------------------------------------------- + * + * Description: Checks that a given unsigned long integer is less than a + * given maximum value. Throws an exception with an error + * message if this is not the case. + * + * Argument: number -- Number to be tested. + * max -- Value the number is to be tested against. + * error_message -- Error message. + * + * Returns: Nothing. Throws an exception if the check fails. + * + * ------------------------------------------------------------------------- */ +{ + if (number >= max) + throw CommandErrorException(string(error_message) + " (" + + toString(number) + ")."); +} + +} diff --git a/lbtt/src/UserCommandReader.h b/lbtt/src/UserCommandReader.h new file mode 100644 index 000000000..da087daca --- /dev/null +++ b/lbtt/src/UserCommandReader.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef USERCOMMANDREADER_H +#define USERCOMMANDREADER_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include "Alloc.h" +#include "Configuration.h" +#include "Exception.h" + +using namespace std; + +extern Configuration configuration; + +extern bool user_break; + + + +/****************************************************************************** + * + * Interactive user command interface. + * + *****************************************************************************/ + +namespace UserCommandInterface +{ + +void executeUserCommands(); /* Function for reading and + * executing user commands + * at the end of a test + * round. + */ + +enum TokenType /* User commands. */ + {ALGORITHMS, BUCHI, BUCHIANALYZE, + CONSISTENCYANALYSIS, CONTINUE, DISABLE, ENABLE, + EVALUATE, FORMULA, HELP, INCONSISTENCIES, QUIT, + RESULTANALYZE, RESULTS, SKIP, STATESPACE, + STATISTICS, VERBOSITY, UNKNOWN, _NO_INPUT}; + +TokenType parseCommand(const string& token); /* Translates a command + * name into its + * corresponding + * TokenType identifier. + */ + +void verifyArgumentCount /* Checks that the */ + (const vector& /* number of arguments */ + arguments, /* for a command is */ + vector::size_type /* within given limits. */ + min_arg_count, + vector::size_type + max_arg_count); + +pair parseRedirection /* Checks whether an */ + (vector& input_tokens); /* user command given + * will require + * redirecting its + * output to a file. + */ + +bool parseFormulaType /* Checks whether an */ + (vector& input_tokens); /* user command + * specified a positive + * or a negative + * formula. + */ + +void verifyNumber /* Checks that a given */ + (unsigned long int number, /* number is less than a */ + unsigned long int max, /* given maximum value. */ + const char* error_message); + + + +/****************************************************************************** + * + * An exception class for reporting errors in user commands. + * + *****************************************************************************/ + +class CommandErrorException : public Exception +{ +public: + CommandErrorException /* Constructor. */ + (const string& message + = "Syntax error in command."); + + /* default copy constructor */ + + ~CommandErrorException() throw(); /* Destructor. */ + + CommandErrorException& operator= /* Assignment operator. */ + (const CommandErrorException& e); +}; + + + +/****************************************************************************** + * + * Inline function definitions for class CommandErrorException. + * + *****************************************************************************/ + +/* ========================================================================= */ +inline CommandErrorException::CommandErrorException + (const string& message) : + Exception(message) +/* ---------------------------------------------------------------------------- + * + * Description: Constructor for class CommandErrorException. Creates an + * exception object and initializes it with an error message. + * + * Arguments: message -- A reference to a constant string containing the + * error message. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline CommandErrorException::~CommandErrorException() throw() +/* ---------------------------------------------------------------------------- + * + * Description: Destructor for class CommandErrorException. + * + * Arguments: None. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ +} + +/* ========================================================================= */ +inline CommandErrorException& +CommandErrorException::operator=(const CommandErrorException& e) +/* ---------------------------------------------------------------------------- + * + * Description: Assignment operator for class CommandErrorException. Copies + * the contents of an exception object to another. + * + * Arguments: e -- A reference to a constant CommandErrorException. + * + * Returns: A reference to the object assigned to. + * + * ------------------------------------------------------------------------- */ +{ + Exception::operator=(e); + return *this; +} + +} + +#endif /* !USERCOMMANDREADER_H */ diff --git a/lbtt/src/UserCommands.cc b/lbtt/src/UserCommands.cc new file mode 100644 index 000000000..ae5a6630a --- /dev/null +++ b/lbtt/src/UserCommands.cc @@ -0,0 +1,2282 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include "DispUtil.h" +#include "Exception.h" +#include "PathEvaluator.h" +#include "SharedTestData.h" +#include "StatDisplay.h" +#include "StringUtil.h" +#include "TestRoundInfo.h" +#include "TestStatistics.h" +#include "UserCommandReader.h" +#include "UserCommands.h" + +/****************************************************************************** + * + * Implementations for the user commands. + * + *****************************************************************************/ + +namespace UserCommands +{ + +using namespace ::DispUtil; +using namespace ::SharedTestData; +using namespace ::StatDisplay; +using namespace ::StringUtil; +using namespace ::UserCommandInterface; + +/* ========================================================================= */ +void computeProductAutomaton + (ProductAutomaton*& product_automaton, + const BuchiAutomaton& buchi_automaton, + pair& last_automaton, + const pair& new_automaton) +/* ---------------------------------------------------------------------------- + * + * Description: Computes a product automaton. + * + * Arguments: product_automaton -- A reference to a pointer giving the + * storage location of the generated + * automaton. + * buchi_automaton -- The Büchi automaton to be used for + * computing the product automaton. + * last_automaton -- A pair telling the algorithm and the + * formula last used for computing an + * automaton (for testing whether the + * result is already available). + * new_automaton -- A pair telling the algorithm and the + * formula which are to be used for + * computing the automaton. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (product_automaton == 0 || last_automaton != new_automaton) + { + if (product_automaton != 0) + { + printText("", 4, 2); + + delete product_automaton; + product_automaton = 0; + + printText(" ok\n", 4); + } + + printText("", 0, 2); + + try + { + product_automaton = new ProductAutomaton(); + product_automaton->computeProduct + (buchi_automaton, *(round_info.statespace), + configuration.global_options.product_mode == Configuration::GLOBAL); + } + catch (...) + { + if (product_automaton != 0) + { + delete product_automaton; + product_automaton = 0; + } + + printText(" error\n", 0); + + try + { + throw; + } + catch (const ::Graph::ProductAutomaton::ProductSizeException&) + { + throw CommandErrorException("Product may be too large"); + } + catch (const UserBreakException&) + { + throw CommandErrorException("User break"); + } + catch (const bad_alloc&) + { + throw CommandErrorException("Out of memory"); + } + } + + printText(" ok\n", 0); + + last_automaton = new_automaton; + } +} + +/* ========================================================================= */ +void printAlgorithmList(ostream& stream, int indent) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `algorithms', i.e., writes a list + * of algorithms used in the tests to a stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of the + * output. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); + + estream << string(indent, ' ') + "List of implementations:\n"; + + for (unsigned long int algorithm_id = 0; + algorithm_id < round_info.number_of_translators; + ++algorithm_id) + { + estream << string(indent + 2, ' ') + + configuration.algorithmString(algorithm_id) + + " (" + + (configuration.algorithms[algorithm_id].enabled + ? "en" + : "dis") + + "abled)\n"; + } + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void synchronizePrefixAndCycle + (deque::size_type, + ALLOC(Graph::Graph::size_type) >& prefix, + deque::size_type, + ALLOC(Graph::Graph::size_type) >& cycle) +/* ---------------------------------------------------------------------------- + * + * Description: Function for `synchronizing' a sequence of states consisting + * of a prefix and a repeating cycle. This means removing from + * the end of the prefix the longest sequence of states that + * forms a postfix of the cycle. The states in the cycle will be + * `rotated' accordingly. + * + * Arguments: prefix -- A reference to a deque of state identifiers + * forming the prefix of the state sequence. + * cycle -- A reference to a deque of state identifiers + * representing the states in the repeating cycle. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (cycle.empty()) + throw CommandErrorException("internal error"); + + while (!prefix.empty() && prefix.back() == cycle.back()) + { + prefix.pop_back(); + cycle.push_front(cycle.back()); + cycle.pop_back(); + } + + Graph::Graph::size_type state_id = cycle.front(); + deque::size_type, + ALLOC(Graph::Graph::size_type) >::const_iterator + s; + + for (s = cycle.begin() + 1; s != cycle.end() && *s == state_id; ++s) + ; + + if (s == cycle.end()) + { + cycle.clear(); + cycle.push_front(state_id); + } +} + +/* ========================================================================= */ +void printCrossComparisonAnalysisResults + (ostream& stream, int indent, bool formula_type, + const vector& input_tokens, + ProductAutomaton*& product_automaton, + pair& last_product_automaton) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `resultanalysis', i.e., analyzes + * a discrepancy between the results of two algorithms (or one + * algorithm against the path algorithm) by searching for a + * system execution producting contradicting results and then + * displaying the execution. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the + * left of the output. + * formula_type -- Tells the LTL formula for which + * the analysis is to be performed. + * input_tokens -- A reference to a vector + * containing the arguments of the + * command. + * product_automaton -- A reference to a pointer telling + * the storage location of the + * generated product automaton. + * last_product_automaton -- A pair telling the algorithm and + * the formula last used for + * computing a product automaton + * (for testing whether the result + * is already available). + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); + + unsigned long int algorithm1, algorithm2; + bool path_compare = false; + + if (!configuration.global_options.do_comp_test) + throw CommandErrorException("This command is available only when the " + "model checking result cross-comparison test " + "is enabled."); + + if (input_tokens[1] == "p") + { + path_compare = true; + algorithm1 = parseNumber(input_tokens[2]); + algorithm2 = round_info.number_of_translators; + } + else + { + algorithm1 = parseNumber(input_tokens[1]); + if (input_tokens[2] == "p") + { + path_compare = true; + algorithm2 = round_info.number_of_translators; + } + else + algorithm2 = parseNumber(input_tokens[2]); + } + + if (path_compare + && !(configuration.global_options.statespace_generation_mode + & Configuration::PATH)) + throw CommandErrorException("This feature is available only when using " + "paths as state spaces."); + + verifyNumber(algorithm1, round_info.number_of_translators, + "No such implementation"); + + int formula = (formula_type ? 0 : 1); + int generator_formula = formula; + + if (configuration.formula_options.output_mode != Configuration::NNF) + generator_formula += 2; + + const AutomatonStats* stats1; + const AutomatonStats* stats2; + + stats1 = &test_results[algorithm1].automaton_stats[formula]; + + if (!path_compare) + { + verifyNumber(algorithm2, round_info.number_of_translators, + "No such implementation"); + + if (algorithm1 == algorithm2) + throw CommandErrorException("Implementation identifiers must be " + "different."); + } + + stats2 = &test_results[algorithm2].automaton_stats[formula]; + + if (!stats1->crossComparisonPerformed(algorithm2)) + { + printTextBlock(stream, indent, + "Model checking result cross-comparison was not " + "performed between " + + configuration.algorithmString(algorithm1) + + " and " + configuration.algorithmString(algorithm2) + + ".\n", + 78); + return; + } + + if (stats1->cross_comparison_stats[algorithm2].second == 0) + { + printTextBlock(stream, indent, + "No inconsistencies detected in the cross-comparison of " + "results given by " + + configuration.algorithmString(algorithm1) + + " and " + + configuration.algorithmString(algorithm2) + ".\n", + 78); + return; + } + + estream << string(indent, ' ') + + "Model checking result cross-comparison analysis:\n" + + string(indent + 2, ' ') + "Formula: " + << *round_info.formulae[generator_formula] + << "\n\n"; + + StateSpace::size_type state; + + if (input_tokens.size() == 3) + { + /* + * If no state identifier was given as a command argument, search for a + * system state in which the results of the two algorithms (or the result + * of an algorithm and the path checking algorithm) differ. + */ + + estream << string(indent + 2, ' ') + "The cross-comparison check failed " + "in " + + toString(stats1->cross_comparison_stats[algorithm2].second) + + " ("; + + changeStreamFormatting(stream, 0, 2, ios::fixed); + estream << static_cast + (stats1->cross_comparison_stats[algorithm2].second) + / round_info.real_emptiness_check_size + * 100.0; + restoreStreamFormatting(stream); + + estream << "%) of " + toString(round_info.real_emptiness_check_size) + + " test cases.\n\n"; + + for (state = 0; + stats1->emptiness_check_result[state] + == stats2->emptiness_check_result[state]; + ++state) + ; + } + else + { + /* + * Otherwise use the state given as the command argument. + */ + + state = parseNumber(input_tokens[3]); + + verifyNumber(state, round_info.statespace->size(), "No such state"); + + if (state >= round_info.real_emptiness_check_size) + { + printTextBlock(stream, indent, + "Model checking result cross-comparison test was not " + "performed between " + + configuration.algorithmString(algorithm1) + + " and " + + configuration.algorithmString(algorithm2) + + " in state " + + toString(state) + + " of the state space.\n", + 78); + return; + } + + if (stats1->emptiness_check_result[state] + == stats2->emptiness_check_result[state]) + { + printTextBlock(stream, indent, + "No inconsistency detected between the results given by " + + configuration.algorithmString(algorithm1) + + " and " + + configuration.algorithmString(algorithm2) + + " in state " + + toString(state) + + " of the state space.\n", + 78); + return; + } + } + + /* + * Translate the formula to be analyzed into a Büchi automaton using the + * algorithm that claims the existence of an accepting cycle beginning in + * the state. + */ + + unsigned long int accepting_algorithm + = (stats1->emptiness_check_result[state] ? algorithm1 : algorithm2); + + unsigned long int rejecting_algorithm = (accepting_algorithm == algorithm1 + ? algorithm2 + : algorithm1); + + deque system_prefix; + deque system_cycle; + deque + automaton_prefix; + deque + automaton_cycle; + + if (!path_compare || accepting_algorithm == algorithm1) + { + /* + * Compute the synchronous product of the automaton and the state space. + */ + + computeProductAutomaton(product_automaton, + *(test_results[accepting_algorithm]. + automaton_stats[formula].buchi_automaton), + last_product_automaton, + make_pair(accepting_algorithm, formula_type)); + + /* + * Search the product automaton for an accepting cycle. + */ + + pair, + deque > + execution; + + try + { + printText("", 0, 2); + product_automaton->findAcceptingExecution(state, execution); + } + catch (...) + { + printText(" error\n", 0); + throw; + } + + printText(" ok\n\n", 0); + + /* + * Separate the parallel executions of the system and the automaton into + * prefixes and cycles. + */ + + while (!execution.first.empty()) + { + automaton_prefix.push_back(execution.first.front().first); + system_prefix.push_back(execution.first.front().second); + execution.first.pop_front(); + } + + while (!execution.second.empty()) + { + automaton_cycle.push_back(execution.second.front().first); + system_cycle.push_back(execution.second.front().second); + execution.second.pop_front(); + } + + synchronizePrefixAndCycle(automaton_prefix, automaton_cycle); + synchronizePrefixAndCycle(system_prefix, system_cycle); + } + else + { + StateSpace::size_type loop_state + = (*((*round_info.statespace)[round_info.statespace->size() - 1]. + edges().begin()))->targetNode(); + + if (state < loop_state) + { + for (StateSpace::size_type path_state = state; path_state < loop_state; + path_state++) + system_prefix.push_back(path_state); + + for (StateSpace::size_type path_state = loop_state; + path_state < round_info.statespace->size(); + path_state++) + system_cycle.push_back(path_state); + } + else + { + for (StateSpace::size_type path_state = state; + path_state < round_info.statespace->size(); + path_state++) + system_cycle.push_back(path_state); + + for (StateSpace::size_type path_state = loop_state; + path_state < state; + path_state++) + system_cycle.push_back(path_state); + } + } + + state = system_prefix.size(); + + /* + * Construct a path in the state space from the system prefix and the cycle. + */ + + vector path; + + path.insert(path.end(), system_prefix.begin(), system_prefix.end()); + path.insert(path.end(), system_cycle.begin(), system_cycle.end()); + + /* + * Write information about the execution to the output stream. That is, + * display the states in the infinite execution (a prefix and a cycle) + * and tell which one of the algorithms would accept and which one + * would reject the execution (or show whether the algorithm compared + * against the path checking algorithm accepted or rejected the + * execution). + */ + + estream << string(indent + 2, ' ') + "Execution M:\n"; + + printPath(stream, indent + 4, system_prefix, system_cycle, + *(round_info.statespace)); + + estream << string(indent + 4, ' ') + + "accepted by: " + + configuration.algorithmString(accepting_algorithm) + '\n' + + string(indent + 4, ' ') + "rejected by: " + + configuration.algorithmString(rejecting_algorithm) + + "\n\n" + + string(indent + 2, ' ') + + "Analysis of the formula in the execution:\n"; + + /* + * Model check the formula separately in the obtained execution to find out + * which one of the algorithms was in error. + */ + + Ltl::PathEvaluator path_evaluator; + bool result = path_evaluator.evaluate + (*round_info.formulae[generator_formula], *(round_info.statespace), path, + state); + + path_evaluator.print(stream, indent + 4); + + printTextBlock(stream, indent + 2, + string(" \n The formula is ") + (result ? "" : "not ") + + "satisfied in the execution, which should therefore be " + + (result ? "accep" : "rejec") + + "ted by all Büchi automata that represent the formula " + "correctly. This suggests an error in implementation " + + (path_compare + ? configuration.algorithmString(algorithm1) + : configuration.algorithmString(result + ? rejecting_algorithm + : accepting_algorithm)) + + ".\n", + 78); + + if (!result) + printAcceptingCycle(stream, indent + 2, accepting_algorithm, + automaton_prefix, automaton_cycle, + *(test_results[accepting_algorithm]. + automaton_stats[formula].buchi_automaton)); +} + +/* ========================================================================= */ +void printConsistencyAnalysisResults + (ostream& stream, int indent, + const vector& input_tokens) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `consistencyanalysis', i.e., + * analyzes a discrepancy detected in the model checking result + * consistency check for an implementation. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of + * the output. + * input_tokens -- A reference to a vector containing the + * arguments of the command. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); + + if (!configuration.global_options.do_cons_test) + throw CommandErrorException("This command is available only when the " + "model checking result consistency check is " + "enabled."); + + unsigned long int algorithm_id = parseNumber(input_tokens[1]); + + verifyNumber(algorithm_id, round_info.number_of_translators, + "No such implementation"); + + if (test_results[algorithm_id].consistency_check_result == -1) + { + printTextBlock(stream, indent, + "Model checking result consistency check was not performed " + "on implementation " + + configuration.algorithmString(algorithm_id) + + ".\n", + 78); + return; + } + + if (test_results[algorithm_id].consistency_check_result == 1) + { + printTextBlock(stream, indent, + "Implementation " + + configuration.algorithmString(algorithm_id) + + " passed the model checking result consistency check.\n", + 78); + return; + } + + StateSpace::size_type state; + int formula = (configuration.formula_options.output_mode + == Configuration::NNF + ? 0 + : 2); + + estream << string(indent, ' ') + "Consistency check result analysis:\n" + + string(indent + 2, ' ') + "Implementation: " + + configuration.algorithmString(algorithm_id) + '\n' + + string(indent + 2, ' ') + "Positive formula: " + << *round_info.formulae[formula] + << "\n\n"; + + if (input_tokens.size() == 2) + { + /* + * If no state identifier was given as a parameter, search for a state in + * which the consistency check failed. + */ + + for (state = 0; + test_results[algorithm_id].automaton_stats[0]. + emptiness_check_result.test(state) + || test_results[algorithm_id].automaton_stats[1]. + emptiness_check_result.test(state); + ++state) + ; + } + else + { + /* + * Otherwise use the state given as a parameter for the command. + */ + + state = parseNumber(input_tokens[2]); + verifyNumber(state, round_info.statespace->size(), "No such state"); + + if (state >= round_info.real_emptiness_check_size) + { + printTextBlock(stream, indent, + "Model checking result consistency check was not " + "performed on implementation " + + configuration.algorithmString(algorithm_id) + + " in state " + toString(state) + " of the state " + "space.\n", + 78); + return; + } + if (test_results[algorithm_id].automaton_stats[0]. + emptiness_check_result.test(state) + || test_results[algorithm_id].automaton_stats[1]. + emptiness_check_result.test(state)) + { + printTextBlock(stream, indent, + "No discrepancy detected in the model checking result " + "consistency check on implementation " + + configuration.algorithmString(algorithm_id) + + " in state " + toString(state) + " of the state " + "space.\n", + 78); + return; + } + } + + vector path; + deque prefix, cycle; + map, ALLOC(StateSpace::size_type) > + ordering; + StateSpace::size_type state_count = 0; + StateSpace::size_type loop_state; + + /* + * Construct a vector of state identifiers representing a path by + * traversing the state space until some state is encountered twice. + */ + + while (1) + { + path.push_back(state); + ordering[state] = state_count; + state_count++; + + state = (*((*round_info.statespace)[state].edges().begin()))->targetNode(); + + if (ordering.find(state) != ordering.end()) + break; + } + + loop_state = ordering[state]; + + for (StateSpace::size_type s = 0; s < loop_state; s++) + prefix.push_back(path[s]); + + for (StateSpace::size_type s = loop_state; s < path.size(); s++) + cycle.push_back(path[s]); + + estream << string(indent + 2, ' ') + "Execution M:\n"; + printPath(stream, indent + 4, prefix, cycle, *(round_info.statespace)); + + estream << '\n' + string(indent + 2, ' ') + + "Analysis of the formula in the execution:\n"; + + Ltl::PathEvaluator path_evaluator; + bool result = path_evaluator.evaluate + (*round_info.formulae[formula], *(round_info.statespace), path, + loop_state); + + path_evaluator.print(stream, indent + 4); + + printTextBlock(stream, indent + 2, + string(" \n The formula is ") + (result ? "" : "not ") + + "satisfied in the execution. It seems that the automaton " + "constructed for the " + + (result ? "posi" : "nega") + + "tive formula rejects the execution incorrectly.\n", + 78); +} + +/* ========================================================================= */ +void printAutomatonAnalysisResults + (ostream& stream, int indent, unsigned long int algorithm1, + unsigned long int algorithm2) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `buchianalysis', i.e., analyzes + * an inconsistency detected in the intersection emptiness check + * for two Büchi automata constructed for the formula and its + * negation. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of the + * output. + * algorithm1, -- Identifiers of the algorithms for which the + * algorithm2 should be performed. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); + + if (!configuration.global_options.do_intr_test) + throw CommandErrorException("This command is available only when the " + "Büchi automata intersection emptiness check " + "is enabled."); + + verifyNumber(algorithm1, round_info.number_of_translators, + "No such implementation"); + verifyNumber(algorithm2, round_info.number_of_translators, + "No such implementation"); + + int test_result + = test_results[algorithm1].automaton_stats[0]. + buchi_intersection_check_stats[algorithm2]; + + if (test_result != 0) + { + printTextBlock(stream, indent, + string("The automata intersection check was ") + + (test_result == -1 ? "not performed" : "successful") + ' ' + + (algorithm1 == algorithm2 ? "o" : "betwee") + + "n implementation" + + (algorithm1 == algorithm2 ? "" : "s") + ' ' + + configuration.algorithmString(algorithm1) + + (algorithm1 == algorithm2 + ? "" + : " (positive formula) and " + + configuration.algorithmString(algorithm2) + + " (negative formula)") + + ".\n", + 78); + + return; + } + + int formula + = (configuration.formula_options.output_mode == Configuration::NNF + ? 0 + : 2); + + estream << string(indent, ' ') + + "Automata intersection emptiness check analysis:\n" + + string(indent + 2, ' ') + "Positive formula: "; + + round_info.formulae[formula]->print(estream); + + estream << '\n' + string(indent + 2, ' ') + "Negative formula: "; + round_info.formulae[formula + 1]->print(estream); + + estream << '\n' + string(indent + 2, ' ') + "Implementation" + + (algorithm1 != algorithm2 ? "s:" : ": ") + " " + + configuration.algorithmString(algorithm1); + + if (algorithm1 != algorithm2) + estream << " (positive formula)\n" + string(indent + 20, ' ') + + configuration.algorithmString(algorithm2) + + " (negative formula)"; + + estream << "\n\n"; + estream.flush(); + + /* + * Compute the intersection of the two automata. + */ + + BuchiAutomaton* a = 0; + + try + { + map, ALLOC(BuchiAutomaton::StateIdPair) > + intersection_state_mapping; + + try + { + printText("", 0, 2); + + a = BuchiAutomaton::intersect + (*(test_results[algorithm1].automaton_stats[0].buchi_automaton), + *(test_results[algorithm2].automaton_stats[1].buchi_automaton), + &intersection_state_mapping); + } + catch (...) + { + printText(" error\n", 0); + throw; + } + + printText(" ok\n", 0); + + /* + * Search the intersection automaton for an accepting execution. This is + * done `indirectly' as follows: + * + * 1. Convert the intersection automaton into a StateSpace, i.e., + * construct a StateSpace which shares a similar transition + * relation with the automaton. + * 2. Construct a `dummy' single-state Büchi automaton which + * accepts all inputs. + * 3. Compute the synchronous product of the state space and the + * automaton. The obtained product automaton again has the same + * transition relation as the original intersection automaton + * (state identifiers may have been permuted; however, the + * product automaton contains information about the + * correspondence between the state identifiers of the product + * automaton and the original intersection automaton). + * 4. Switch the roles of the state space and the Büchi automaton in + * the product to effectively transfer state acceptance + * information to the product automaton from the automaton + * intersection. + * 5. Search an accepting cycle in the product automaton. This then + * corresponds to an accepting execution of the intersection + * automaton. + */ + + StateSpace automaton_as_statespace + (configuration.formula_options.formula_generator. + number_of_available_variables, + a->size()); + + /* + * 1. + */ + + for (BuchiAutomaton::size_type state = 0; state < a->size(); state++) + { + for (GraphEdgeContainer::const_iterator transition + = (*a)[state].edges().begin(); + transition != (*a)[state].edges().end(); + ++transition) + automaton_as_statespace.connect(state, (*transition)->targetNode()); + } + + /* + * 2. + */ + + BuchiAutomaton dummy_automaton(1, 0, 0); + dummy_automaton.connect(0, 0, &Ltl::True::construct()); + + /* + * 3. + */ + + ProductAutomaton p; + p.computeProduct(dummy_automaton, automaton_as_statespace, false); + + /* + * 4. + */ + + p.buchi_automaton = a; + p.statespace_size = 1; + + /* + * 5. + */ + + pair, + deque > + execution; + + try + { + printText("", + 0, 2); + + p.findAcceptingExecution(0, execution); + } + catch (...) + { + printText(" error\n", 0); + throw; + } + + printText(" ok\n\n", 0); + + /* + * Extract the state identifiers belonging to the execution of the + * intersection automaton from the result. + */ + + deque prefix; + deque cycle; + vector path; + + while (!execution.first.empty()) + { + prefix.push_back(execution.first.front().first); + path.push_back(execution.first.front().first); + execution.first.pop_front(); + } + + while (!execution.second.empty()) + { + cycle.push_back(execution.second.front().first); + path.push_back(execution.second.front().first); + execution.second.pop_front(); + } + + /* + * Construct an execution accepted by both of the automata. This is done + * by giving suitable truth assignments for the atomic propositions in + * selected states of the state space to which the intersection automaton + * was converted. + */ + + GraphEdgeContainer::const_iterator transition; + + path.push_back(cycle.front()); + + for (vector + ::size_type state = 0; + state + 1 < path.size(); + state++) + { + for (transition = (*a)[path[state]].edges().begin(); + (*transition)->targetNode() != path[state + 1]; + ++transition) + ; + + automaton_as_statespace[path[state]].positiveAtoms() + = static_cast(*transition) + ->guard().findPropositionalModel + (configuration.formula_options.formula_generator. + number_of_available_variables); + } + + path.pop_back(); + + delete a; + a = 0; + + /* + * Display the input sequence accepted by both automata. + */ + + estream << string(indent + 2, ' ') + + "Execution M accepted by both automata:\n"; + + printPath(stream, indent + 4, prefix, cycle, automaton_as_statespace); + + estream << '\n'; + + /* + * For each of the original automata, display the accepting runs that + * these automata have on the input sequence. + */ + + deque + aut_prefix; + + deque + aut_cycle; + + const deque* + original_execution_states; + deque* + new_execution_states; + + for (int i = 0; i < 2; i++) + { + aut_prefix.clear(); + aut_cycle.clear(); + + for (int j = 0; j < 2; j++) + { + if (j == 0) + { + original_execution_states = &prefix; + new_execution_states = &aut_prefix; + } + else + { + original_execution_states = &cycle; + new_execution_states = &aut_cycle; + } + + for (deque::const_iterator + execution_state = original_execution_states->begin(); + execution_state != original_execution_states->end(); + ++execution_state) + { + new_execution_states->push_back + (i == 0 + ? intersection_state_mapping[*execution_state].first + : intersection_state_mapping[*execution_state].second); + } + } + + synchronizePrefixAndCycle(aut_prefix, aut_cycle); + + printAcceptingCycle(stream, indent + 2, + (i == 0 ? algorithm1 : algorithm2), + aut_prefix, aut_cycle, + *(test_results[(i == 0 ? algorithm1 : algorithm2)]. + automaton_stats[i].buchi_automaton)); + } + + estream << string(indent + 2, ' ') + + "Analysis of the positive formula in the execution M:\n"; + + Ltl::PathEvaluator path_evaluator; + bool result = path_evaluator.evaluate + (*round_info.formulae[formula], automaton_as_statespace, path, + prefix.size()); + + path_evaluator.print(stream, indent + 4); + + printTextBlock(stream, indent + 2, + " \n The positive formula is " + + string(result ? "" : "not ") + + "satisfied in the execution. This suggests that the " + "Büchi automaton constructed for the " + + (result ? "nega" : "posi") + "tive formula " + + (algorithm1 == algorithm2 + ? "" + : "(the automaton constructed by implementation " + + configuration.algorithmString(result ? algorithm2 + : algorithm1) + + ") ") + + "is incorrect.\n", + 78); + } + catch (...) + { + if (a != 0) + delete a; + } +} + +/* ========================================================================= */ +void printPath + (ostream& stream, int indent, + const deque& prefix, + const deque& cycle, + const StateSpace& path) +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a single execution path to a stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of output. + * prefix -- A reference to a constant deque of state + * identifiers forming the prefix of the execution. + * cycle -- A reference to a constant deque of state + * identifiers forming the infinitely repeating + * cycle in the execution. + * path -- A reference to the constant state space from + * which the state identifiers in the deques are + * taken. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); + + const deque* + execution_states; + + for (int counter = 0; counter < 2; counter++) + { + estream << string(indent, ' '); + + if (counter == 0) + { + execution_states = &prefix; + estream << "prefix:"; + } + else + { + execution_states = &cycle; + estream << "cycle: "; + } + + estream << string(6, ' ') + "< "; + + if (!execution_states->empty()) + { + bool first_printed; + + for (deque + ::const_iterator execution_state = execution_states->begin(); + execution_state != execution_states->end(); + ++execution_state) + { + if (execution_state != execution_states->begin()) + estream << ",\n" + string(indent + 15, ' '); + + estream << 's' + toString(*execution_state) + " {"; + + first_printed = false; + + for (unsigned long int proposition = 0; + proposition < path.numberOfPropositions(); + proposition++) + { + if (path[*execution_state].positiveAtoms().test(proposition)) + { + if (first_printed) + estream << ", "; + else + first_printed = true; + + estream << 'p' + toString(proposition); + } + } + + estream << '}'; + } + } + else + estream << "empty"; + + estream << " >\n"; + } +} + +/* ========================================================================= */ +void printAcceptingCycle + (ostream& stream, int indent, + vector::size_type + algorithm_id, + const deque& + prefix, + const deque& + cycle, + const BuchiAutomaton& automaton) +/* ---------------------------------------------------------------------------- + * + * Description: Writes information about a single automaton execution to a + * stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave to the left of + * output. + * algorithm_id -- Identifier for an algorithm. + * prefix -- A reference to a constant deque of state + * identifiers forming the prefix of the + * execution. + * cycle -- A reference to a constant deque of state + * identifiers forming the infinitely + * repeating cycle in the execution. + * automaton -- A reference to a constant BuchiAutomaton + * from which the state identifiers in the + * deques are taken. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); + + const deque* + execution_states; + bool first_printed; + + printTextBlock(stream, indent, + "On input M, the automaton constructed by implementation " + + configuration.algorithmString(algorithm_id) + " (with " + + toString(automaton.numberOfAcceptanceSets()) + + " acceptance set" + + (automaton.numberOfAcceptanceSets() == 1 ? "" : "s") + + ") has the following accepting execution:", + 78); + + for (int counter = 0; counter < 2; counter++) + { + estream << string(indent + 2, ' '); + + if (counter == 0) + { + estream << "prefix"; + execution_states = &prefix; + } + else + { + estream << "cycle"; + execution_states = &cycle; + } + + estream << ":\n"; + + string execution_string = "<"; + + if (!execution_states->empty()) + { + first_printed = false; + + for (deque::const_iterator + execution_state = execution_states->begin(); + execution_state != execution_states->end(); + ++execution_state) + { + if (first_printed) + execution_string += ", "; + else + first_printed = true; + execution_string += toString(*execution_state); + + if (counter == 1) + { + bool first_acceptance_set_printed = false; + + for (unsigned long int accept_set = 0; + accept_set < automaton.numberOfAcceptanceSets(); + accept_set++) + { + if (automaton[*execution_state].acceptanceSets().test(accept_set)) + { + if (first_acceptance_set_printed) + execution_string += ", "; + else + { + first_acceptance_set_printed = true; + execution_string += " [acceptance sets: {"; + } + execution_string += toString(accept_set); + } + } + + if (first_acceptance_set_printed) + execution_string += "}]"; + } + } + } + else + execution_string += "empty"; + + execution_string += ">"; + + printTextBlock(stream, indent + 4, execution_string, 78); + } + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void printBuchiAutomaton + (ostream& stream, int indent, bool formula_type, + vector& input_tokens, + Graph::GraphOutputFormat fmt) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `buchi', i.e., writes information + * about a set of states of a Büchi automaton computed using a + * given algorithm to a stream. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of + * the output. + * formula_type -- Determines the formula to be translated + * into a Büchi automaton. + * input_tokens -- A reference to a vector containing the + * arguments of the user command (the + * algorithm and automaton state numbers). + * fmt -- Determines the format of output. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::badbit | ios::failbit); + + unsigned long int algorithm = parseNumber(input_tokens[1]); + + verifyNumber(algorithm, round_info.number_of_translators, + "No such implementation"); + + int formula + = (configuration.formula_options.output_mode == Configuration::NNF + ? 0 : 2) + + (formula_type ? 0 : 1); + + if (!test_results[algorithm].automaton_stats[formula_type ? 0 : 1]. + buchiAutomatonComputed()) + { + printTextBlock(stream, indent, + "No automaton was generated using implementation " + + configuration.algorithmString(algorithm) + + " for the formula `" + + toString(*round_info.formulae[formula]) + + "'.\n", + 78); + return; + } + + const BuchiAutomaton* automaton + = test_results[algorithm].automaton_stats[formula_type ? 0 : 1]. + buchi_automaton; + + if (fmt == Graph::NORMAL) + { + /* + * Display information about the states of the automaton. If no state + * list was given, display the whole automaton. + */ + + estream << string(indent, ' ') + "Büchi automaton information:\n" + + string(indent + 2, ' ') + "Formula: "; + + round_info.formulae[formula]->print(estream); + + estream << '\n' + string(indent + 2, ' ') + "Implementation: " + + configuration.algorithmString(algorithm) + '\n' + + string(indent + 2, ' ') + "Number of acceptance sets: " + + toString(automaton->numberOfAcceptanceSets()) + '\n'; + + if (automaton->empty()) + estream << string(indent + 2, ' ') + "The automaton is empty.\n"; + else + { + set, + ALLOC(unsigned long int) > states; + + if (input_tokens.size() == 2) + input_tokens.push_back("*"); + + parseInterval(input_tokens[2], states, 0, automaton->size() - 1); + + for (set, + ALLOC(unsigned long int) >::const_iterator + state = states.begin(); + state != states.end(); ++state) + { + verifyNumber(*state, automaton->size(), + "State identifier out of range"); + + estream << string(indent + 2, ' ') + "State " + toString(*state) + + (*state == automaton->initialState() + ? " (initial state)" : "") + ":\n"; + automaton->node(*state).print(stream, indent + 4, Graph::NORMAL, + automaton->numberOfAcceptanceSets()); + } + } + } + else if (fmt == Graph::DOT) + automaton->print(stream, indent, Graph::DOT); + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void evaluateFormula + (ostream& stream, int indent, bool formula_type, + vector& input_tokens) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `evaluate', i.e., tells whether + * there exists an accepting execution beginning at some system + * state according to the product automaton constructed from a + * Büchi automaton computed using a given algorithm (or + * algorithms). + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of + * the output. + * formula_type -- Determines the LTL formula to be evaluated. + * input_tokens -- A reference to a vector of strings + * containing the arguments of the user + * command. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + set, ALLOC(unsigned long int) > + algorithms; + set, ALLOC(unsigned long int) > + system_states; + string algorithm_name; + int formula = (formula_type ? 0 : 1); + bool show_path_eval_results = false; + + if (!configuration.global_options.do_comp_test + && !configuration.global_options.do_cons_test) + throw CommandErrorException("This command is available only when one of " + "the model checking tests is enabled."); + + if (round_info.statespace == 0) + throw CommandErrorException("No state space was generated in this test " + "round."); + string algorithm_list; + + if (input_tokens.size() < 2) + { + /* + * If no list of algorithms was given as an argument, show the results of + * all algorithms. + */ + + algorithm_list = "*"; + input_tokens.push_back("*"); + show_path_eval_results + = ((configuration.global_options.statespace_generation_mode + & Configuration::PATH) != 0); + } + else + { + /* + * Otherwise parse the list of algorithms. Test also whether the internal + * model checking algorithm was included in the list. + */ + + vector algorithm_list_elements; + sliceString(input_tokens[1], ",", algorithm_list_elements); + + for (vector::const_iterator alg + = algorithm_list_elements.begin(); + alg != algorithm_list_elements.end(); + ++alg) + { + if (algorithm_list.length() > 0) + algorithm_list += ','; + + if (*alg == "p") + { + if ((configuration.global_options.statespace_generation_mode + & Configuration::PATH) == 0) + throw CommandErrorException("This feature is available only when " + "using paths as state spaces."); + else + show_path_eval_results = true; + } + else + algorithm_list += *alg; + } + } + + parseInterval(algorithm_list, algorithms, 0, + round_info.number_of_translators - 1); + + /* + * If no list of states was given, show information about all states. + * (If only a local product was computed, show information only about the + * initial state.) + */ + + if (input_tokens.size() < 3) + input_tokens.push_back("*"); + + parseInterval(input_tokens[2], system_states, 0, + round_info.real_emptiness_check_size - 1); + + estream << string(indent, ' ') + "Acceptance information:\n" + + string(indent + 2, ' ') + "CTL* formula: E "; + + round_info.formulae[configuration.formula_options.output_mode + == Configuration::NNF + ? formula + : 2 + formula]->print(estream); + + estream << '\n'; + + for (set, + ALLOC(unsigned long int) >::const_iterator + state = system_states.begin(); + state != system_states.end(); + ++state) + { + verifyNumber(*state, round_info.real_emptiness_check_size, + "State identifier out of range"); + + estream << string(indent + 2, ' ') + "State " + toString(*state) + ":\n"; + + for (set, + ALLOC(unsigned long int) >::const_iterator + algorithm = algorithms.begin(); + algorithm != algorithms.end(); + ++algorithm) + { + verifyNumber(*algorithm, round_info.number_of_translators, + "No such implementation"); + + algorithm_name = configuration.algorithms[*algorithm].name + ->substr(0, 20); + estream << string(indent + 4, ' ') + toString(*algorithm) + ": " + + algorithm_name + ':' + + string(21 - algorithm_name.length(), ' '); + + if (!test_results[*algorithm].automaton_stats[formula]. + emptiness_check_performed) + estream << "emptiness check not performed\n"; + else + estream << (test_results[*algorithm].automaton_stats[formula]. + emptiness_check_result[*state] + ? "true" + : "false") + << '\n'; + } + + if (show_path_eval_results) + { + estream << string(indent + 4, ' ') + "lbtt: "; + if (!test_results[round_info.number_of_translators]. + automaton_stats[formula].emptiness_check_performed) + estream << "no model checking result available\n"; + else + estream << (test_results[round_info.number_of_translators]. + automaton_stats[formula].emptiness_check_result[*state] + ? "true" + : "false") + << '\n'; + } + } + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void printFormula(ostream& stream, int indent, bool formula_type) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `formula', i.e., displays the + * random LTL formula (or its negation) used for generating the + * Büchi automata. The formula is shown in its original form and + * possibly also in negation normal form if the user has + * requested the conversion. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of + * the output. + * formula_type -- Identifies the formula to be displayed. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (round_info.formulae[0] == 0) + throw CommandErrorException("No formulas were generated in this test " + "round."); + + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + estream << string(indent, ' ') + "Formula:" + string(17, ' '); + round_info.formulae[formula_type ? 2 : 3]->print(estream); + + if (configuration.formula_options.output_mode == Configuration::NNF) + { + estream << '\n' + string(indent, ' ') + "In negation normal form: "; + round_info.formulae[formula_type ? 0 : 1]->print(estream); + } + + estream << "\n\n"; + estream.flush(); +} + +/* ========================================================================= */ +void printCommandHelp + (ostream& stream, int indent, + const vector& input_tokens) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `help', i.e., gives instructions + * on the use of different commands. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of + * the output. + * input_tokens -- A reference to a constant vector of strings + * containing the arguments of the user + * command. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + TokenType command = _NO_INPUT; + + if (input_tokens.size() > 1) + command = parseCommand(input_tokens[1]); + + if (command == UNKNOWN) + estream << string(indent, ' ') + "Unknown command (`" + input_tokens[1] + + "').\n\n"; + + switch (command) + { + case ALGORITHMS : + estream << string(indent, ' ') + "algorithms\n"; + + printTextBlock(stream, indent + 4, + "List all implementations currently available for " + "testing.", + 78); + break; + + case BUCHI : + estream << string(indent, ' ') + "buchi [\"+\"|\"-\"] " + "[states|\"dot\"]\n"; + + printTextBlock(stream, indent + 4, + "Display information about a (list of) state(s) of a " + "Büchi automaton constructed either from the positive or " + "the negated formula using a given implementation (\"+\" " + "denotes the positive formula, \"-\" the negated " + "formula). If no formula is specified, use the positive " + "formula. If no state list is given, display all the " + "states of the automaton. The description of the entire " + "automaton can be alternatively obtained in the input " + "format of the `dot' tool that can be used to produce a " + "graphical representation of the automaton. To do this, " + "replace the state list with the string \"dot\".", + 78); + break; + + case BUCHIANALYZE : + estream << string(indent, ' ') + "buchianalysis " + "\n"; + + printTextBlock(stream, indent + 4, + "Analyze a contradiction in the Büchi automata " + "intersection emptiness check between the automaton " + "constructed from the positive formula by " + "`implementation 1' " + "and the automaton constructed from the negated formula " + "by `implementation 2'.", + 78); + break; + + case CONSISTENCYANALYSIS : + estream << string(indent, ' ') + + "consistencyanalysis [state]\n"; + + printTextBlock(stream, indent + 4, + "Analyze a contradiction in the model checking result " + "consistency check for a given implementation. The " + "optional argument `state' can be used to specify a " + "state in which to do the analysis.", + 78); + break; + + case CONTINUE : + estream << string(indent, ' ') + "continue [number of rounds]\n"; + + printTextBlock(stream, indent + 4, + "Continue testing for a given number of rounds. If the " + "number of rounds is omitted, testing will continue " + "following the current interactivity mode (for example, " + "if the option `--pauseonerror' was given in the command " + "line when invoking `lbtt', testing will continue until " + "the next test failure or until all tests have " + "finished).\n\n" + "Note: The output of this command cannot be redirected " + "to a file.", + 78); + break; + + case DISABLE : + estream << string(indent, ' ') + "disable [implementations]\n"; + + printTextBlock(stream, indent + 4, + "Disable testing of a given set of implementations. " + "If no implementations are specified, disables all " + "implementations.\n\n" + "Note: The output of this command cannot be redirected " + "to a file.", + 78); + break; + + case ENABLE : + estream << string(indent, ' ') + "enable [implementations]\n"; + + printTextBlock(stream, indent + 4, + "Enable testing of a given set of implementations. " + "If no implementations are specified, enables all " + "implementations.\n\n" + "Note: The output of this command cannot be redirected " + "to a file.", + 78); + break; + + case EVALUATE : + estream << string(indent, ' ') + "evaluate [\"+\"|\"-\"] " + "[implementations] [states]\n"; + + printTextBlock(stream, indent + 4, + "Tell whether the Büchi automaton constructed from " + "a formula (\"+\" denotes the positive formula, \"-\" " + "the negated formula) using a given implementation " + "accepts some system execution starting from a given " + "system state. If no formula is specified, use the " + "positive formula. Leaving the list of implementations " + "or the list of states unspecified will display the " + "results for all implementations or all system states, " + "respectively. If using paths as state spaces, the " + "special symbol \"p\" in the implementation list will " + "show also the results obtained with the internal " + "model checking algorithm.", + 78); + break; + + case FORMULA : + estream << string(indent, ' ') + "formula [\"+\"|\"-\"]\n"; + + printTextBlock(stream, indent + 4, + "Display the LTL formula used in this test round for " + "generating Büchi automata (\"+\" denotes the positive " + "formula, \"-\" the negated formula). If no formula is " + "specified, show the positive formula.", + 78); + break; + + case HELP : + estream << string(indent, ' ') + "help [command]\n"; + + printTextBlock(stream, indent + 4, + "Display help about a specific command. If no command " + "name is given, give a list of all available commands." + "\n\nIn command-specific help, arguments in angle " + "brackets < > are obligatory, while square bracketed [ ] " + "arguments are optional. A vertical bar | denotes " + "selection between alternatives. Arguments " + "in quotes must be entered literally (without the " + "quotes themselves).", + 78); + break; + + case INCONSISTENCIES : + estream << string(indent, ' ') + "inconsistencies [implementations]\n"; + + printTextBlock(stream, indent + 4, + "List the system states where the model checking result " + "consistency check failed for a given (set of) " + "implementation(s).", + 78); + break; + + case QUIT : + estream << string(indent, ' ') + "quit\n"; + + printTextBlock(stream, indent + 4, "Abort testing.", 78); + break; + + case RESULTANALYZE : + estream << string(indent, ' ') + "resultanalysis [\"+\"|\"-\"] " + " " + "[state]\n"; + + printTextBlock(stream, indent + 4, + "Analyze a contradiction in the model checking results " + "of two implementations on a formula (\"+\" denotes the " + "positive formula, \"-\" the negated formula). If no " + "formula is specified, use the positive formula. When " + "using paths as state spaces, one of implementation " + "identifiers can be replaced by the symbol \"p\" to " + "analyze the result given by some implementation against " + "that given by the internal model checking algorithm. " + "The optional argument `state' can be used to specify " + "the state of the state space in which to do the " + "analysis.", + 78); + break; + + case RESULTS : + estream << string(indent, ' ') + "results [implementations]\n"; + + printTextBlock(stream, indent + 4, + "Display this round's test results for a given (set of) " + "implementation(s). If no implementations are specified, " + "show the results for all implementations.", + 78); + break; + + case SKIP : + estream << string(indent, ' ') + "skip [number of rounds]\n"; + + printTextBlock(stream, indent + 4, + "Skip a given number of rounds (defaults to 1 if no " + "number is specified).\n\nNote: The output of this " + "command cannot be redirected to a file.", + 78); + break; + + case STATESPACE : + estream << string(indent, ' ') + "statespace [states|\"dot\"]\n"; + + printTextBlock(stream, indent + 4, + "Display information about a given (list of) system " + "state(s). Display the whole state space if no " + "states are specified. Alternatively, the state " + "space description can be obtained in the input format " + "of the `dot' tool that can be used to produce a " + "graphical representation of the state space. To do " + "this, replace the state list with the string \"dot\".", + 78); + break; + + case STATISTICS : + estream << string(indent, ' ') + "statistics\n"; + + printTextBlock(stream, indent + 4, + "Display test statistics over all tests performed.", + 78); + break; + + case VERBOSITY : + estream << string(indent, ' ') + "verbosity [0-5]\n"; + + printTextBlock(stream, indent + 4, + "Change the verbosity of the output produced by the " + "program. If no value is given, display the current " + "setting.\n\nNote: The output of this command cannot " + "be redirected to a file.", + 78); + break; + + default : + printTextBlock(stream, indent, + "List of available commands (use `help " + "[command]' for command specific help):\n", + 78); + + printTextBlock(stream, indent + 2, + "algorithms\n" + "buchi\n" + "buchianalysis\n" + "continue\n" + "consistencyanalysis\n" + "disable\n" + "enable\n" + "evaluate\n" + "formula\n" + "help\n" + "inconsistencies\n" + "quit\n" + "resultanalysis\n" + "results\n" + "skip\n" + "statespace\n" + "statistics\n" + "verbosity\n", + 78); + + printTextBlock(stream, indent, + "Command names can be abbreviated to the shortest prefix " + "that identifies the command unambiguously. " + "For example, the prefix `h' can be used instead of the " + "`help' command.\n\n" + "Lists of implementation or state identifiers may be " + "specified as comma-separated intervals (with no spaces " + "in between), e.g., the command `statespace " + "-5,8,14-18,22-' would display the list of all states " + "with an identifier less than or equal to 5, state 8 and " + "states 14--18, together with the states whose " + "identifiers are greater than or equal to 22.\n\n" + "The output of most commands can be redirected or " + "appended to a file by ending the command line with " + "`>filename' or `>>filename', respectively. Optionally, " + "the output can be written to a pipe instead by ending " + "the command line with `| '.", + 78); + break; + } + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void printInconsistencies + (ostream& stream, int indent, vector& input_tokens) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `inconsistencies', i.e., lists + * the states where the consistency check failed for a set of + * algorithms. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of + * the output. + * input_tokens -- A reference to a vector of strings + * containing the parameters of the user + * command. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + + set, ALLOC(unsigned long int) > + number_set; + + if (!configuration.global_options.do_cons_test) + throw CommandErrorException("This command is available only when the " + "model checking result consistency check is " + "enabled."); + + estream << string(indent, ' ') + "Model checking result consistency check " + "results for round " + + toString(round_info.current_round) + ":\n"; + + indent += 2; + + estream << string(indent, ' ') + "Positive formula: " + << *round_info.formulae[configuration.formula_options.output_mode + == Configuration::NNF + ? 0 + : 2] + << '\n'; + + if (input_tokens.size() == 1) + input_tokens.push_back("*"); + + parseInterval(input_tokens[1], number_set, 0, + round_info.number_of_translators - 1); + + for (set, + ALLOC(unsigned long int) >::const_iterator + algorithm = number_set.begin(); + algorithm != number_set.end(); ++algorithm) + { + estream << '\n'; + + verifyNumber(*algorithm, round_info.number_of_translators, + "No such implementation"); + + estream << string(indent, ' ') + configuration.algorithmString(*algorithm) + + '\n'; + + if (test_results[*algorithm].consistency_check_result == -1) + printTextBlock(stream, indent + 2, + "Model checking result consistency check was not " + "performed on this implementation.", + 78); + else + { + if (test_results[*algorithm].consistency_check_result > 0) + printTextBlock(stream, indent + 2, + "The implementation passed the model checking result " + "consistency check.", + 78); + else + { + bool first_printed = false; + + estream << string(indent + 2, ' ') + "Check failed in states\n"; + + string resultstring = "{"; + + for (unsigned long int state = 0; + state < round_info.real_emptiness_check_size; + state++) + { + if (!test_results[*algorithm].automaton_stats[0]. + emptiness_check_result[state] + && !test_results[*algorithm].automaton_stats[1]. + emptiness_check_result[state]) + { + if (first_printed) + resultstring += ", "; + else + first_printed = true; + + resultstring += toString(state); + } + } + + resultstring += "}"; + + printTextBlock(stream, indent + 4, resultstring, 78); + } + } + } + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void printTestResults + (ostream& stream, int indent, vector& input_tokens) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `results', i.e., displays the + * current round's test results for a given (set of) + * algorithm(s). + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of + * the output. + * input_tokens -- A reference to a vector of strings + * containing the arguments of the user + * command. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + set, ALLOC(unsigned long int) > + number_set; + + estream << string(indent, ' ') + "Test results for round " + + toString(round_info.current_round) << ":\n"; + + if (input_tokens.size() == 1) + input_tokens.push_back("*"); + + parseInterval(input_tokens[1], number_set, 0, + round_info.number_of_translators - 1); + + for (set, + ALLOC(unsigned long int) >::const_iterator + algorithm = number_set.begin(); + algorithm != number_set.end(); ++algorithm) + { + verifyNumber(*algorithm, round_info.number_of_translators, + "No such implementation"); + + printAllStats(stream, indent, *algorithm); + + if (configuration.global_options.do_comp_test) + { + estream << string(indent, ' ') + + "Model checking result cross-comparison:\n"; + printCrossComparisonStats(stream, indent + 2, *algorithm); + } + + if (configuration.global_options.do_intr_test) + { + estream << string(indent, ' ') + + "Büchi automata intersection emptiness check:\n"; + printBuchiIntersectionCheckStats(stream, indent + 2, *algorithm); + } + estream.flush(); + } +} + +/* ========================================================================= */ +void printStateSpace + (ostream& stream, int indent, vector& input_tokens, + Graph::GraphOutputFormat fmt) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `statespace', i.e., displays + * information about a given set of states of the state space. + * + * Arguments: stream -- A reference to an output stream. + * indent -- Number of spaces to leave on the left of + * the output. + * input_tokens -- A reference to a vector of strings + * containing the arguments of the user + * command. + * fmt -- Determines the format of output. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + Exceptional_ostream estream(&stream, ios::failbit | ios::badbit); + set, ALLOC(unsigned long int) > + number_set; + + if (!configuration.global_options.do_comp_test + && !configuration.global_options.do_cons_test) + throw CommandErrorException("This command is available only when one of " + "the model checking tests is enabled."); + + if (round_info.statespace == 0) + throw CommandErrorException("No state space was generated in this test " + "round."); + + if (fmt == Graph::NORMAL) + { + estream << string(indent, ' ') + "State space information:\n"; + + if (input_tokens.size() == 1) + input_tokens.push_back("*"); + + parseInterval(input_tokens[1], number_set, 0, + round_info.statespace->size() - 1); + + for (set, + ALLOC(unsigned long int) >::const_iterator + state = number_set.begin(); + state != number_set.end(); ++state) + { + verifyNumber(*state, round_info.statespace->size(), + "State identifier out of range"); + + estream << string(indent, ' ') + "State " + toString(*state) + + (*state == round_info.statespace->initialState() + ? " (initial state)" + : "") + + ":\n"; + + (*(round_info.statespace))[*state].print + (stream, indent + 2, fmt, + round_info.statespace->numberOfPropositions()); + } + } + else if (fmt == Graph::DOT) + round_info.statespace->print(stream, indent, Graph::DOT); + + estream << '\n'; + estream.flush(); +} + +/* ========================================================================= */ +void changeVerbosity(const vector& input_tokens) +/* ---------------------------------------------------------------------------- + * + * Description: Implements the user command `verbosity', i.e., displays or + * changes the output verbosity level. + * + * Argument: input_tokens -- A reference to a constant vector of strings + * containing the argument of the command. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + if (input_tokens.size() == 1) + printText("Output verbosity is currently set to " + + toString(configuration.global_options.verbosity) + + ".\n\n", + 0, + 2); + else + { + unsigned long int new_verbosity = parseNumber(input_tokens[1]); + if (new_verbosity > 5) + throw CommandErrorException("Verbosity level must be between 0 and 5 " + "inclusive."); + + configuration.global_options.verbosity = new_verbosity; + + printText("Output verbosity level set to " + + toString(new_verbosity) + + ".\n\n", + 0, + 2); + } +} + +/* ========================================================================= */ +void changeAlgorithmState + (vector& input_tokens, bool enable) +/* ---------------------------------------------------------------------------- + * + * Description: Changes the enabledness of a set of algorithms used in the + * tests. + * + * Argument: input_tokens -- A reference to a constant vector of strings + * containing the argument of the command. + * enable -- Determines whether the algorithms are to be + * enabled or disabled. + * + * Returns: Nothing. + * + * ------------------------------------------------------------------------- */ +{ + set, ALLOC(unsigned long int) > + algorithms; + + if (input_tokens.size() < 2) + input_tokens.push_back("*"); + + parseInterval(input_tokens[1], algorithms, 0, + round_info.number_of_translators - 1); + + for (set, + ALLOC(unsigned long int) >::const_iterator + alg = algorithms.begin(); + alg != algorithms.end(); alg++) + { + verifyNumber(*alg, round_info.number_of_translators, + "No such implementation"); + + printText(string(enable ? "En" : "Dis") + + "abling implementation " + + configuration.algorithmString(*alg) + + ".\n", + 0, + 2); + + configuration.algorithms[*alg].enabled = enable; + } + + round_info.cout << '\n'; + round_info.cout.flush(); +} + +} diff --git a/lbtt/src/UserCommands.h b/lbtt/src/UserCommands.h new file mode 100644 index 000000000..02b7a0925 --- /dev/null +++ b/lbtt/src/UserCommands.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef USERCOMMANDS_H +#define USERCOMMANDS_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include +#include +#include "Alloc.h" +#include "BuchiAutomaton.h" +#include "Configuration.h" +#include "ProductAutomaton.h" +#include "StateSpace.h" + +using namespace std; + +extern Configuration configuration; + +/****************************************************************************** + * + * Implementations for user commands. + * + *****************************************************************************/ + +namespace UserCommands +{ + +void computeProductAutomaton /* Computes a product */ + (ProductAutomaton*& product_automaton, /* automaton. */ + const BuchiAutomaton& buchi_automaton, + pair& last_automaton, + const pair& + new_automaton); + +void printAlgorithmList /* Displays a list of */ + (ostream& stream, int indent); /* algorithms used in + * the tests. + */ + +void synchronizePrefixAndCycle /* Synchronizes a prefix */ + (deque /* with a cycle in a */ + ::size_type, /* sequence of graph */ + ALLOC(Graph::Graph /* state identifiers. */ + ::size_type) >& + prefix, + deque + ::size_type, + ALLOC(Graph::Graph + ::size_type) >& + cycle); + +void printCrossComparisonAnalysisResults /* Analyzes a */ + (ostream& stream, int indent, /* contradiction between */ + bool formula_type, /* test results of two */ + const vector& /* implementations. */ + input_tokens, + ProductAutomaton*& product_automaton, + pair& + last_product_automaton); + +void printConsistencyAnalysisResults /* Analyzes a */ + (ostream& stream, int indent, /* contradicition in the */ + const vector& /* model checking result */ + input_tokens); /* consistency check for + * an implementation. + */ + +void printAutomatonAnalysisResults /* Analyzes a */ + (ostream& stream, int indent, /* contradiction in the */ + unsigned long int algorithm1, /* Büchi automata */ + unsigned long int algorithm2); /* intersection + * emptiness check. + */ + +void printPath /* Displays information */ + (ostream& stream, int indent, /* about a single */ + const deque& + prefix, + const deque& + cycle, + const StateSpace& path); + +void printAcceptingCycle /* Displays information */ + (ostream& stream, int indent, /* a single automaton */ + vector + ::size_type + algorithm_id, + const deque& + prefix, + const deque& + cycle, + const BuchiAutomaton& automaton); + +void printBuchiAutomaton /* Displays information */ + (ostream& stream, int indent, /* about a Büchi */ + bool formula_type, /* automaton. */ + vector& input_tokens, + Graph::GraphOutputFormat fmt); + +void evaluateFormula /* Displays information */ + (ostream& stream, int indent, /* about existence of */ + bool formula_type, /* accepting system */ + vector& input_tokens); /* executions. */ + +void printFormula /* Displays a formula */ + (ostream& stream, int indent, /* used for testing. */ + bool formula_type); + +void printCommandHelp /* Displays help about */ + (ostream& stream, int indent, /* user commands. */ + const vector& + input_tokens); + +void printInconsistencies /* Lists the system */ + (ostream& stream, int indent, /* states failing the */ + vector& input_tokens); /* consistency check + * for an algorihm. + */ + +void printTestResults /* Displays the test */ + (ostream& stream, int indent, /* results of the last */ + vector& input_tokens); /* round performed. */ + +void printStateSpace /* Displays information */ + (ostream& stream, int indent, /* about a state space. */ + vector& input_tokens, + Graph::GraphOutputFormat fmt); + +void changeVerbosity /* Displays or changes */ + (const vector& /* the verbosity of */ + input_tokens); /* output. */ + +void changeAlgorithmState /* Enables or disables a */ + (vector& input_tokens, /* set of algorithms */ + bool enable); /* used in the tests. */ + +} + +#endif /* !USERCOMMANDS_H */ diff --git a/lbtt/src/getopt.c b/lbtt/src/getopt.c new file mode 100644 index 000000000..a950cb50e --- /dev/null +++ b/lbtt/src/getopt.c @@ -0,0 +1,1055 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. */ +# if defined HAVE_LIBINTL_H || defined _LIBC +# include +# ifndef _ +# define _(msgid) gettext (msgid) +# endif +# else +# define _(msgid) (msgid) +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "gnu-getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *gnu_optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int gnu_optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __gnu_getopt_initialized; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int gnu_opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int gnu_optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include +# define my_index strchr +#else + +# if HAVE_STRING_H +# include +# else +# include +# endif + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Stored original parameters. + XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ +extern int __libc_argc; +extern char **__libc_argv; + +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +# ifdef USE_NONOPTION_FLAGS +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; +# endif + +# ifdef USE_NONOPTION_FLAGS +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +# else +# define SWAP_FLAGS(ch1, ch2) +# endif +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = gnu_optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (gnu_optind - last_nonopt); + last_nonopt = gnu_optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = gnu_optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + if (posixly_correct == NULL + && argc == __libc_argc && argv == __libc_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_gnu_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct gnu_option *longopts; + int *longind; + int long_only; +{ + int print_errors = gnu_opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + gnu_optarg = NULL; + + if (gnu_optind == 0 || !__gnu_getopt_initialized) + { + if (gnu_optind == 0) + gnu_optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __gnu_getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#if defined _LIBC && defined USE_NONOPTION_FLAGS +# define NONOPTION_P (argv[gnu_optind][0] != '-' || argv[gnu_optind][1] == '\0' \ + || (gnu_optind < nonoption_flags_len \ + && __getopt_nonoption_flags[gnu_optind] == '1')) +#else +# define NONOPTION_P (argv[gnu_optind][0] != '-' || argv[gnu_optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > gnu_optind) + last_nonopt = gnu_optind; + if (first_nonopt > gnu_optind) + first_nonopt = gnu_optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != gnu_optind) + exchange ((char **) argv); + else if (last_nonopt != gnu_optind) + first_nonopt = gnu_optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (gnu_optind < argc && NONOPTION_P) + gnu_optind++; + last_nonopt = gnu_optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (gnu_optind != argc && !strcmp (argv[gnu_optind], "--")) + { + gnu_optind++; + + if (first_nonopt != last_nonopt && last_nonopt != gnu_optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = gnu_optind; + last_nonopt = argc; + + gnu_optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (gnu_optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + gnu_optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + gnu_optarg = argv[gnu_optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[gnu_optind] + 1 + + (longopts != NULL && argv[gnu_optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[gnu_optind][1] == '-' + || (long_only && (argv[gnu_optind][2] || !my_index (optstring, argv[gnu_optind][1]))))) + { + char *nameend; + const struct gnu_option *p; + const struct gnu_option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[gnu_optind]); + nextchar += strlen (nextchar); + gnu_optind++; + gnu_optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + gnu_optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + gnu_optarg = nameend + 1; + else + { + if (print_errors) + { + if (argv[gnu_optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[gnu_optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + gnu_optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (gnu_optind < argc) + gnu_optarg = argv[gnu_optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[gnu_optind - 1]); + nextchar += strlen (nextchar); + gnu_optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[gnu_optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (print_errors) + { + if (argv[gnu_optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[gnu_optind][0], nextchar); + } + nextchar = (char *) ""; + gnu_optind++; + gnu_optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++gnu_optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + gnu_optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct gnu_option *p; + const struct gnu_option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + gnu_optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + gnu_optind++; + } + else if (gnu_optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + gnu_optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + gnu_optarg = argv[gnu_optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = gnu_optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[gnu_optind]); + nextchar += strlen (nextchar); + gnu_optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + gnu_optarg = nameend + 1; + else + { + if (print_errors) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (gnu_optind < argc) + gnu_optarg = argv[gnu_optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[gnu_optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + gnu_optarg = nextchar; + gnu_optind++; + } + else + gnu_optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + gnu_optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + gnu_optind++; + } + else if (gnu_optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + gnu_optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + gnu_optarg = argv[gnu_optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +gnu_getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _gnu_getopt_internal (argc, argv, optstring, + (const struct gnu_option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = gnu_optind ? gnu_optind : 1; + + c = gnu_getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", gnu_optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (gnu_optind < argc) + { + printf ("non-option ARGV-elements: "); + while (gnu_optind < argc) + printf ("%s ", argv[gnu_optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/lbtt/src/getopt1.c b/lbtt/src/getopt1.c new file mode 100644 index 000000000..23c4e508e --- /dev/null +++ b/lbtt/src/getopt1.c @@ -0,0 +1,188 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "gnu-getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +gnu_getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct gnu_option *long_options; + int *opt_index; +{ + return _gnu_getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +gnu_getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct gnu_option *long_options; + int *opt_index; +{ + return _gnu_getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = gnu_optind ? gnu_optind : 1; + int option_index = 0; + static struct gnu_option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = gnu_getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (gnu_optarg) + printf (" with arg %s", gnu_optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", gnu_optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", gnu_optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (gnu_optind < argc) + { + printf ("non-option ARGV-elements: "); + while (gnu_optind < argc) + printf ("%s ", argv[gnu_optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/lbtt/src/gnu-getopt.h b/lbtt/src/gnu-getopt.h new file mode 100644 index 000000000..0ff0fb1ae --- /dev/null +++ b/lbtt/src/gnu-getopt.h @@ -0,0 +1,180 @@ +/* Declarations for getopt. + Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +/* If __GNU_LIBRARY__ is not already defined, either we are being used + standalone, or this is the first header included in the source file. + If we are being used with glibc, we need to include , but + that does not exist if we are standalone. So: if __GNU_LIBRARY__ is + not defined, include , which will pull in for us + if it's from glibc. (Why ctype.h? It's guaranteed to exist and it + doesn't flood the namespace with stuff the way some other headers do.) */ +#if !defined __GNU_LIBRARY__ +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *gnu_optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int gnu_optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int gnu_opterr; + +/* Set to an option character which was unrecognized. */ + +extern int gnu_optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct gnu_option +{ +# if (defined __STDC__ && __STDC__) || defined __cplusplus + const char *name; +# else + char *name; +# endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +#if (defined __STDC__ && __STDC__) || defined __cplusplus +# ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int gnu_getopt (int __argc, char *const *__argv, const char *__shortopts); +# else /* not __GNU_LIBRARY__ */ +extern int gnu_getopt (); +# endif /* __GNU_LIBRARY__ */ + +# ifndef __need_getopt +extern int gnu_getopt_long (int __argc, char *const *__argv, const char *__shortopts, + const struct gnu_option *__longopts, int *__longind); +extern int gnu_getopt_long_only (int __argc, char *const *__argv, + const char *__shortopts, + const struct gnu_option *__longopts, int *__longind); + +/* Internal only. Users should not call this directly. */ +extern int _gnu_getopt_internal (int __argc, char *const *__argv, + const char *__shortopts, + const struct gnu_option *__longopts, int *__longind, + int __long_only); +# endif +#else /* not __STDC__ */ +extern int gnu_getopt (); +# ifndef __need_getopt +extern int gnu_getopt_long (); +extern int gnu_getopt_long_only (); + +extern int _gnu_getopt_internal (); +# endif +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/lbtt/src/main.cc b/lbtt/src/main.cc new file mode 100644 index 000000000..8cf2c665d --- /dev/null +++ b/lbtt/src/main.cc @@ -0,0 +1,725 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_READLINE +#include +#include +#include +#endif /* HAVE_READLINE */ +#include "Alloc.h" +#include "Configuration.h" +#include "DispUtil.h" +#include "Exception.h" +#include "LtlFormula.h" +#include "Random.h" +#include "SharedTestData.h" +#include "StatDisplay.h" +#include "TestOperations.h" +#include "TestRoundInfo.h" +#include "TestStatistics.h" +#include "UserCommandReader.h" + +using namespace std; + +/****************************************************************************** + * + * Handler for the SIGINT signal. + * + *****************************************************************************/ + +RETSIGTYPE breakHandler(int) +{ + user_break = true; +} + + + +/****************************************************************************** + * + * This variable will be used for testing whether the testing has been aborted + * with a SIGINT signal. + * + *****************************************************************************/ + +bool user_break; + + + +/****************************************************************************** + * + * Program configuration. + * + *****************************************************************************/ + +Configuration configuration; + + + +/****************************************************************************** + * + * Variables for storing test results and maintaining test state information. + * + *****************************************************************************/ + +namespace SharedTestData +{ + +TestRoundInfo round_info; /* Data structure for + * storing information + * about the current test + * round. + */ + +vector /* individual algorithm. */ + test_results; + +vector /* Overall test */ + final_statistics; /* statistics for each + * algorithm. + */ + +} + + + +/****************************************************************************** + * + * Test loop. + * + *****************************************************************************/ + +void testLoop() +{ + using namespace DispUtil; + using namespace SharedTestData; + using namespace StatDisplay; + using namespace StringUtil; + using namespace TestOperations; + + const Configuration::GlobalConfiguration& global_options + = configuration.global_options; + + /* + * Initialize the test state information data structure with program + * configuration information. + */ + + round_info.number_of_translators = configuration.algorithms.size(); + + round_info.next_round_to_run += global_options.init_skip; + + round_info.next_round_to_stop + = (global_options.interactive == Configuration::ALWAYS + ? round_info.next_round_to_run + : global_options.number_of_rounds + 1); + + if (tmpnam(round_info.formula_file_name[0]) == 0 + || tmpnam(round_info.formula_file_name[1]) == 0 + || tmpnam(round_info.automaton_file_name) == 0 + || tmpnam(round_info.cout_capture_file) == 0 + || tmpnam(round_info.cerr_capture_file) == 0) + throw Exception("unable to allocate names for temporary files"); + + /* + * If a name for the error log file was given in the configuration, create + * the file. + */ + + if (!global_options.transcript_filename.empty()) + { + time_t current_time; + + time(¤t_time); + + try + { + openFile(global_options.transcript_filename.c_str(), + round_info.transcript_file, + ios::out | ios::trunc, + 0); + } + catch (const IOException&) + { + throw Exception("error creating log file `" + + global_options.transcript_filename + '\''); + } + + try + { + round_info.transcript_file << "lbtt " PACKAGE_VERSION + " error log file, created on " + + string(ctime(¤t_time)) + + '\n'; + + configuration.print(round_info.transcript_file); + } + catch (const IOException&) + { + round_info.transcript_file.close(); + } + } + + /* + * If a formula file name was given in the configuration, open the file for + * reading. + */ + + try + { + if (!global_options.formula_input_filename.empty()) + openFile(global_options.formula_input_filename.c_str(), + round_info.formula_input_file, + ios::in, + 0); + } + catch (const FileOpenException& e) + { + if (round_info.transcript_file.is_open()) + writeToTranscript("Testing aborted: " + string(e.what()), false); + + throw; + } + + /* + * If using the rand48() function family for generating random numbers, + * initialize the random number generators. + */ + +#ifdef HAVE_RAND48 + unsigned short int statespace_random_state[3]; + unsigned short int formula_random_state[3]; + + SRAND(configuration.global_options.statespace_random_seed); + for (int i = 0; i < 3; i++) + statespace_random_state[i] = static_cast(LRAND(0, LONG_MAX)); + + SRAND(configuration.global_options.formula_random_seed); + for (int i = 0; i < 3; i++) + formula_random_state[i] = static_cast(LRAND(0, LONG_MAX)); +#endif /* HAVE_RAND48 */ + + /* + * If using paths as state spaces, include the internal model checking + * algorithm in the set of algorithms. + */ + + if (global_options.statespace_generation_mode & Configuration::PATH) + { + Configuration::AlgorithmInformation lbtt_info + = {new string("lbtt"), new string(), new string(), true}; + + configuration.algorithms.push_back(lbtt_info); + } + + /* + * Intialize the vector for storing the test results for each + * implementation and the vector for collecting overall test statistics for + * each implementation. + */ + + StateSpace::size_type max_emptiness_checking_size + = (global_options.product_mode == Configuration::GLOBAL + ? configuration.statespace_generator.max_size + : 1); + + test_results.clear(); + final_statistics.clear(); + for (unsigned long int i = 0; i < configuration.algorithms.size(); ++i) + { + test_results.push_back + (AlgorithmTestResults(configuration.algorithms.size(), + max_emptiness_checking_size)); + final_statistics.push_back + (TestStatistics(configuration.algorithms.size())); + } + + /* + * Test loop. + */ + + for (round_info.current_round = 1; + !round_info.abort + && round_info.current_round <= global_options.number_of_rounds; + ++round_info.current_round) + { + user_break = false; + round_info.error = false; + round_info.skip + = (round_info.current_round < round_info.next_round_to_run); + + if (!round_info.skip) + { + if (!printText(string("Round ") + toString(round_info.current_round) + + " of " + toString(global_options.number_of_rounds) + + "\n\n", + 2)) + { + if (global_options.verbosity == 1) + { + if (round_info.current_round > 1) + round_info.cout << ' '; + round_info.cout << round_info.current_round; + round_info.cout.flush(); + } + } + } + + try + { + /* + * Generate a new state space if necessary. + */ + + round_info.fresh_statespace + = ((global_options.do_comp_test || global_options.do_cons_test) + && round_info.next_round_to_change_statespace + == round_info.current_round); + + if (round_info.fresh_statespace) + { +#ifdef HAVE_RAND48 + seed48(statespace_random_state); + for (int i = 0; i < 3; i++) + statespace_random_state[i] = static_cast + (LRAND(0, LONG_MAX)); +#else + SRAND(global_options.statespace_random_seed); + configuration.global_options.statespace_random_seed + = LRAND(0, RAND_MAX); +#endif /* HAVE_RAND48 */ + + if (global_options.statespace_change_interval == 0) + round_info.next_round_to_change_statespace + = global_options.number_of_rounds + 1; + else + round_info.next_round_to_change_statespace + += global_options.statespace_change_interval; + + for (vector + ::iterator it = test_results.begin(); + it != test_results.end(); + ++it) + it->emptinessReset(); + + if ((!user_break && !round_info.skip) + || (global_options.statespace_generation_mode + == Configuration::ENUMERATEDPATH) + || (round_info.next_round_to_run + < round_info.next_round_to_change_statespace)) + { + try + { + generateStateSpace(); + } + catch (const UserBreakException&) + { + } + catch (const StateSpaceGenerationException&) + { + round_info.error = true; + } + } + } + + /* + * Test whether it is necessary to generate (or read) a new LTL formula. + */ + + round_info.fresh_formula + = (round_info.next_round_to_change_formula + == round_info.current_round); + + if (round_info.fresh_formula) + { +#ifdef HAVE_RAND48 + seed48(formula_random_state); + for (int i = 0; i < 3; i++) + formula_random_state[i] = static_cast(LRAND(0, LONG_MAX)); +#else + SRAND(global_options.formula_random_seed); + configuration.global_options.formula_random_seed = LRAND(0, RAND_MAX); +#endif /* HAVE_RAND48 */ + + if (global_options.formula_change_interval == 0) + round_info.next_round_to_change_formula + = global_options.number_of_rounds + 1; + else + round_info.next_round_to_change_formula + += global_options.formula_change_interval; + + round_info.formula_in_file[0] = round_info.formula_in_file[1] = false; + + for (vector + ::iterator it = test_results.begin(); + it != test_results.end(); + ++it) + it->fullReset(); + + if ((!round_info.error && !user_break && !round_info.skip) + || (round_info.next_round_to_run + < round_info.next_round_to_change_formula)) + { + try + { + generateFormulae(round_info.formula_input_file.is_open() + ? &round_info.formula_input_file + : 0); + } + catch (const FormulaGenerationException&) + { + round_info.error = true; + round_info.abort = true; + continue; + } + } + } + + if (user_break) + { + printText("[User break]\n\n", 2, 4); + throw UserBreakException(); + } + + if (!round_info.error && !round_info.skip) + { + writeFormulaeToFiles(); + + /* + * If the generated state spaces paths, model check the formula + * separately in the path. + */ + + if (global_options.statespace_generation_mode & Configuration::PATH + && (global_options.do_cons_test || global_options.do_comp_test) + && (!test_results[round_info.number_of_translators]. + automaton_stats[0].emptiness_check_performed)) + verifyFormulaOnPath(); + + if (!round_info.error) + { + unsigned long int num_enabled_implementations = 0; + + for (unsigned long int algorithm_id = 0; + algorithm_id < round_info.number_of_translators; + ++algorithm_id) + { + if (!configuration.algorithms[algorithm_id].enabled) + continue; + + num_enabled_implementations++; + + printText(configuration.algorithmString(algorithm_id) + '\n', + 2, 4); + + for (int counter = 0; counter < 2; counter++) + { + if (user_break) + { + printText("[User break]\n\n", 2, 4); + throw UserBreakException(); + } + + printText(string(counter == 1 ? "Negated" : "Positive") + + " formula:\n", + 2, + 6); + + try + { + try + { + round_info.product_automaton = 0; + + /* + * Generate a Büchi automaton using the current algorithm. + * `counter' determines the formula which is to be + * translated into an automaton; 0 denotes the positive and + * 1 the negated formula. + */ + + generateBuchiAutomaton(counter, algorithm_id); + + if (global_options.do_cons_test + || global_options.do_comp_test) + { + /* + * Compute the product of the Büchi automaton with the + * state space. + */ + + generateProductAutomaton(counter, algorithm_id); + + /* + * Find the system states from which an accepting + * execution cycle can be reached by checking the product + * automaton for emptiness. + */ + + performEmptinessCheck(counter, algorithm_id); + + /* + * If a product automaton was computed in this test round + * (it might have not if the emptiness checking result was + * already available), release the memory allocated for + * the product automaton. + */ + + if (round_info.product_automaton != 0) + { + printText("", 4, 8); + + delete round_info.product_automaton; + round_info.product_automaton = 0; + + printText(" ok\n", 4); + } + } + } + catch (...) + { + if (round_info.product_automaton != 0) + { + delete round_info.product_automaton; + round_info.product_automaton = 0; + } + throw; + } + } + catch (const BuchiAutomatonGenerationException&) + { + round_info.error = true; + final_statistics[algorithm_id]. + failures_to_compute_buchi_automaton[counter]++; + } + catch (const ProductAutomatonGenerationException&) + { + round_info.error = true; + final_statistics[algorithm_id]. + failures_to_compute_product_automaton[counter]++; + } + catch (const EmptinessCheckFailedException&) + { + round_info.error = true; + } + } + + /* + * If the emptiness check was performed successfully for the + * product automata constructed from both the positive and + * negated formulae, test whether the emptiness check results + * are consistent with each other. (It should not be possible + * for both the formula and its negation to be true in any + * state.) + */ + if (global_options.do_cons_test + && test_results[algorithm_id].automaton_stats[0]. + emptiness_check_performed + && test_results[algorithm_id].automaton_stats[1]. + emptiness_check_performed) + performConsistencyCheck(algorithm_id); + + printText("\n", 2); + } + + if (num_enabled_implementations > 0) + { + if (global_options.do_comp_test) + { + /* + * Perform the pairwise comparisons of the emptiness check + * results obtained using the different algorithms. + */ + + if (num_enabled_implementations >= 2 + || (num_enabled_implementations == 1 + && global_options.statespace_generation_mode + & Configuration::PATH)) + compareResults(); + } + + if (global_options.do_intr_test) + { + /* + * Perform the pairwise intersection emptiness checks on the + * Büchi automata computed during this test round using the + * different algorithms. + */ + + performBuchiIntersectionCheck(); + } + + if ((global_options.do_comp_test || global_options.do_intr_test) + && global_options.verbosity == 2) + { + round_info.cout << '\n'; + round_info.cout.flush(); + } + } + } + } + } + catch (const UserBreakException&) + { + user_break = false; + round_info.next_round_to_stop = round_info.current_round; + } + + /* + * Determine from the program configuration and the error status whether + * the testing should be paused to wait for user commands. + */ + + if (round_info.error + && global_options.interactive == Configuration::ONERROR) + round_info.next_round_to_stop = round_info.current_round; + + if (round_info.next_round_to_stop == round_info.current_round) + { + if (global_options.verbosity == 1) + { + round_info.cout << '\n'; + round_info.cout.flush(); + } + + ::UserCommandInterface::executeUserCommands(); + } + } + + for (int i = 0; i < 2; i++) + removeFile(round_info.formula_file_name[i], 2); + + if (round_info.path_iterator != 0) + delete round_info.path_iterator; + else if (round_info.statespace != 0) + delete round_info.statespace; + + for (int f = 0; f < 4; f++) + { + if (round_info.formulae[f] != 0) + ::Ltl::LtlFormula::destruct(round_info.formulae[f]); + } + + for (vector + ::iterator it = test_results.begin(); + it != test_results.end(); + ++it) + it->fullReset(); + + round_info.current_round--; + + if (round_info.transcript_file.is_open()) + { + round_info.transcript_file << endl; + + if (round_info.abort) + round_info.transcript_file << "Testing aborted in round " + + toString(round_info.current_round) + + ".\n" + << endl; + + try + { + printCollectiveStats(round_info.transcript_file, 0); + } + catch (const IOException&) + { + } + + round_info.transcript_file << endl; + + time_t current_time; + + time(¤t_time); + + round_info.transcript_file << "lbtt error log closed on " + + string(ctime(¤t_time)) + << endl; + + round_info.transcript_file.close(); + } + + if (global_options.verbosity >= 1) + printCollectiveStats(cout, 0); + + if (round_info.formula_input_file.is_open()) + round_info.formula_input_file.close(); +} + + + +/****************************************************************************** + * + * Main function. + * + *****************************************************************************/ + +int main(int argc, char* argv[]) +{ + try + { + configuration.read(argc, argv); + } + catch (const Configuration::ConfigurationException& e) + { + cerr << argv[0]; + if (!e.line_info.empty()) + cerr << ":" << configuration.global_options.cfg_filename << ":" + << e.line_info; + cerr << ": " << e.what() << endl; + exit(-1); + } + + if (configuration.global_options.verbosity >= 3) + configuration.print(cout); + + user_break = false; + signal(SIGINT, breakHandler); + +#ifdef HAVE_OBSTACK_H + obstack_alloc_failed_handler = &ObstackAllocator::failure; +#endif /* HAVE_OBSTACK_H */ + +#ifdef HAVE_READLINE + using_history(); +#endif /* HAVE_READLINE */ + + try + { + testLoop(); + } + catch (const Exception& e) + { + cerr << argv[0] << ": " << e.what() << endl; + exit(-1); + } + catch (const bad_alloc&) + { + cerr << argv[0] << ": out of memory" << endl; + exit(-1); + } + + return 0; +} diff --git a/lbtt/src/translate.cc b/lbtt/src/translate.cc new file mode 100644 index 000000000..bf2679c74 --- /dev/null +++ b/lbtt/src/translate.cc @@ -0,0 +1,230 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef __GNUC__ +#pragma implementation +#endif /* __GNUC__ */ + +#include +#include +#include +#include +#include "Exception.h" +#include "LbtWrapper.h" +#include "LtlFormula.h" +#include "SpinWrapper.h" +#ifdef HAVE_GETOPT_LONG +#include +#define OPTIONSTRUCT struct option +#else +#include "gnu-getopt.h" +#define opterr gnu_opterr +#define OPTIONSTRUCT struct gnu_option +#define getopt_long gnu_getopt_long +#endif /* HAVE_GETOPT_LONG */ + +/****************************************************************************** + * + * Pointer to the command line arguments of the program. + * + *****************************************************************************/ + +char** command_line_arguments; + + + +/****************************************************************************** + * + * A function for showing warnings to the user. + * + *****************************************************************************/ + +void printWarning(const string& msg) +{ + cerr << string(command_line_arguments[0]) + ": warning: " + msg << endl; +} + + + +/****************************************************************************** + * + * Signal handler for debugging purposes. + * + *****************************************************************************/ + +RETSIGTYPE signalHandler(int signal_number) +{ + cerr << string(command_line_arguments[0]) + ": received signal " + << signal_number + << endl; + signal(signal_number, SIG_DFL); + raise(signal_number); +} + + + +/****************************************************************************** + * + * Main function. + * + *****************************************************************************/ + +int main(int argc, char** argv) +{ + typedef enum {OPT_HELP = 'h', OPT_LBT, OPT_SPIN, OPT_VERSION = 'v'} + OptionType; + + static OPTIONSTRUCT command_line_options[] = + { + {"help", no_argument, 0, OPT_HELP}, + {"lbt", no_argument, 0, OPT_LBT}, + {"spin", no_argument, 0, OPT_SPIN}, + {"version", no_argument, 0, OPT_VERSION}, + {0, 0, 0, 0} + }; + + command_line_arguments = argv; + + opterr = 1; + int opttype, option_index; + + TranslatorInterface* translator = 0; + + do + { + option_index = 0; + opttype = getopt_long(argc, argv, "hv", command_line_options, + &option_index); + + switch (opttype) + { + case OPT_HELP : + cout << string("Usage: ") << command_line_arguments[0] + << " [translator] [command line for translator] [formula " + "file] [automaton file]\n" + "General options:\n" + " --h, --help Show this help\n" + " --v, --version Show version and exit\n\n" + "Translator options:\n" + " --lbt lbt\n" + " --spin Spin\n" + "The command line for these translators must be given as a " + "single argument\n" + "including the name (and location) of an external program to " + "execute, together\n" + "with any optional parameters to be passed to the " + "program.\n\n"; + exit(0); + break; + + case OPT_LBT : + translator = new LbtWrapper(); + break; + + case OPT_SPIN : + translator = new SpinWrapper(); + break; + + case OPT_VERSION : + cout << "lbtt-translate " PACKAGE_VERSION "\n" + "lbtt-translate is free software; you may change and " + "redistribute it under the\n" + "terms of the GNU General Public License. lbtt-translate " + "comes with NO WARRANTY.\n" + "See the file COPYING for details.\n"; + exit(0); + break; + + case '?' : + case ':' : + exit(-1); + } + } + while (opttype != -1); + + if (argc < 5) + { + cerr << argv[0] << ": too few command line arguments" << endl; + exit(-1); + } + + if (argc > 5) + { + cerr << argv[0] << ": too many command line arguments" << endl; + exit(-1); + } + + int exitstatus = 0; + + signal(SIGHUP, signalHandler); + signal(SIGINT, signalHandler); + signal(SIGQUIT, signalHandler); + signal(SIGILL, signalHandler); + signal(SIGABRT, signalHandler); + signal(SIGFPE, signalHandler); + signal(SIGSEGV, signalHandler); + signal(SIGPIPE, signalHandler); + signal(SIGALRM, signalHandler); + signal(SIGTERM, signalHandler); + + ::Ltl::LtlFormula* formula(0); + + try + { + ifstream input_file; + input_file.open(command_line_arguments[argc - 2], ios::in); + if (!input_file.good()) + throw FileOpenException(command_line_arguments[argc - 2]); + + formula = ::Ltl::LtlFormula::read(input_file); + + translator->translate(*formula, command_line_arguments[argc - 1]); + + ::Ltl::LtlFormula::destruct(formula); + delete translator; + } + catch (...) + { + if (formula != 0) + ::Ltl::LtlFormula::destruct(formula); + + cerr << string(command_line_arguments[0]) + ": "; + exitstatus = -1; + + if (translator != 0) + delete translator; + + try + { + throw; + } + catch (const Exception& e) + { + cerr << e.what(); + } + catch (...) + { + cerr << "fatal error, aborting"; + } + + cerr << endl; + } + + return exitstatus; +} diff --git a/lbtt/src/translate.h b/lbtt/src/translate.h new file mode 100644 index 000000000..b0fb7e53f --- /dev/null +++ b/lbtt/src/translate.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999, 2000, 2001, 2002 + * Heikki Tauriainen + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef TRANSLATE_H +#define TRANSLATE_H + +#ifdef __GNUC__ +#pragma interface +#endif /* __GNUC__ */ + +#include +#include + +/****************************************************************************** + * + * Declarations of external variables and functions defined in translate.cc. + * + *****************************************************************************/ + +extern char** command_line_arguments; /* Command line arguments + * passed to the main + * translator program. + */ + +void printWarning(const string& msg); /* Displays a warning. */ + +#endif /* !TRANSLATE_H */