Skip to content

Commit 03fc328

Browse files
Lukasz A.J. Wronaromainbrenguier
authored andcommitted
Move string_constraint functions out of string_refinement
1 parent 3d3296c commit 03fc328

File tree

3 files changed

+143
-140
lines changed

3 files changed

+143
-140
lines changed

src/solvers/refinement/string_constraint.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
// Author: DiffBlue Ltd.
2+
3+
#include <stack>
14
#include "string_constraint.h"
5+
#include <util/simplify_expr.h>
6+
#include <util/expr_iterator.h>
27

38
void replace(string_constraintt &axiom, const replace_mapt &symbol_resolve)
49
{
@@ -59,3 +64,132 @@ string_not_contains_constraintt &to_string_not_contains_constraint(exprt &expr)
5964
"operands"));
6065
return static_cast<string_not_contains_constraintt &>(expr);
6166
}
67+
68+
typedef std::map<exprt, std::vector<exprt>> array_index_mapt;
69+
70+
static array_index_mapt gather_indices(const exprt &expr)
71+
{
72+
array_index_mapt indices;
73+
std::for_each(
74+
expr.depth_begin(),
75+
expr.depth_end(),
76+
[&](const exprt &expr) { // NOLINT
77+
if(const auto index_expr = expr_try_dynamic_cast<const index_exprt>(expr))
78+
indices[index_expr->array()].push_back(index_expr->index());
79+
});
80+
return indices;
81+
}
82+
83+
/// \param expr: an expression
84+
/// \param var: a symbol
85+
/// \return Boolean telling whether `expr` is a linear function of `var`.
86+
/// \todo Add unit tests
87+
/// \related string_constraintt
88+
static bool
89+
is_linear_arithmetic_expr(const exprt &expr, const symbol_exprt &var)
90+
{
91+
for(auto it = expr.depth_begin(); it != expr.depth_end();)
92+
{
93+
if(
94+
it->id() != ID_plus && it->id() != ID_minus &&
95+
it->id() != ID_unary_minus && *it != var)
96+
{
97+
const auto find_qvar = std::find(it->depth_begin(), it->depth_end(), var);
98+
if(find_qvar != it->depth_end())
99+
return false;
100+
else
101+
it.next_sibling_or_parent();
102+
}
103+
else
104+
++it;
105+
}
106+
return true;
107+
}
108+
109+
/// The universally quantified variable is only allowed to occur in index
110+
/// expressions in the body of a string constraint. This function returns true
111+
/// if this is the case and false otherwise.
112+
/// \param [in] expr: The string constraint to check
113+
/// \return true if the universal variable only occurs in index expressions,
114+
/// false otherwise.
115+
static bool universal_only_in_index(const string_constraintt &expr)
116+
{
117+
for(auto it = expr.body.depth_begin(); it != expr.body.depth_end();)
118+
{
119+
if(*it == expr.univ_var)
120+
return false;
121+
if(it->id() == ID_index)
122+
it.next_sibling_or_parent();
123+
else
124+
++it;
125+
}
126+
return true;
127+
}
128+
129+
/// Checks the data invariant for \link string_constraintt \endlink
130+
/// \related string_constraintt
131+
/// \param stream: message stream
132+
/// \param ns: namespace
133+
/// \param [in] constraint: the string constraint to check
134+
/// \return whether the constraint satisfies the invariant
135+
bool is_valid_string_constraint(
136+
messaget::mstreamt &stream,
137+
const namespacet &ns,
138+
const string_constraintt &constraint)
139+
{
140+
const auto eom = messaget::eom;
141+
// Condition 1: The premise cannot contain any string indices
142+
const array_index_mapt premise_indices = gather_indices(constraint.premise);
143+
if(!premise_indices.empty())
144+
{
145+
stream << "Premise has indices: " << to_string(constraint) << ", map: {";
146+
for(const auto &pair : premise_indices)
147+
{
148+
stream << format(pair.first) << ": {";
149+
for(const auto &i : pair.second)
150+
stream << format(i) << ", ";
151+
}
152+
stream << "}}" << eom;
153+
return false;
154+
}
155+
156+
const array_index_mapt body_indices = gather_indices(constraint.body);
157+
// Must validate for each string. Note that we have an invariant that the
158+
// second value in the pair is non-empty.
159+
for(const auto &pair : body_indices)
160+
{
161+
// Condition 2: All indices of the same string must be the of the same form
162+
const exprt rep = pair.second.back();
163+
for(size_t j = 0; j < pair.second.size() - 1; j++)
164+
{
165+
const exprt i = pair.second[j];
166+
const equal_exprt equals(rep, i);
167+
const exprt result = simplify_expr(equals, ns);
168+
if(result.is_false())
169+
{
170+
stream << "Indices not equal: " << to_string(constraint)
171+
<< ", str: " << format(pair.first) << eom;
172+
return false;
173+
}
174+
}
175+
176+
// Condition 3: f must be linear
177+
if(!is_linear_arithmetic_expr(rep, constraint.univ_var))
178+
{
179+
stream << "f is not linear: " << to_string(constraint)
180+
<< ", str: " << format(pair.first) << eom;
181+
return false;
182+
}
183+
184+
// Condition 4: the quantified variable can only occur in indices in the
185+
// body
186+
if(!universal_only_in_index(constraint))
187+
{
188+
stream << "Universal variable outside of index:" << to_string(constraint)
189+
<< eom;
190+
return false;
191+
}
192+
}
193+
194+
return true;
195+
}

