2018-04-19 15:08:50 +02:00
|
|
|
|
#include <anthem/IntegerVariableDetection.h>
|
|
|
|
|
|
|
|
|
|
#include <anthem/ASTCopy.h>
|
|
|
|
|
#include <anthem/ASTUtils.h>
|
|
|
|
|
#include <anthem/ASTVisitors.h>
|
|
|
|
|
#include <anthem/Exception.h>
|
|
|
|
|
#include <anthem/Simplification.h>
|
|
|
|
|
#include <anthem/output/AST.h>
|
|
|
|
|
|
|
|
|
|
namespace anthem
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// IntegerVariableDetection
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
using VariableDomainMap = std::map<const ast::VariableDeclaration *, ast::Domain>;
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
ast::Domain domain(const ast::Variable &variable, VariableDomainMap &variableDomainMap)
|
|
|
|
|
{
|
|
|
|
|
if (variable.declaration->domain != ast::Domain::Unknown)
|
|
|
|
|
return variable.declaration->domain;
|
|
|
|
|
|
|
|
|
|
const auto match = variableDomainMap.find(variable.declaration);
|
|
|
|
|
|
|
|
|
|
if (match == variableDomainMap.end())
|
|
|
|
|
return ast::Domain::Unknown;
|
|
|
|
|
|
|
|
|
|
return match->second;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void clearVariableDomainMap(VariableDomainMap &variableDomainMap)
|
|
|
|
|
{
|
|
|
|
|
for (auto &variableDeclaration : variableDomainMap)
|
|
|
|
|
variableDeclaration.second = ast::Domain::Unknown;
|
|
|
|
|
}
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
enum class OperationResult
|
|
|
|
|
{
|
|
|
|
|
Unchanged,
|
|
|
|
|
Changed,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
enum class EvaluationResult
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
True,
|
|
|
|
|
False,
|
|
|
|
|
Unknown,
|
|
|
|
|
Error,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
EvaluationResult isArithmetic(const ast::Term &term, VariableDomainMap &variableDomainMap);
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
struct IsTermArithmeticVisitor
|
|
|
|
|
{
|
|
|
|
|
static EvaluationResult visit(const ast::BinaryOperation &binaryOperation, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto isLeftArithemtic = isArithmetic(binaryOperation.left, variableDomainMap);
|
|
|
|
|
const auto isRightArithmetic = isArithmetic(binaryOperation.right, variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isLeftArithemtic == EvaluationResult::Error || isRightArithmetic == EvaluationResult::Error)
|
|
|
|
|
return EvaluationResult::Error;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isLeftArithemtic == EvaluationResult::False || isRightArithmetic == EvaluationResult::False)
|
|
|
|
|
return EvaluationResult::Error;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isLeftArithemtic == EvaluationResult::Unknown || isRightArithmetic == EvaluationResult::Unknown)
|
|
|
|
|
return EvaluationResult::Unknown;
|
|
|
|
|
|
|
|
|
|
return EvaluationResult::True;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Boolean &, VariableDomainMap &)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
return EvaluationResult::False;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Function &function, VariableDomainMap &)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
switch (function.declaration->domain)
|
|
|
|
|
{
|
|
|
|
|
case ast::Domain::General:
|
|
|
|
|
return EvaluationResult::False;
|
|
|
|
|
case ast::Domain::Integer:
|
|
|
|
|
return EvaluationResult::True;
|
|
|
|
|
case ast::Domain::Unknown:
|
|
|
|
|
return EvaluationResult::Unknown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return EvaluationResult::Unknown;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Integer &, VariableDomainMap &)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
return EvaluationResult::True;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Interval &interval, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto isFromArithmetic = isArithmetic(interval.from, variableDomainMap);
|
|
|
|
|
const auto isToArithmetic = isArithmetic(interval.to, variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isFromArithmetic == EvaluationResult::Error || isToArithmetic == EvaluationResult::Error)
|
|
|
|
|
return EvaluationResult::Error;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isFromArithmetic == EvaluationResult::False || isToArithmetic == EvaluationResult::False)
|
|
|
|
|
return EvaluationResult::Error;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isFromArithmetic == EvaluationResult::Unknown || isToArithmetic == EvaluationResult::Unknown)
|
|
|
|
|
return EvaluationResult::Unknown;
|
|
|
|
|
|
|
|
|
|
return EvaluationResult::True;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::SpecialInteger &, VariableDomainMap &)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
return EvaluationResult::False;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::String &, VariableDomainMap &)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
return EvaluationResult::False;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::UnaryOperation &unaryOperation, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto isArgumentArithmetic = isArithmetic(unaryOperation.argument, variableDomainMap);
|
|
|
|
|
|
|
|
|
|
switch (unaryOperation.operator_)
|
|
|
|
|
{
|
|
|
|
|
case ast::UnaryOperation::Operator::Absolute:
|
|
|
|
|
return (isArgumentArithmetic == EvaluationResult::False ? EvaluationResult::Error : isArgumentArithmetic);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return EvaluationResult::Unknown;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Variable &variable, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
switch (domain(variable, variableDomainMap))
|
|
|
|
|
{
|
|
|
|
|
case ast::Domain::General:
|
|
|
|
|
return EvaluationResult::False;
|
|
|
|
|
case ast::Domain::Integer:
|
|
|
|
|
return EvaluationResult::True;
|
|
|
|
|
case ast::Domain::Unknown:
|
|
|
|
|
return EvaluationResult::Unknown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return EvaluationResult::Unknown;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
EvaluationResult isArithmetic(const ast::Term &term, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
return term.accept(IsTermArithmeticVisitor(), variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
EvaluationResult evaluate(const ast::Formula &formula, VariableDomainMap &variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
struct EvaluateFormulaVisitor
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::And &and_, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
bool someFalse = false;
|
|
|
|
|
bool someUnknown = false;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
for (const auto &argument : and_.arguments)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto result = evaluate(argument, variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
switch (result)
|
|
|
|
|
{
|
|
|
|
|
case EvaluationResult::Error:
|
|
|
|
|
return EvaluationResult::Error;
|
|
|
|
|
case EvaluationResult::True:
|
|
|
|
|
break;
|
|
|
|
|
case EvaluationResult::False:
|
|
|
|
|
someFalse = true;
|
|
|
|
|
break;
|
|
|
|
|
case EvaluationResult::Unknown:
|
|
|
|
|
someUnknown = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (someFalse)
|
|
|
|
|
return EvaluationResult::False;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (someUnknown)
|
|
|
|
|
return EvaluationResult::Unknown;
|
|
|
|
|
|
|
|
|
|
return EvaluationResult::True;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Biconditional &biconditional, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto leftResult = evaluate(biconditional.left, variableDomainMap);
|
|
|
|
|
const auto rightResult = evaluate(biconditional.right, variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (leftResult == EvaluationResult::Error || rightResult == EvaluationResult::Error)
|
|
|
|
|
return EvaluationResult::Error;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (leftResult == EvaluationResult::Unknown || rightResult == EvaluationResult::Unknown)
|
|
|
|
|
return EvaluationResult::Unknown;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
return (leftResult == rightResult ? EvaluationResult::True : EvaluationResult::False);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Boolean &boolean, VariableDomainMap &)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
return (boolean.value == true ? EvaluationResult::True : EvaluationResult::False);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Comparison &comparison, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto isLeftArithmetic = isArithmetic(comparison.left, variableDomainMap);
|
|
|
|
|
const auto isRightArithmetic = isArithmetic(comparison.right, variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isLeftArithmetic == EvaluationResult::Error || isRightArithmetic == EvaluationResult::Error)
|
|
|
|
|
return EvaluationResult::Error;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isLeftArithmetic == EvaluationResult::Unknown || isRightArithmetic == EvaluationResult::Unknown)
|
|
|
|
|
return EvaluationResult::Unknown;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isLeftArithmetic == isRightArithmetic)
|
|
|
|
|
return EvaluationResult::Unknown;
|
|
|
|
|
|
|
|
|
|
// Handle the case where one side is arithmetic but the other one isn’t
|
|
|
|
|
switch (comparison.operator_)
|
|
|
|
|
{
|
|
|
|
|
case ast::Comparison::Operator::Equal:
|
|
|
|
|
return EvaluationResult::False;
|
|
|
|
|
case ast::Comparison::Operator::NotEqual:
|
|
|
|
|
return EvaluationResult::True;
|
|
|
|
|
default:
|
|
|
|
|
// TODO: implement more cases
|
|
|
|
|
return EvaluationResult::Unknown;
|
|
|
|
|
}
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Exists &exists, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
return evaluate(exists.argument, variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::ForAll &forAll, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
return evaluate(forAll.argument, variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Implies &implies, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto antecedentResult = evaluate(implies.antecedent, variableDomainMap);
|
|
|
|
|
const auto consequentResult = evaluate(implies.consequent, variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (antecedentResult == EvaluationResult::Error || consequentResult == EvaluationResult::Error)
|
|
|
|
|
return EvaluationResult::Error;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (antecedentResult == EvaluationResult::False)
|
|
|
|
|
return EvaluationResult::True;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (consequentResult == EvaluationResult::True)
|
|
|
|
|
return EvaluationResult::True;
|
|
|
|
|
|
|
|
|
|
if (antecedentResult == EvaluationResult::True && consequentResult == EvaluationResult::False)
|
|
|
|
|
return EvaluationResult::False;
|
|
|
|
|
|
|
|
|
|
return EvaluationResult::Unknown;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::In &in, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto isElementArithmetic = isArithmetic(in.element, variableDomainMap);
|
|
|
|
|
const auto isSetArithmetic = isArithmetic(in.set, variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isElementArithmetic == EvaluationResult::Error || isSetArithmetic == EvaluationResult::Error)
|
|
|
|
|
return EvaluationResult::Error;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isElementArithmetic == EvaluationResult::Unknown || isSetArithmetic == EvaluationResult::Unknown)
|
|
|
|
|
return EvaluationResult::Unknown;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isElementArithmetic == isSetArithmetic)
|
|
|
|
|
return EvaluationResult::Unknown;
|
|
|
|
|
|
|
|
|
|
// If one side is arithmetic, but the other one isn’t, set inclusion is never satisfied
|
|
|
|
|
return EvaluationResult::False;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Not ¬_, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto result = evaluate(not_.argument, variableDomainMap);
|
|
|
|
|
|
|
|
|
|
if (result == EvaluationResult::Error || result == EvaluationResult::Unknown)
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
return (result == EvaluationResult::True ? EvaluationResult::False : EvaluationResult::True);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Or &or_, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
bool someTrue = false;
|
|
|
|
|
bool someUnknown = false;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
for (const auto &argument : or_.arguments)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto result = evaluate(argument, variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
switch (result)
|
|
|
|
|
{
|
|
|
|
|
case EvaluationResult::Error:
|
|
|
|
|
return EvaluationResult::Error;
|
|
|
|
|
case EvaluationResult::True:
|
|
|
|
|
someTrue = true;
|
|
|
|
|
break;
|
|
|
|
|
case EvaluationResult::False:
|
|
|
|
|
break;
|
|
|
|
|
case EvaluationResult::Unknown:
|
|
|
|
|
someUnknown = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (someTrue)
|
|
|
|
|
return EvaluationResult::True;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (someUnknown)
|
|
|
|
|
return EvaluationResult::Unknown;
|
|
|
|
|
|
|
|
|
|
return EvaluationResult::False;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static EvaluationResult visit(const ast::Predicate &predicate, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
assert(predicate.arguments.size() == predicate.declaration->arity());
|
2018-04-19 16:57:27 +02:00
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < predicate.arguments.size(); i++)
|
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto &argument = predicate.arguments[i];
|
|
|
|
|
const auto ¶meter = predicate.declaration->parameters[i];
|
2018-04-19 16:57:27 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (parameter.domain != ast::Domain::Integer)
|
2018-04-19 16:57:27 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
const auto isArgumentArithmetic = isArithmetic(argument, variableDomainMap);
|
2018-04-19 16:57:27 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (isArgumentArithmetic == EvaluationResult::Error || isArgumentArithmetic == EvaluationResult::False)
|
|
|
|
|
return isArgumentArithmetic;
|
2018-04-19 16:57:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
return EvaluationResult::Unknown;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
EvaluationResult evaluate(const ast::Formula &formula, VariableDomainMap &variableDomainMap)
|
|
|
|
|
{
|
|
|
|
|
return formula.accept(EvaluateFormulaVisitor(), variableDomainMap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
OperationResult detectIntegerVariables(ast::Formula &formula, ast::Formula &definition, VariableDomainMap &variableDomainMap);
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2018-04-19 15:08:50 +02:00
|
|
|
|
struct DetectIntegerVariablesVisitor
|
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static OperationResult visit(ast::And &and_, ast::Formula &definition, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
|
|
|
|
auto operationResult = OperationResult::Unchanged;
|
|
|
|
|
|
|
|
|
|
for (auto &argument : and_.arguments)
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (detectIntegerVariables(argument, definition, variableDomainMap) == OperationResult::Changed)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
|
|
|
|
|
return operationResult;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static OperationResult visit(ast::Biconditional &biconditional, ast::Formula &definition, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
|
|
|
|
auto operationResult = OperationResult::Unchanged;
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (detectIntegerVariables(biconditional.left, definition, variableDomainMap) == OperationResult::Changed)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (detectIntegerVariables(biconditional.right, definition, variableDomainMap) == OperationResult::Changed)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
|
|
|
|
|
return operationResult;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static OperationResult visit(ast::Boolean &, ast::Formula &, VariableDomainMap &)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
|
|
|
|
return OperationResult::Unchanged;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static OperationResult visit(ast::Comparison &, ast::Formula &, VariableDomainMap &)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
|
|
|
|
return OperationResult::Unchanged;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static OperationResult visit(ast::Exists &exists, ast::Formula &definition, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
|
|
|
|
auto operationResult = OperationResult::Unchanged;
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (detectIntegerVariables(exists.argument, definition, variableDomainMap) == OperationResult::Changed)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
|
|
|
|
|
for (auto &variableDeclaration : exists.variables)
|
2018-04-20 16:12:54 +02:00
|
|
|
|
{
|
|
|
|
|
if (variableDeclaration->domain != ast::Domain::Unknown)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
clearVariableDomainMap(variableDomainMap);
|
|
|
|
|
|
|
|
|
|
auto argumentResult = evaluate(exists.argument, variableDomainMap);
|
|
|
|
|
auto definitionResult = evaluate(definition, variableDomainMap);
|
|
|
|
|
|
|
|
|
|
if (argumentResult == EvaluationResult::Error || argumentResult == EvaluationResult::False
|
|
|
|
|
|| definitionResult == EvaluationResult::Error || definitionResult == EvaluationResult::False)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-04-19 16:57:27 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
// As a hypothesis, make the parameter’s domain noninteger
|
|
|
|
|
variableDomainMap[variableDeclaration.get()] = ast::Domain::General;
|
|
|
|
|
|
|
|
|
|
argumentResult = evaluate(exists.argument, variableDomainMap);
|
|
|
|
|
definitionResult = evaluate(definition, variableDomainMap);
|
2018-04-19 16:57:27 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (argumentResult == EvaluationResult::Error || argumentResult == EvaluationResult::False
|
|
|
|
|
|| definitionResult == EvaluationResult::Error || definitionResult == EvaluationResult::False)
|
|
|
|
|
{
|
|
|
|
|
// If making the variable noninteger leads to a false or erroneous result, it’s proven to be integer
|
2018-04-19 15:08:50 +02:00
|
|
|
|
operationResult = OperationResult::Changed;
|
2018-04-20 16:12:54 +02:00
|
|
|
|
variableDeclaration->domain = ast::Domain::Integer;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
2018-04-20 16:12:54 +02:00
|
|
|
|
}
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
|
|
|
|
return operationResult;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static OperationResult visit(ast::ForAll &forAll, ast::Formula &definition, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
|
|
|
|
auto operationResult = OperationResult::Unchanged;
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (detectIntegerVariables(forAll.argument, definition, variableDomainMap) == OperationResult::Changed)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
|
|
|
|
|
for (auto &variableDeclaration : forAll.variables)
|
2018-04-20 16:12:54 +02:00
|
|
|
|
{
|
|
|
|
|
if (variableDeclaration->domain != ast::Domain::Unknown)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
clearVariableDomainMap(variableDomainMap);
|
|
|
|
|
|
|
|
|
|
auto argumentResult = evaluate(forAll.argument, variableDomainMap);
|
|
|
|
|
auto definitionResult = evaluate(definition, variableDomainMap);
|
|
|
|
|
|
|
|
|
|
if (argumentResult == EvaluationResult::Error || argumentResult == EvaluationResult::False
|
|
|
|
|
|| definitionResult == EvaluationResult::Error || definitionResult == EvaluationResult::False)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-04-19 16:57:27 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
// As a hypothesis, make the parameter’s domain noninteger
|
|
|
|
|
variableDomainMap[variableDeclaration.get()] = ast::Domain::General;
|
|
|
|
|
|
|
|
|
|
argumentResult = evaluate(forAll.argument, variableDomainMap);
|
|
|
|
|
definitionResult = evaluate(definition, variableDomainMap);
|
2018-04-19 16:57:27 +02:00
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (argumentResult == EvaluationResult::Error || argumentResult == EvaluationResult::False
|
|
|
|
|
|| definitionResult == EvaluationResult::Error || definitionResult == EvaluationResult::False)
|
|
|
|
|
{
|
|
|
|
|
// If making the variable noninteger leads to a false or erroneous result, it’s proven to be integer
|
2018-04-19 15:08:50 +02:00
|
|
|
|
operationResult = OperationResult::Changed;
|
2018-04-20 16:12:54 +02:00
|
|
|
|
variableDeclaration->domain = ast::Domain::Integer;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
2018-04-20 16:12:54 +02:00
|
|
|
|
}
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
|
|
|
|
return operationResult;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static OperationResult visit(ast::Implies &implies, ast::Formula &definition, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
|
|
|
|
auto operationResult = OperationResult::Unchanged;
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (detectIntegerVariables(implies.antecedent, definition, variableDomainMap) == OperationResult::Changed)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (detectIntegerVariables(implies.consequent, definition, variableDomainMap) == OperationResult::Changed)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
|
|
|
|
|
return operationResult;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static OperationResult visit(ast::In &, ast::Formula &, VariableDomainMap &)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
|
|
|
|
return OperationResult::Unchanged;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static OperationResult visit(ast::Not ¬_, ast::Formula &definition, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
return detectIntegerVariables(not_.argument, definition, variableDomainMap);
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static OperationResult visit(ast::Or &or_, ast::Formula &definition, VariableDomainMap &variableDomainMap)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
|
|
|
|
auto operationResult = OperationResult::Unchanged;
|
|
|
|
|
|
|
|
|
|
for (auto &argument : or_.arguments)
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (detectIntegerVariables(argument, definition, variableDomainMap) == OperationResult::Changed)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
|
|
|
|
|
return operationResult;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
static OperationResult visit(ast::Predicate &predicate, ast::Formula &, VariableDomainMap &)
|
2018-04-19 15:08:50 +02:00
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
auto operationResult = OperationResult::Unchanged;
|
|
|
|
|
|
|
|
|
|
assert(predicate.arguments.size() == predicate.declaration->arity());
|
|
|
|
|
|
|
|
|
|
// Propagate integer domains from predicates to variables
|
|
|
|
|
for (size_t i = 0; i < predicate.arguments.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
auto &variableArgument = predicate.arguments[i];
|
|
|
|
|
auto ¶meter = predicate.declaration->parameters[i];
|
|
|
|
|
|
|
|
|
|
if (parameter.domain != ast::Domain::Integer)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!variableArgument.is<ast::Variable>())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
auto &variable = variableArgument.get<ast::Variable>();
|
|
|
|
|
|
|
|
|
|
if (variable.declaration->domain == ast::Domain::Integer)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
variable.declaration->domain = ast::Domain::Integer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return operationResult;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
OperationResult detectIntegerVariables(ast::Formula &formula, ast::Formula &definition, VariableDomainMap &variableDomainMap)
|
|
|
|
|
{
|
|
|
|
|
return formula.accept(DetectIntegerVariablesVisitor(), definition, variableDomainMap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2018-04-19 15:08:50 +02:00
|
|
|
|
// Assumes the completed formulas to be in translated but not simplified form.
|
|
|
|
|
// That is, completed formulas are either variable-free or universally quantified
|
|
|
|
|
void detectIntegerVariables(std::vector<ast::Formula> &completedFormulas)
|
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
VariableDomainMap variableDomainMap;
|
|
|
|
|
auto operationResult = OperationResult::Changed;
|
2018-04-19 15:08:50 +02:00
|
|
|
|
|
|
|
|
|
while (operationResult == OperationResult::Changed)
|
|
|
|
|
{
|
|
|
|
|
operationResult = OperationResult::Unchanged;
|
|
|
|
|
|
|
|
|
|
for (auto &completedFormula : completedFormulas)
|
|
|
|
|
{
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (detectIntegerVariables(completedFormula, completedFormula, variableDomainMap) == OperationResult::Changed)
|
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
|
2018-04-19 15:08:50 +02:00
|
|
|
|
if (!completedFormula.is<ast::ForAll>())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
auto &forAll = completedFormula.get<ast::ForAll>();
|
|
|
|
|
|
|
|
|
|
if (!forAll.argument.is<ast::Biconditional>())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
auto &biconditional = forAll.argument.get<ast::Biconditional>();
|
|
|
|
|
|
|
|
|
|
if (!biconditional.left.is<ast::Predicate>())
|
|
|
|
|
continue;
|
|
|
|
|
|
2018-04-19 16:57:27 +02:00
|
|
|
|
auto &predicate = biconditional.left.get<ast::Predicate>();
|
2018-04-19 15:08:50 +02:00
|
|
|
|
auto &definition = biconditional.right;
|
|
|
|
|
|
2018-04-19 16:57:27 +02:00
|
|
|
|
assert(predicate.arguments.size() == predicate.declaration->arity());
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (detectIntegerVariables(definition, definition, variableDomainMap) == OperationResult::Changed)
|
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
|
2018-04-19 16:57:27 +02:00
|
|
|
|
for (size_t i = 0; i < predicate.arguments.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
auto &variableArgument = predicate.arguments[i];
|
2018-04-20 16:12:54 +02:00
|
|
|
|
auto ¶meter = predicate.declaration->parameters[i];
|
2018-04-19 16:57:27 +02:00
|
|
|
|
|
|
|
|
|
assert(variableArgument.is<ast::Variable>());
|
|
|
|
|
|
|
|
|
|
auto &variable = variableArgument.get<ast::Variable>();
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
if (parameter.domain != ast::Domain::Unknown)
|
2018-04-19 16:57:27 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
2018-04-20 16:12:54 +02:00
|
|
|
|
clearVariableDomainMap(variableDomainMap);
|
|
|
|
|
|
|
|
|
|
auto result = evaluate(definition, variableDomainMap);
|
|
|
|
|
|
|
|
|
|
if (result == EvaluationResult::Error || result == EvaluationResult::False)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// As a hypothesis, make the parameter’s domain noninteger
|
|
|
|
|
variableDomainMap[variable.declaration] = ast::Domain::General;
|
|
|
|
|
|
|
|
|
|
result = evaluate(definition, variableDomainMap);
|
|
|
|
|
|
|
|
|
|
if (result == EvaluationResult::Error || result == EvaluationResult::False)
|
|
|
|
|
{
|
|
|
|
|
// If making the variable noninteger leads to a false or erroneous result, it’s proven to be integer
|
|
|
|
|
operationResult = OperationResult::Changed;
|
|
|
|
|
variable.declaration->domain = ast::Domain::Integer;
|
|
|
|
|
parameter.domain = ast::Domain::Integer;
|
|
|
|
|
}
|
2018-04-19 16:57:27 +02:00
|
|
|
|
}
|
2018-04-19 15:08:50 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
}
|