spot/lbtt/src/UserCommands.cc

2019 lines
68 KiB
C++
Raw Blame History

/*
* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
* Heikki Tauriainen <Heikki.Tauriainen@tkk.fi>
*
* 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 <config.h>
#include "BuchiProduct.h"
#include "DispUtil.h"
#include "Exception.h"
#include "PathEvaluator.h"
#include "Product.h"
#include "SharedTestData.h"
#include "StatDisplay.h"
#include "StateSpaceProduct.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;
/* ========================================================================= */
unsigned long int parseAlgorithmId(const string& id)
/* ----------------------------------------------------------------------------
*
* Description: Parses an algorithm identifier (either a symbolic or a
* numeric one).
*
* Argument: id -- String containing the identifier.
*
* Returns: The numeric identifier of the algorithm. Throws a
* CommandErrorException if the identifier is not recognizable
* as a proper algorithm identifier.
*
* ------------------------------------------------------------------------- */
{
unsigned long int result;
string unquoted_id = unquoteString(id);
try
{
result = parseNumber(unquoted_id);
verifyNumber(result, round_info.number_of_translators,
"Implementation identifier out of range");
}
catch (const NotANumberException&)
{
map<string, unsigned long int>::const_iterator id_finder
= configuration.algorithm_names.find(unquoted_id);
if (id_finder == configuration.algorithm_names.end())
throw CommandErrorException
("Unknown implementation identifier (`" + unquoted_id + "').");
result = id_finder->second;
}
return result;
}
/* ========================================================================= */
void parseAlgorithmIdList(const string& ids, IntervalList& algorithms)
/* ----------------------------------------------------------------------------
*
* Description: Parses a list of algorithm identifiers specified either as
* comma-separated intervals or algorithm identifiers.
*
* Arguments: ids -- A constant reference to a string containing
* the list of algorithm identifiers.
* algorithms -- A reference to an IntervalList for storing
* the numeric identifiers of the algorithms.
*
* Returns: Nothing. Throws a CommandErrorException if the identifier
* list includes a string not recognizable as an algorithm
* identifier.
*
* ------------------------------------------------------------------------- */
{
/*
* Make a copy of `ids' in which each comma (',') within double quotes is
* substituted with a newline. This is necessary to handle symbolic
* algorithm identifiers with commas correctly.
*/
string id_string = substituteInQuotedString(ids, ",", "\n", INSIDE_QUOTES);
try
{
vector<string> nonnumeric_algorithm_ids;
parseIntervalList(id_string, algorithms, 0,
round_info.number_of_translators - 1,
&nonnumeric_algorithm_ids);
for (vector<string>::iterator id = nonnumeric_algorithm_ids.begin();
id != nonnumeric_algorithm_ids.end();
++id)
{
*id = unquoteString(substituteInQuotedString(*id, "\n", ","));
map<string, unsigned long int>::const_iterator
id_finder = configuration.algorithm_names.find(*id);
if (id_finder == configuration.algorithm_names.end())
throw CommandErrorException
("Unknown implementation identifier (`" + *id + "').");
algorithms.merge(id_finder->second);
}
}
catch (const IntervalRangeException& e)
{
throw CommandErrorException
(string("Implementation identifier out of range (")
+ toString(e.getNumber())
+ ").");
}
}
/* ========================================================================= */
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.flush();
}
/* ========================================================================= */
void printCrossComparisonAnalysisResults
(ostream& stream, int indent, bool formula_type,
const vector<string>& input_tokens)
/* ----------------------------------------------------------------------------
*
* 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.
*
* 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.");
algorithm1 = parseAlgorithmId(input_tokens[1]);
algorithm2 = parseAlgorithmId(input_tokens[2]);
if (algorithm1 == algorithm2)
throw CommandErrorException("Implementation identifiers must be "
"different.");
/*
* Arrange the algorithm identifiers such that `algorithm1' never refers to
* the internal model checking algorithm (swap `algorithm1' and `algorithm2'
* if necessary).
*/
if (configuration.isInternalAlgorithm(algorithm1))
{
path_compare = true;
algorithm1 ^= algorithm2;
algorithm2 ^= algorithm1;
algorithm1 ^= algorithm2;
}
if (configuration.isInternalAlgorithm(algorithm2))
path_compare = true;
if (path_compare
&& !(configuration.global_options.statespace_generation_mode
& Configuration::PATH))
throw CommandErrorException("This feature is available only when using "
"paths as state spaces.");
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];
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)
+ ".",
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) + ".",
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<double>
(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(),
"State identifier out of range");
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.",
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.",
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);
::Graph::Product<Graph::StateSpaceProduct>::Witness witness;
if (!path_compare || accepting_algorithm == algorithm1)
{
/*
* Search the product automaton for an accepting cycle.
*/
const BuchiAutomaton& automaton
= *test_results[accepting_algorithm].automaton_stats[formula]
.buchi_automaton;
::Graph::Product<Graph::StateSpaceProduct> product
(automaton, *round_info.statespace);
try
{
printText("<searching for a system execution producing contradictory "
"results>", 0, 2);
product.findWitness(automaton.initialState(), state, witness);
if (witness.cycle.first.empty())
throw Exception
("UserCommands::printCrossComparisonAnalysisResults(...): internal "
"error [witness construction failed]");
}
catch (const UserBreakException&)
{
printText(" user break\n", 0);
throw;
}
catch (...)
{
printText(" error\n", 0);
throw;
}
printText(" ok\n\n", 0);
}
else
{
const StateSpace::size_type loop_state
= (*((*round_info.statespace)[round_info.statespace->size() - 1].
edges().begin()))->targetNode();
const StateSpace::size_type loop_length
= round_info.statespace->size() - loop_state;
for ( ; state < loop_state; ++state)
witness.prefix.second.push_back(StateSpace::PathElement(state, 0));
state -= loop_state;
for (StateSpace::size_type s = 0; s < loop_length; ++s)
{
witness.cycle.second.push_back
(StateSpace::PathElement(state + loop_state, 0));
state = (state + 1) % loop_length;
}
}
/*
* 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, witness.prefix.second, witness.cycle.second,
*(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], witness.prefix.second,
witness.cycle.second, *round_info.statespace);
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))
+ ".",
78);
if (!result)
{
estream << '\n';
printAcceptingCycle(stream, indent + 2, accepting_algorithm,
witness.prefix.first, witness.cycle.first,
*(test_results[accepting_algorithm].
automaton_stats[formula].buchi_automaton),
witness.prefix.second, witness.cycle.second,
*round_info.statespace);
}
}
/* ========================================================================= */
void printConsistencyAnalysisResults
(ostream& stream, int indent, const vector<string>& 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 = parseAlgorithmId(input_tokens[1]);
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)
+ ".",
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.",
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(),
"State identifier out of range");
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.",
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.",
78);
return;
}
}
vector<StateSpace::size_type> path;
StateSpace::Path prefix, cycle;
map<StateSpace::size_type, 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(StateSpace::PathElement(path[s], 0));
for (StateSpace::size_type s = loop_state; s < path.size(); s++)
cycle.push_back(StateSpace::PathElement(path[s], 0));
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], prefix, cycle, *round_info.statespace);
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.",
78);
}
/* ========================================================================= */
void printAutomatonAnalysisResults
(ostream& stream, int indent, const vector<string>& input_tokens)
/* ----------------------------------------------------------------------------
*
* 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.
* 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_intr_test)
throw CommandErrorException("This command is available only when the "
"B<EFBFBD>chi automata intersection emptiness check "
"is enabled.");
unsigned long int algorithm1 = parseAlgorithmId(input_tokens[1]);
unsigned long int algorithm2 = parseAlgorithmId(input_tokens[2]);
if (configuration.isInternalAlgorithm(algorithm1)
|| configuration.isInternalAlgorithm(algorithm2))
throw CommandErrorException
("This feature is not available for lbtt's internal "
"model checking algorithm.");
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)")
+ ".",
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();
::Graph::Product<Graph::BuchiProduct>::Witness witness;
try
{
printText("<searching for an accepting execution>", 0, 2);
const BuchiAutomaton& automaton_1
= *(test_results[algorithm1].automaton_stats[0].buchi_automaton);
const BuchiAutomaton& automaton_2
= *(test_results[algorithm2].automaton_stats[1].buchi_automaton);
::Graph::Product<Graph::BuchiProduct> product(automaton_1, automaton_2);
product.findWitness(automaton_1.initialState(), automaton_2.initialState(),
witness);
if (witness.cycle.first.empty())
throw Exception
("UserCommands::printAutomatonAnalysisResults(...): internal error "
"[witness construction failed]");
}
catch (const UserBreakException&)
{
printText(" user break\n", 0);
throw;
}
catch (...)
{
printText(" error\n", 0);
throw;
}
printText(" ok\n", 0);
const unsigned long int valuation_size
= configuration.formula_options.formula_generator
.number_of_available_variables;
const StateSpace::size_type path_length
= witness.prefix.first.size() + witness.cycle.first.size();
StateSpace path(valuation_size, path_length);
StateSpace::size_type state = 0;
StateSpace::Path::const_iterator p1, p2;
StateSpace::Path path_prefix, path_cycle;
for (int i = 0; i < 2; ++i)
{
const pair<StateSpace::Path, StateSpace::Path>* witness_segment;
StateSpace::Path* path_segment;
if (i == 0)
{
witness_segment = &witness.prefix;
path_segment = &path_prefix;
}
else
{
witness_segment = &witness.cycle;
path_segment = &path_cycle;
}
for (p1 = witness_segment->first.begin(),
p2 = witness_segment->second.begin();
p1 != witness_segment->first.end();
++p1, ++p2, ++state)
{
::Ltl::LtlFormula* f
= &::Ltl::And::construct
(static_cast<const BuchiAutomaton::BuchiTransition&>(p1->edge())
.guard(),
static_cast<const BuchiAutomaton::BuchiTransition&>(p2->edge())
.guard());
path[state].positiveAtoms().copy
(f->findPropositionalModel(valuation_size - 1), valuation_size);
path_segment->push_back(StateSpace::PathElement(state, 0));
::Ltl::LtlFormula::destruct(f);
}
}
/*
* Display the input sequence accepted by both automata.
*/
estream << string(indent + 2, ' ')
+ "Execution M accepted by both automata:\n";
printPath(stream, indent + 4, path_prefix, path_cycle, path);
estream << '\n';
/*
* For each of the original automata, display the accepting runs that
* these automata have on the input sequence.
*/
for (int i = 0; i < 2; ++i)
{
const BuchiAutomaton::Path* prefix;
const BuchiAutomaton::Path* cycle;
if (i == 0)
{
prefix = &witness.prefix.first;
cycle = &witness.cycle.first;
}
else
{
prefix = &witness.prefix.second;
cycle = &witness.cycle.second;
}
printAcceptingCycle(stream, indent + 2, (i == 0 ? algorithm1 : algorithm2),
*prefix, *cycle,
*(test_results[i == 0 ? algorithm1 : algorithm2]
.automaton_stats[i].buchi_automaton),
path_prefix, path_cycle, path);
estream << '\n';
}
/*
* Display a proof or a refutation for the formula in the execution.
*/
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],
path_prefix, path_cycle, path);
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<EFBFBD>chi automaton constructed for the "
+ (result ? "nega" : "posi") + "tive formula "
+ (algorithm1 == algorithm2
? ""
: "(the automaton constructed by implementation "
+ configuration.algorithmString(result ? algorithm2
: algorithm1)
+ ") ")
+ "is incorrect.",
78);
}
/* ========================================================================= */
void printPath
(ostream& stream, int indent, const StateSpace::Path& prefix,
const StateSpace::Path& cycle, const StateSpace& path)
/* ----------------------------------------------------------------------------
*
* Description: Writes information about a path in a state space to a stream.
*
* Arguments: stream -- A reference to an output stream.
* indent -- Number of spaces to leave to the left of output.
* prefix -- The prefix of the path.
* cycle -- The cycle of the path.
* path -- The state space to which the state identifiers in
* the prefix and the cycle refer.
*
* Returns: Nothing.
*
* ------------------------------------------------------------------------- */
{
Exceptional_ostream estream(&stream, ios::badbit | ios::failbit);
const StateSpace::Path* path_segment;
for (int i = 0; i < 2; ++i)
{
if (i == 0 && prefix.empty())
continue;
estream << string(indent, ' ');
if (i == 0)
{
path_segment = &prefix;
estream << "prefix";
}
else
{
path_segment = &cycle;
estream << "cycle";
}
estream << ":\n";
bool first_printed;
for (StateSpace::Path::const_iterator path_element = path_segment->begin();
path_element != path_segment->end();
++path_element)
{
if (path_element != path_segment->begin())
estream << toString(path_element->node()) + "\n";
estream << string(indent + 2, ' ') + 's' + toString(path_element->node())
+ " {";
first_printed = false;
for (unsigned long int proposition = 0;
proposition < path.numberOfPropositions();
++proposition)
{
if (path[path_element->node()].positiveAtoms().test(proposition))
{
if (first_printed)
estream << ',';
else
first_printed = true;
estream << 'p' + toString(proposition);
}
}
estream << "} --> s";
}
estream << toString(cycle.begin()->node()) + "\n";
}
estream.flush();
}
/* ========================================================================= */
void printAcceptingCycle
(ostream& stream, int indent,
vector<Configuration::AlgorithmInformation>::size_type algorithm_id,
const BuchiAutomaton::Path& aut_prefix,
const BuchiAutomaton::Path& aut_cycle,
const BuchiAutomaton& automaton, const StateSpace::Path& path_prefix,
const StateSpace::Path& path_cycle, const StateSpace& path)
/* ----------------------------------------------------------------------------
*
* Description: Writes information about an execution of a B<>chi automaton 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.
* aut_prefix -- The prefix of a path in a B<>chi automaton.
* aut_cycle -- The cycle of a path in a B<>chi automaton.
* automaton -- The B<>chi automaton to which the state
* identifiers in `aut_prefix' and `aut_cycle'
* refer.
* path_prefix -- The prefix of a path in a state space.
* (This path is interpreted as the input for
* the B<>chi automaton.) It is assumed that
* `path_prefix.size() == aut_prefix.size()'.
* path_cycle -- The cycle of a path in a state space. It
* is assumed that `path_cycle.size() ==
* aut_cycle.size()'.
* path -- The state space to which the state
* identifiers in `path_prefix' and
* `path_cycle' refer.
*
* Returns: Nothing.
*
* ------------------------------------------------------------------------- */
{
Exceptional_ostream estream(&stream, ios::badbit | ios::failbit);
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);
const BuchiAutomaton::Path* aut_segment;
const StateSpace::Path* path_segment;
BuchiAutomaton::Path::const_iterator aut_state;
StateSpace::Path::const_iterator path_state;
for (int i = 0; i < 2; ++i)
{
if (i == 0 && aut_prefix.empty())
continue;
estream << string(indent + 2, ' ');
if (i == 0)
{
estream << "prefix";
aut_segment = &aut_prefix;
path_segment = &path_prefix;
}
else
{
estream << "cycle";
aut_segment = &aut_cycle;
path_segment = &path_cycle;
}
estream << ":\n";
bool first_printed = false;
for (aut_state = aut_segment->begin(), path_state = path_segment->begin();
aut_state != aut_segment->end();
++aut_state, ++path_state)
{
estream << string(indent + 4, ' ') + toString(aut_state->node()) + ' ';
const BitArray* bits;
for (int j = 0; j < 2; ++j)
{
bits = (j == 0
? &automaton[aut_state->node()].acceptanceSets()
: &static_cast<const BuchiAutomaton::BuchiTransition&>
(aut_state->edge()).acceptanceSets());
first_printed = false;
for (unsigned long int accept_set = 0;
accept_set < automaton.numberOfAcceptanceSets();
++accept_set)
{
if (bits->test(accept_set))
{
if (first_printed)
estream << ',';
else
{
first_printed = true;
if (j == 1)
estream << "--";
estream << '{';
}
estream << accept_set;
}
}
if (first_printed)
{
estream << "}";
if (j == 0)
estream << ' ';
}
}
estream << "--> " + toString(aut_state->edge().targetNode()) + " [ {";
bits = &path[path_state->node()].positiveAtoms();
first_printed = false;
for (unsigned long int proposition = 0;
proposition < path.numberOfPropositions();
++proposition)
{
if (bits->test(proposition))
{
if (first_printed)
estream << ',';
else
first_printed = true;
estream << 'p' + toString(proposition);
}
}
estream << "} |== ";
static_cast<const BuchiAutomaton::BuchiTransition&>(aut_state->edge())
.guard().print(estream);
estream << " ]\n";
}
}
estream.flush();
}
/* ========================================================================= */
void printBuchiAutomaton
(ostream& stream, int indent, bool formula_type,
vector<string>& 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 = parseAlgorithmId(input_tokens[1]);
int formula
= (configuration.formula_options.output_mode == Configuration::NNF
? 0 : 2)
+ (formula_type ? 0 : 1);
if (configuration.isInternalAlgorithm(algorithm))
throw CommandErrorException
("This feature is not available for lbtt's internal "
"model checking algorithm.");
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])
+ "'.",
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<EFBFBD>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
{
IntervalList states;
if (input_tokens.size() == 2)
input_tokens.push_back("*");
try
{
parseIntervalList(input_tokens[2], states, 0, automaton->size() - 1);
}
catch (const IntervalRangeException& e)
{
throw CommandErrorException
(string("State identifier out of range (")
+ toString(e.getNumber())
+ ").");
}
for (IntervalList::const_iterator state = states.begin();
state != states.end();
++state)
{
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.flush();
}
/* ========================================================================= */
void evaluateFormula
(ostream& stream, int indent, bool formula_type,
vector<string>& 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);
IntervalList algorithms;
IntervalList system_states;
string algorithm_name;
int formula = (formula_type ? 0 : 1);
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 no list of algorithms was given as an argument, show the results of
* all algorithms.
*/
if (input_tokens.size() < 2)
input_tokens.push_back("*");
parseAlgorithmIdList(input_tokens[1], algorithms);
/*
* 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("*");
try
{
parseIntervalList(input_tokens[2], system_states, 0,
round_info.real_emptiness_check_size - 1);
}
catch (const IntervalRangeException& e)
{
throw CommandErrorException
(string("State identifier out of range (")
+ toString(e.getNumber())
+ ").");
}
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 (IntervalList::const_iterator state = system_states.begin();
state != system_states.end();
++state)
{
estream << string(indent + 2, ' ') + "State " + toString(*state) + ":\n";
for (IntervalList::const_iterator algorithm = algorithms.begin();
algorithm != algorithms.end();
++algorithm)
{
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 << (configuration.isInternalAlgorithm(*algorithm)
? "model checking result not available\n"
: "emptiness check not performed\n");
else
estream << (test_results[*algorithm].automaton_stats[formula].
emptiness_check_result[*state]
? "true"
: "false")
<< '\n';
}
}
estream.flush();
}
/* ========================================================================= */
void printFormula
(ostream& stream, int indent, bool formula_type,
const vector<string>& input_tokens)
/* ----------------------------------------------------------------------------
*
* 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.
* input_tokens -- A reference to a vector of strings
* containing the arguments of the user
* command.
*
* 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);
if (input_tokens.size() <= 1 || input_tokens[1] == "normal")
{
estream << string(indent, ' ');
round_info.formulae[formula_type ? 2 : 3]->print(estream);
}
else if (input_tokens[1] == "nnf")
{
estream << string(indent, ' ');
if (configuration.formula_options.output_mode != Configuration::NNF)
{
::Ltl::LtlFormula* f
= round_info.formulae[formula_type ? 2 : 3]->nnfClone();
f->print(estream);
::Ltl::LtlFormula::destruct(f);
}
else
round_info.formulae[formula_type ? 0 : 1]->print(estream);
}
else
throw CommandErrorException
("`" + input_tokens[1] + "' is not a valid formula mode.");
estream << '\n';
estream.flush();
}
/* ========================================================================= */
void printCommandHelp
(ostream& stream, int indent, const vector<string>& 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"
+ string(indent, ' ') + "implementations\n"
+ string(indent, ' ') + "translators\n";
printTextBlock(stream, indent + 4,
"List all implementations currently available for "
"testing.",
78);
break;
case BUCHI :
estream << string(indent, ' ') + "buchi [\"+\"|\"-\"] <implementation> "
"[states|\"dot\"]\n";
printTextBlock(stream, indent + 4,
"Display information about a (list of) state(s) of a "
"B<EFBFBD>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 <implementation 1> "
"<implementation 2>\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 <implementation> [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.",
78);
break;
case FORMULA :
estream << string(indent, ' ')
+ "formula [\"+\"|\"-\"] [\"normal\"|\"nnf\"]\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; \"normal\" and "
"\"nnf\" select between the display mode). If no formula "
"(display mode) is specified, show the positive formula "
"(the formula as generated).",
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 [\"+\"|\"-\"] "
"<implementation> <implementation> "
"[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. 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"
"implementations\n"
"inconsistencies\n"
"quit\n"
"resultanalysis\n"
"results\n"
"skip\n"
"statespace\n"
"statistics\n"
"translators\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 `| <command>'.",
78);
break;
}
estream.flush();
}
/* ========================================================================= */
void printInconsistencies
(ostream& stream, int indent, vector<string>& 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);
IntervalList algorithms;
if (!configuration.global_options.do_cons_test)
throw CommandErrorException("This command is available only when the "
"model checking result consistency check is "
"enabled.");
if (input_tokens.size() == 1)
input_tokens.push_back("*");
parseAlgorithmIdList(input_tokens[1], algorithms);
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';
for (IntervalList::const_iterator algorithm = algorithms.begin();
algorithm != algorithms.end();
++algorithm)
{
estream << '\n'
+ 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.flush();
}
/* ========================================================================= */
void printTestResults
(ostream& stream, int indent, vector<string>& 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);
IntervalList algorithms;
if (input_tokens.size() == 1)
input_tokens.push_back("*");
parseAlgorithmIdList(input_tokens[1], algorithms);
estream << string(indent, ' ') + "Test results for round "
+ toString(round_info.current_round) << ":\n\n";
if (configuration.global_options.verbosity <= 2)
printStatTableHeader(stream, indent);
for (IntervalList::const_iterator algorithm = algorithms.begin();
algorithm != algorithms.end();
++algorithm)
printAllStats(stream, indent, *algorithm);
if (configuration.global_options.verbosity <= 2)
estream << '\n';
if (configuration.global_options.do_comp_test)
{
estream << string(indent, ' ')
+ "Model checking result cross-comparison:\n";
printCrossComparisonStats(stream, indent + 2, algorithms);
}
if (configuration.global_options.do_intr_test)
{
estream << string(indent, ' ')
+ "B<EFBFBD>chi automata intersection emptiness check:\n";
printBuchiIntersectionCheckStats(stream, indent + 2, algorithms);
}
estream.flush();
}
/* ========================================================================= */
void printStateSpace
(ostream& stream, int indent, vector<string>& 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);
IntervalList states;
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)
{
if (input_tokens.size() == 1)
input_tokens.push_back("*");
try
{
parseIntervalList(input_tokens[1], states, 0,
round_info.statespace->size() - 1);
}
catch (const IntervalRangeException& e)
{
throw CommandErrorException
(string("State identifier out of range (")
+ toString(e.getNumber())
+ ").");
}
estream << string(indent, ' ') + "State space information:\n";
for (IntervalList::const_iterator state = states.begin();
state != states.end();
++state)
{
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.flush();
}
/* ========================================================================= */
void changeVerbosity(const vector<string>& 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<string>& 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.
*
* ------------------------------------------------------------------------- */
{
IntervalList algorithms;
if (input_tokens.size() < 2)
input_tokens.push_back("*");
parseAlgorithmIdList(input_tokens[1], algorithms);
for (IntervalList::const_iterator algorithm = algorithms.begin();
algorithm != algorithms.end();
++algorithm)
{
printText(string(enable ? "En" : "Dis")
+ "abling implementation "
+ configuration.algorithmString(*algorithm)
+ ".\n",
0,
2);
configuration.algorithms[*algorithm].enabled = enable;
}
round_info.cout << '\n';
round_info.cout.flush();
}
}