src/solvers/refinement/string_constraint.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ exprt univ_within_bounds(const string_constraintt &axiom);
8989
/// \return rendered string
9090
std::string to_string(const string_constraintt &expr);
9191

92+
/// Checks the data invariant for \link string_constraintt
93+
/// \related string_constraintt
94+
/// \param [in] constraint: the string constraint to check
95+
/// \return whether the constraint satisfies the invariant
96+
bool is_valid_string_constraint(
97+
messaget::mstreamt &stream,
98+
const namespacet &ns,
99+
const string_constraintt &constraint);
100+
92101
/// Constraints to encode non containement of strings.
93102
class string_not_contains_constraintt final : public exprt
94103
{

src/solvers/refinement/string_refinement.cpp

Lines changed: 0 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,6 @@ Author: Alberto Griggio, alberto.griggio@gmail.com
2929
#include <solvers/refinement/string_constraint_instantiation.h>
3030
#include <unordered_set>
3131

32-
static bool is_valid_string_constraint(
33-
messaget::mstreamt &stream,
34-
const namespacet &ns,
35-
const string_constraintt &constraint);
36-
3732
static optionalt<exprt> find_counter_example(
3833
const namespacet &ns,
3934
ui_message_handlert::uit ui,
@@ -2327,138 +2322,3 @@ static optionalt<exprt> find_counter_example(
23272322
else
23282323
return { };
23292324
}
2330-
2331-
/// \related string_constraintt
2332-
typedef std::map<exprt, std::vector<exprt>> array_index_mapt;
2333-
2334-
/// \related string_constraintt
2335-
static array_index_mapt gather_indices(const exprt &expr)
2336-
{
2337-
array_index_mapt indices;
2338-
// clang-format off
2339-
std::for_each(
2340-
expr.depth_begin(),
2341-
expr.depth_end(),
2342-
[&](const exprt &expr)
2343-
{
2344-
const auto index_expr = expr_try_dynamic_cast<const index_exprt>(expr);
2345-
if(index_expr)
2346-
indices[index_expr->array()].push_back(index_expr->index());
2347-
});
2348-
// clang-format on
2349-
return indices;
2350-
}
2351-
2352-
/// \param expr: an expression
2353-
/// \param var: a symbol
2354-
/// \return Boolean telling whether `expr` is a linear function of `var`.
2355-
/// \todo Add unit tests
2356-
/// \related string_constraintt
2357-
static bool
2358-
is_linear_arithmetic_expr(const exprt &expr, const symbol_exprt &var)
2359-
{
2360-
for(auto it = expr.depth_begin(); it != expr.depth_end();)
2361-
{
2362-
if(
2363-
it->id() != ID_plus && it->id() != ID_minus &&
2364-
it->id() != ID_unary_minus && *it != var)
2365-
{
2366-
if(find_qvar(*it, var))
2367-
return false;
2368-
else
2369-
it.next_sibling_or_parent();
2370-
}
2371-
else
2372-
++it;
2373-
}
2374-
return true;
2375-
}
2376-
2377-
/// The universally quantified variable is only allowed to occur in index
2378-
/// expressions in the body of a string constraint. This function returns true
2379-
/// if this is the case and false otherwise.
2380-
/// \related string_constraintt
2381-
/// \param [in] expr: The string constraint to check
2382-
/// \return true if the universal variable only occurs in index expressions,
2383-
/// false otherwise.
2384-
static bool universal_only_in_index(const string_constraintt &expr)
2385-
{
2386-
for(auto it = expr.body.depth_begin(); it != expr.body.depth_end();)
2387-
{
2388-
if(*it == expr.univ_var)
2389-
return false;
2390-
if(it->id() == ID_index)
2391-
it.next_sibling_or_parent();
2392-
else
2393-
++it;
2394-
}
2395-
return true;
2396-
}
2397-
2398-
/// Checks the data invariant for \link string_constraintt \endlink
2399-
/// \related string_constraintt
2400-
/// \param stream: message stream
2401-
/// \param ns: namespace
2402-
/// \param [in] constraint: the string constraint to check
2403-
/// \return whether the constraint satisfies the invariant
2404-
static bool is_valid_string_constraint(
2405-
messaget::mstreamt &stream,
2406-
const namespacet &ns,
2407-
const string_constraintt &constraint)
2408-
{
2409-
const auto eom=messaget::eom;
2410-
// Condition 1: The premise cannot contain any string indices
2411-
const array_index_mapt premise_indices = gather_indices(constraint.premise);
2412-
if(!premise_indices.empty())
2413-
{
2414-
stream << "Premise has indices: " << to_string(constraint) << ", map: {";
2415-
for(const auto &pair : premise_indices)
2416-
{
2417-
stream << format(pair.first) << ": {";
2418-
for(const auto &i : pair.second)
2419-
stream << format(i) << ", ";
2420-
}
2421-
stream << "}}" << eom;
2422-
return false;
2423-
}
2424-
2425-
const array_index_mapt body_indices = gather_indices(constraint.body);
2426-
// Must validate for each string. Note that we have an invariant that the
2427-
// second value in the pair is non-empty.
2428-
for(const auto &pair : body_indices)
2429-
{
2430-
// Condition 2: All indices of the same string must be the of the same form
2431-
const exprt rep=pair.second.back();
2432-
for(size_t j=0; j<pair.second.size()-1; j++)
2433-
{
2434-
const exprt i=pair.second[j];
2435-
const equal_exprt equals(rep, i);
2436-
const exprt result=simplify_expr(equals, ns);
2437-
if(result.is_false())
2438-
{
2439-
stream << "Indices not equal: " << to_string(constraint)
2440-
<< ", str: " << format(pair.first) << eom;
2441-
return false;
2442-
}
2443-
}
2444-
2445-
// Condition 3: f must be linear in the quantified variable
2446-
if(!is_linear_arithmetic_expr(rep, constraint.univ_var))
2447-
{
2448-
stream << "f is not linear: " << to_string(constraint)
2449-
<< ", str: " << format(pair.first) << eom;
2450-
return false;
2451-
}
2452-
2453-
// Condition 4: the quantified variable can only occur in indices in the
2454-
// body
2455-
if(!universal_only_in_index(constraint))
2456-
{
2457-
stream << "Universal variable outside of index:" << to_string(constraint)
2458-
<< eom;
2459-
return false;
2460-
}
2461-
}
2462-
2463-
return true;
2464-
}

0 commit comments

Comments
 (0)