CBMC
interval_union.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: Util
4 
5 Author: Diffblue Limited
6 
7 \*******************************************************************/
8 
9 #include "interval_union.h"
10 #include "arith_tools.h"
11 #include "range.h"
12 #include "string_utils.h"
13 #include <regex>
14 #include <util/invariant.h>
15 #include <util/std_expr.h>
16 
18 {
19  return of_interval(intervalt{});
20 }
21 
23 {
24  interval_uniont interval_union{};
26  interval.make_ge_than(value);
27  interval_union.intervals.emplace_back(std::move(interval));
28  return interval_union;
29 }
30 
32 {
33  interval_uniont interval_union{};
35  interval.make_le_than(value);
36  interval_union.intervals.emplace_back(std::move(interval));
37  return interval_union;
38 }
39 
42 {
43  interval_uniont result{};
44  auto it1 = intervals.begin();
45  auto it2 = other.intervals.begin();
46  while(it1 != intervals.end() && it2 != other.intervals.end())
47  {
48  intervalt intersection = *it1;
49  intersection.intersect_with(*it2);
50  if(!intersection.empty())
51  result.intervals.emplace_back(std::move(intersection));
52  if(it1->upper < it2->upper)
53  ++it1;
54  else
55  ++it2;
56  }
57  return result;
58 }
59 
61 {
62  if(intervals.empty())
63  return other;
64  if(other.intervals.empty())
65  return *this;
66 
67  // The algorithm goes process the intervals contained in both unions in order
68  // of their lower bound. At each iteration of the loop, the \c result object
69  // contains the union of intervals from both sets that have lower bound
70  // smaller than the intervals currently pointed by \c it1 and \c it2.
71  interval_uniont result{};
72  auto it1 = intervals.begin();
73  auto it2 = other.intervals.begin();
74 
75  // Initialize the first interval of \c result depending on which of the two
76  // sets are unbounded to the left.
77  if(!it1->lower_set)
78  {
79  if(!it2->lower_set)
80  {
81  result = smaller_or_equal(std::max(it1->upper, it2->upper));
82  ++it1;
83  ++it2;
84  }
85  else
86  {
87  result.intervals.push_back(*it1);
88  ++it1;
89  }
90  }
91  else
92  {
93  if(!it2->lower_set)
94  {
95  result.intervals.push_back(*it2);
96  ++it2;
97  }
98  else if(it1->lower <= it2->lower)
99  {
100  result.intervals.push_back(*it1);
101  ++it1;
102  }
103  else
104  {
105  result.intervals.push_back(*it2);
106  ++it2;
107  }
108  }
109  while(it1 != intervals.end() || it2 != other.intervals.end())
110  {
111  intervalt &back = result.intervals.back();
112  INVARIANT(
113  !back.lower_set ||
114  ((it1 == intervals.end() || it1->lower >= back.lower) &&
115  (it2 == other.intervals.end() || it2->lower >= back.lower)),
116  "intervals should be processed in order");
117  INVARIANT(
118  (it1 == intervals.end() || it1->lower_set) &&
119  (it2 == other.intervals.end() || it2->lower_set),
120  "intervals unbounded to the left should have been processed in prelude");
121  if(it1 != intervals.end() && it1->lower <= back.upper + 1)
122  {
123  back.approx_union_with(*it1);
124  ++it1;
125  }
126  else if(it2 != other.intervals.end() && it2->lower <= back.upper + 1)
127  {
128  back.approx_union_with(*it2);
129  ++it2;
130  }
131  else if(
132  it1 != intervals.end() &&
133  (it2 == other.intervals.end() || it1->lower <= it2->lower))
134  {
135  result.intervals.push_back(*it1);
136  ++it1;
137  }
138  else
139  {
140  result.intervals.push_back(*it2);
141  ++it2;
142  }
143  }
144  return result;
145 }
146 
148 {
149  return intervals.empty();
150 }
151 
152 std::optional<mp_integer> interval_uniont::maximum() const
153 {
154  if(intervals.empty())
155  return {};
156  const intervalt &back = intervals.back();
157  if(back.upper_set)
158  return back.upper;
159  return {};
160 }
161 
162 std::optional<mp_integer> interval_uniont::minimum() const
163 {
164  if(intervals.empty())
165  return {};
166  const intervalt &front = intervals.front();
167  if(front.lower_set)
168  return front.lower;
169  return {};
170 }
171 
172 std::string interval_uniont::to_string() const
173 {
174  if(intervals.empty())
175  return "[]";
176  std::ostringstream output;
177  for(const intervalt &i : intervals)
178  {
179  output << '[';
180  if(i.lower_set)
181  output << i.lower;
182  output << ':';
183  if(i.upper_set)
184  output << i.upper;
185  output << "]";
186  }
187  return output.str();
188 }
189 
191 {
192  return disjunction(make_range(intervals).map([&e](const intervalt &interval) {
193  std::vector<exprt> conjuncts;
194  if(interval.lower_set)
195  {
196  conjuncts.emplace_back(binary_relation_exprt{
197  e, ID_ge, from_integer(interval.lower, e.type())});
198  }
199  if(interval.upper_set)
200  {
201  conjuncts.emplace_back(binary_relation_exprt{
202  e, ID_le, from_integer(interval.upper, e.type())});
203  }
204  return conjunction(conjuncts);
205  }));
206 }
207 
210 {
211  interval_uniont result;
212  result.intervals.emplace_back(std::move(interval));
213  return result;
214 }
215 
216 std::optional<interval_uniont>
217 interval_uniont::of_string(const std::string &to_parse)
218 {
219  const std::regex limits_regex("\\[(-\\d+|\\d*):(-\\d+|\\d*)\\]");
220  interval_uniont result;
221  for(const std::string &interval_string : split_string(to_parse, ','))
222  {
223  std::smatch base_match;
224  if(!std::regex_match(interval_string, base_match, limits_regex))
225  return {};
227  if(!base_match[1].str().empty())
228  interval.make_ge_than(string2integer(base_match[1].str()));
229  if(!base_match[2].str().empty())
230  interval.make_le_than(string2integer(base_match[2].str()));
231  if(!interval.empty())
232  result = result.make_union(of_interval(interval));
233  }
234  INVARIANT(result.validate(), "intervals should be strictly ordered");
235  return result;
236 }
237 
238 std::optional<mp_integer> interval_uniont::as_singleton() const
239 {
240  if(intervals.size() != 1)
241  return {};
242  const intervalt &interval = intervals[0];
243  if(!interval.lower_set || !interval.upper_set)
244  return {};
245  if(interval.upper != interval.lower)
246  return {};
247  return interval.lower;
248 }
249 
251 static bool strictly_below(
254 {
255  return a.upper_set && b.lower_set && a.upper < b.lower;
256 }
257 
259 {
260  return std::is_sorted(intervals.begin(), intervals.end(), strictly_below);
261 }
Base class for all expressions.
Definition: expr.h:56
void approx_union_with(const interval_templatet &i)
Represents a set of integers by a union of intervals, which are stored in increasing order for effici...
interval_uniont make_union(const interval_uniont &other) const
Return a new interval_uniontt object representing the set of intergers in the union of this object an...
static interval_uniont greater_or_equal(const mp_integer &value)
Construct interval_uniont object representing the set of integers that are greater or equal to value.
std::vector< intervalt > intervals
Non-overlapping intervals stored in order of their lower bound, so that each interval is strictly bel...
static interval_uniont smaller_or_equal(const mp_integer &value)
Construct interval_uniont object representing the set of integers that are greater or equal to value.
std::optional< mp_integer > maximum() const
empty optional means either unbounded on the right or empty, is_empty has to be called to distinguish...
std::optional< mp_integer > as_singleton() const
If the set contains only one element, return the value of this element.
bool validate() const
Check that intervals are strictly ordered.
std::optional< mp_integer > minimum() const
empty optional means either unbounded on the left or empty, is_empty has to be called to distinguish ...
bool is_empty() const
static std::optional< interval_uniont > of_string(const std::string &to_parse)
Parse a string which is a comma , separated list of intervals of the form "[lower1:upper1]",...
std::string to_string() const
Convert the set to a string representing a sequence of intervals, each interval being of the form "[l...
static interval_uniont all_integers()
Set of all integers.
static interval_uniont of_interval(intervalt interval)
Construct interval union from a single interval.
interval_uniont make_intersection(const interval_uniont &other) const
Return a new interval_uniontt object representing the set of intergers in the intersection of this ob...
exprt make_contains_expr(const exprt &e) const
Expression which is true exactly when e belongs to the set.
void intersection(const typename graph_nodet< E >::edgest &a, const typename graph_nodet< E >::edgest &b, typename graph_nodet< E >::edgest &dest)
Compute intersection of two edge sets, in linear time.
Definition: graph.h:115
static bool strictly_below(const interval_templatet< mp_integer > &a, const interval_templatet< mp_integer > &b)
Check that interval a is strictly below interval b.
Union of intervals.
const mp_integer string2integer(const std::string &n, unsigned base)
Definition: mp_arith.cpp:54
Ranges: pair of begin and end iterators, which can be initialized from containers,...
ranget< iteratort > make_range(iteratort begin, iteratort end)
Definition: range.h:522
BigInt mp_integer
Definition: smt_terms.h:17
exprt conjunction(const exprt::operandst &op)
1) generates a conjunction for two or more operands 2) for one operand, returns the operand 3) return...
Definition: std_expr.cpp:66
exprt disjunction(const exprt::operandst &op)
1) generates a disjunction for two or more operands 2) for one operand, returns the operand 3) return...
Definition: std_expr.cpp:54
API to expression classes.
void split_string(const std::string &s, char delim, std::vector< std::string > &result, bool strip, bool remove_empty)