Skip to content

Commit 1027f98

Browse files
committed
Introduce euclidean_mod expression
This introduces a variant of the modulo operator, to be used on integers, which uses Boute's Euclidean definition. The result is never negative, and thus differs from the C99 "%" operator. The semantics match what SMT-LIB2 does in the integer theory. The operator % is not defined by SMT-LIB2, and is removed from the parser.
1 parent 9a04436 commit 1027f98

File tree

5 files changed

+71
-1
lines changed

5 files changed

+71
-1
lines changed

src/solvers/smt2/smt2_conv.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,10 @@ void smt2_convt::convert_expr(const exprt &expr)
12851285
{
12861286
convert_mod(to_mod_expr(expr));
12871287
}
1288+
else if(expr.id() == ID_euclidean_mod)
1289+
{
1290+
convert_euclidean_mod(to_euclidean_mod_expr(expr));
1291+
}
12881292
else if(expr.id()==ID_mult)
12891293
{
12901294
convert_mult(to_mult_expr(expr));
@@ -2988,6 +2992,21 @@ void smt2_convt::convert_constant(const constant_exprt &expr)
29882992
UNEXPECTEDCASE("unknown constant: "+expr_type.id_string());
29892993
}
29902994

2995+
void smt2_convt::convert_euclidean_mod(const euclidean_mod_exprt &expr)
2996+
{
2997+
if(expr.type().id() == ID_integer)
2998+
{
2999+
out << "(mod ";
3000+
convert_expr(expr.op0());
3001+
out << ' ';
3002+
convert_expr(expr.op1());
3003+
out << ')';
3004+
}
3005+
else
3006+
UNEXPECTEDCASE(
3007+
"unsupported type for euclidean_mod: " + expr.type().id_string());
3008+
}
3009+
29913010
void smt2_convt::convert_mod(const mod_exprt &expr)
29923011
{
29933012
if(expr.type().id()==ID_unsignedbv ||

src/solvers/smt2/smt2_conv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class smt2_convt : public stack_decision_proceduret
120120
void convert_floatbv_div(const ieee_float_op_exprt &expr);
121121
void convert_floatbv_mult(const ieee_float_op_exprt &expr);
122122
void convert_mod(const mod_exprt &expr);
123+
void convert_euclidean_mod(const euclidean_mod_exprt &expr);
123124
void convert_index(const index_exprt &expr);
124125
void convert_member(const member_exprt &expr);
125126

src/solvers/smt2/smt2_parser.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,12 @@ void smt2_parsert::setup_expressions()
10671067

10681068
expressions["bvurem"] = [this] { return binary(ID_mod, operands()); };
10691069

1070-
expressions["%"] = [this] { return binary(ID_mod, operands()); };
1070+
expressions["mod"] = [this] {
1071+
// SMT-LIB2 uses Boute's Euclidean definition for mod,
1072+
// which is never negative and undefined when the second
1073+
// argument is zero.
1074+
return binary(ID_euclidean_mod, operands());
1075+
};
10711076

10721077
expressions["concat"] = [this] {
10731078
auto op = operands();

src/util/irep_ids.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ IREP_ID_ONE(array_equal)
235235
IREP_ID_ONE(array_set)
236236
IREP_ID_ONE(array_copy)
237237
IREP_ID_ONE(array_list)
238+
IREP_ID_ONE(euclidean_mod)
238239
IREP_ID_ONE(mod)
239240
IREP_ID_ONE(rem)
240241
IREP_ID_ONE(shr)

src/util/std_expr.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,50 @@ inline mod_exprt &to_mod_expr(exprt &expr)
10881088
return ret;
10891089
}
10901090

1091+
/// \brief Boute's Euclidean definition of Modulo -- to match SMT-LIB2
1092+
class euclidean_mod_exprt : public binary_exprt
1093+
{
1094+
public:
1095+
euclidean_mod_exprt(exprt _lhs, exprt _rhs)
1096+
: binary_exprt(std::move(_lhs), ID_euclidean_mod, std::move(_rhs))
1097+
{
1098+
}
1099+
};
1100+
1101+
template <>
1102+
inline bool can_cast_expr<euclidean_mod_exprt>(const exprt &base)
1103+
{
1104+
return base.id() == ID_euclidean_mod;
1105+
}
1106+
1107+
inline void validate_expr(const euclidean_mod_exprt &value)
1108+
{
1109+
validate_operands(value, 2, "Modulo must have two operands");
1110+
}
1111+
1112+
/// \brief Cast an exprt to a \ref euclidean_mod_exprt
1113+
///
1114+
/// \a expr must be known to be \ref euclidean_mod_exprt.
1115+
///
1116+
/// \param expr: Source expression
1117+
/// \return Object of type \ref euclidean_mod_exprt
1118+
inline const euclidean_mod_exprt &to_euclidean_mod_expr(const exprt &expr)
1119+
{
1120+
PRECONDITION(expr.id() == ID_euclidean_mod);
1121+
const euclidean_mod_exprt &ret =
1122+
static_cast<const euclidean_mod_exprt &>(expr);
1123+
validate_expr(ret);
1124+
return ret;
1125+
}
1126+
1127+
/// \copydoc to_euclidean_mod_expr(const exprt &)
1128+
inline euclidean_mod_exprt &to_euclidean_mod_expr(exprt &expr)
1129+
{
1130+
PRECONDITION(expr.id() == ID_euclidean_mod);
1131+
euclidean_mod_exprt &ret = static_cast<euclidean_mod_exprt &>(expr);
1132+
validate_expr(ret);
1133+
return ret;
1134+
}
10911135

10921136
/// \brief Remainder of division
10931137
class rem_exprt:public binary_exprt

0 commit comments

Comments
 (0)