PPL  1.2
Generator.cc
Go to the documentation of this file.
1 /* Generator class implementation (non-inline functions).
2  Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it>
3  Copyright (C) 2010-2016 BUGSENG srl (http://bugseng.com)
4 
5 This file is part of the Parma Polyhedra Library (PPL).
6 
7 The PPL is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 The PPL is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software Foundation,
19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA.
20 
21 For the most up-to-date information see the Parma Polyhedra Library
22 site: http://bugseng.com/products/ppl/ . */
23 
24 #include "ppl-config.h"
25 #include "Generator_defs.hh"
26 #include "Variable_defs.hh"
27 #include "Variables_Set_defs.hh"
28 #include "math_utilities_defs.hh"
29 
30 #include <iostream>
31 #include <sstream>
32 #include <stdexcept>
33 
34 namespace PPL = Parma_Polyhedra_Library;
35 
36 void
38  const char* v_name,
39  const Variable v) const {
40  std::ostringstream s;
41  s << "PPL::Generator::" << method << ":" << std::endl
42  << "this->space_dimension() == " << space_dimension() << ", "
43  << v_name << ".space_dimension() == " << v.space_dimension() << ".";
44  throw std::invalid_argument(s.str());
45 }
46 
47 void
49  const char* reason) const {
50  std::ostringstream s;
51  s << "PPL::Generator::" << method << ":" << std::endl
52  << reason << ".";
53  throw std::invalid_argument(s.str());
54 }
55 
58  Coefficient_traits::const_reference d,
59  Representation r) {
60  if (d == 0) {
61  throw std::invalid_argument("PPL::point(e, d):\n"
62  "d == 0.");
63  }
64  Linear_Expression ec(e, r);
67 
68  // If the divisor is negative, we negate it as well as
69  // all the coefficients of the point, because we want to preserve
70  // the invariant: the divisor of a point is strictly positive.
71  if (d < 0) {
72  neg_assign(g.expr);
73  }
74 
75  // Enforce normalization.
76  g.expr.normalize();
77  return g;
78 }
79 
82  Representation r) {
83  return point(e, Coefficient_one(), r);
84 }
85 
88  return point(Linear_Expression::zero(), Coefficient_one(), r);
89 }
90 
93  Coefficient_traits::const_reference d,
94  Representation r) {
95  if (d == 0) {
96  throw std::invalid_argument("PPL::closure_point(e, d):\n"
97  "d == 0.");
98  }
99  Linear_Expression ec(e, r);
101 
103 
104  // If the divisor is negative, we negate it as well as
105  // all the coefficients of the point, because we want to preserve
106  // the invariant: the divisor of a point is strictly positive.
107  if (d < 0) {
108  neg_assign(g.expr);
109  }
110 
111  // Enforce normalization.
112  g.expr.normalize();
113  return g;
114 }
115 
118  Representation r) {
119  return closure_point(e, Coefficient_one(), r);
120 }
121 
124  return closure_point(Linear_Expression::zero(), Coefficient_one(), r);
125 }
126 
129  // The origin of the space cannot be a ray.
131  throw std::invalid_argument("PPL::ray(e):\n"
132  "e == 0, but the origin cannot be a ray.");
133  }
134 
135  Linear_Expression ec(e, r);
138 
139  return g;
140 }
141 
144  // The origin of the space cannot be a line.
146  throw std::invalid_argument("PPL::line(e):\n"
147  "e == 0, but the origin cannot be a line.");
148  }
149 
150  Linear_Expression ec(e, r);
153 
154  return g;
155 }
156 
157 void
159  PPL_ASSERT(v1.space_dimension() <= space_dimension());
160  PPL_ASSERT(v2.space_dimension() <= space_dimension());
161  expr.swap_space_dimensions(v1, v2);
162  // *this is still normalized but it may not be strongly normalized.
163  sign_normalize();
164  PPL_ASSERT(OK());
165 }
166 
167 bool
169  PPL_ASSERT(vars.space_dimension() <= space_dimension());
170  expr.remove_space_dimensions(vars);
171 
172  if (is_line_or_ray() && expr.all_homogeneous_terms_are_zero()) {
173  // Become a point.
174  set_is_ray_or_point();
175  expr.set_inhomogeneous_term(1);
176  if (is_not_necessarily_closed()) {
177  set_epsilon_coefficient(1);
178  }
179 
180  PPL_ASSERT(OK());
181  return false;
182  }
183  else {
184  strong_normalize();
185  PPL_ASSERT(OK());
186  return true;
187  }
188 }
189 
190 void
192 ::permute_space_dimensions(const std::vector<Variable>& cycle) {
193  if (cycle.size() < 2) {
194  // No-op. No need to call sign_normalize().
195  return;
196  }
197 
198  expr.permute_space_dimensions(cycle);
199 
200  // *this is still normalized but may be not strongly normalized: sign
201  // normalization is necessary.
202  sign_normalize();
203  PPL_ASSERT(OK());
204 }
205 
206 void
208  expr.linear_combine(y.expr, i);
209  strong_normalize();
210 }
211 
213 int
214 PPL::compare(const Generator& x, const Generator& y) {
215  const bool x_is_line_or_equality = x.is_line_or_equality();
216  const bool y_is_line_or_equality = y.is_line_or_equality();
217  if (x_is_line_or_equality != y_is_line_or_equality) {
218  // Equalities (lines) precede inequalities (ray/point).
219  return y_is_line_or_equality ? 2 : -2;
220  }
221 
222  return compare(x.expr, y.expr);
223 }
224 
225 bool
227  const Generator& x = *this;
228  const dimension_type x_space_dim = x.space_dimension();
229  if (x_space_dim != y.space_dimension()) {
230  return false;
231  }
232 
233  const Type x_type = x.type();
234  if (x_type != y.type()) {
235  return false;
236  }
237 
238  if (x_type == POINT
239  && !(x.is_necessarily_closed() && y.is_necessarily_closed())) {
240  // Due to the presence of epsilon-coefficients, syntactically
241  // different points may actually encode the same generator.
242  // First, drop the epsilon-coefficient ...
243  Linear_Expression x_expr(x.expression());
244  Linear_Expression y_expr(y.expression());
245  // ... second, re-normalize ...
246  x_expr.normalize();
247  y_expr.normalize();
248  // ... and finally check for syntactic equality.
249  return x_expr.is_equal_to(y_expr);
250  }
251 
252  // Here the epsilon-coefficient, if present, is zero.
253  // It is sufficient to check for syntactic equality.
254  return x.expr.is_equal_to(y.expr);
255 }
256 
257 bool
259  return expr.is_equal_to(y.expr) && kind_ == y.kind_
260  && topology_ == y.topology_;
261 }
262 
263 void
265  if (is_line_or_equality()) {
266  expr.sign_normalize();
267  }
268 }
269 
270 bool
272  Generator tmp = *this;
273  tmp.strong_normalize();
274  return compare(*this, tmp) == 0;
275 }
276 
279 
280 void
282  PPL_ASSERT(zero_dim_point_p == 0);
283  zero_dim_point_p
284  = new Generator(point());
285 
286  PPL_ASSERT(zero_dim_closure_point_p == 0);
287  zero_dim_closure_point_p
288  = new Generator(closure_point());
289 }
290 
291 void
293  PPL_ASSERT(zero_dim_point_p != 0);
294  delete zero_dim_point_p;
295  zero_dim_point_p = 0;
296 
297  PPL_ASSERT(zero_dim_closure_point_p != 0);
298  delete zero_dim_closure_point_p;
299  zero_dim_closure_point_p = 0;
300 }
301 
302 void
303 PPL::Generator::fancy_print(std::ostream& s) const {
304  bool needed_divisor = false;
305  bool extra_parentheses = false;
306  const dimension_type num_variables = space_dimension();
307  const Generator::Type t = type();
308  switch (t) {
309  case Generator::LINE:
310  s << "l(";
311  break;
312  case Generator::RAY:
313  s << "r(";
314  break;
315  case Generator::POINT:
316  s << "p(";
317  goto any_point;
319  s << "c(";
320  any_point:
321  if (expr.inhomogeneous_term() != 1) {
322  needed_divisor = true;
323  if (!expr.all_zeroes(1, num_variables + 1)) {
324  extra_parentheses = true;
325  s << "(";
326  }
327  }
328  break;
329  }
330 
332  bool first = true;
333  for (Linear_Expression::const_iterator i = expr.begin(),
334  i_end = expr.lower_bound(Variable(num_variables)); i != i_end; ++i) {
335  c = *i;
336  if (!first) {
337  if (c > 0) {
338  s << " + ";
339  }
340  else {
341  s << " - ";
342  neg_assign(c);
343  }
344  }
345  else {
346  first = false;
347  }
348  if (c == -1) {
349  s << "-";
350  }
351  else if (c != 1) {
352  s << c << "*";
353  }
354  IO_Operators::operator<<(s, i.variable());
355  }
356  if (first) {
357  // A point or closure point in the origin.
358  s << 0;
359  }
360  if (extra_parentheses) {
361  s << ")";
362  }
363  if (needed_divisor) {
364  s << "/" << expr.inhomogeneous_term();
365  }
366  s << ")";
367 }
368 
370 std::ostream&
371 PPL::IO_Operators::operator<<(std::ostream& s, const Generator& g) {
372  g.fancy_print(s);
373  return s;
374 }
375 
377 std::ostream&
378 PPL::IO_Operators::operator<<(std::ostream& s, const Generator::Type& t) {
379  const char* n = 0;
380  switch (t) {
381  case Generator::LINE:
382  n = "LINE";
383  break;
384  case Generator::RAY:
385  n = "RAY";
386  break;
387  case Generator::POINT:
388  n = "POINT";
389  break;
391  n = "CLOSURE_POINT";
392  break;
393  }
394  s << n;
395  return s;
396 }
397 
398 bool
400  PPL_ASSERT(topology() == p.topology()
401  && space_dimension() == p.space_dimension()
402  && type() == CLOSURE_POINT
403  && p.type() == POINT);
404  const Generator& cp = *this;
405  if (cp.expr.inhomogeneous_term() == p.expr.inhomogeneous_term()) {
406  // Divisors are equal: we can simply compare coefficients
407  // (disregarding the epsilon coefficient).
408  return cp.expr.is_equal_to(p.expr, 1, cp.expr.space_dimension());
409  }
410  else {
411  // Divisors are different: divide them by their GCD
412  // to simplify the following computation.
414  gcd_assign(gcd, cp.expr.inhomogeneous_term(), p.expr.inhomogeneous_term());
415  const bool rel_prime = (gcd == 1);
416  PPL_DIRTY_TEMP_COEFFICIENT(cp_0_scaled);
417  PPL_DIRTY_TEMP_COEFFICIENT(p_0_scaled);
418  if (!rel_prime) {
419  exact_div_assign(cp_0_scaled, cp.expr.inhomogeneous_term(), gcd);
420  exact_div_assign(p_0_scaled, p.expr.inhomogeneous_term(), gcd);
421  }
422  const Coefficient& cp_div = rel_prime ? cp.expr.inhomogeneous_term() : cp_0_scaled;
423  const Coefficient& p_div = rel_prime ? p.expr.inhomogeneous_term() : p_0_scaled;
424  return cp.expr.is_equal_to(p.expr, p_div, cp_div, 1, cp.expr.space_dimension());
425  }
426 }
427 
429 
430 
431 bool
432 PPL::Generator::OK() const {
433  // Topology consistency checks.
434  if (is_not_necessarily_closed() && expr.space_dimension() == 0) {
435 #ifndef NDEBUG
436  std::cerr << "Generator has fewer coefficients than the minimum "
437  << "allowed by its topology."
438  << std::endl;
439 #endif
440  return false;
441  }
442 
443  // Normalization check.
444  Generator tmp = *this;
445  tmp.strong_normalize();
446  if (tmp != *this) {
447 #ifndef NDEBUG
448  std::cerr << "Generators should be strongly normalized!"
449  << std::endl;
450 #endif
451  return false;
452  }
453 
454  switch (type()) {
455  case LINE:
456  // Intentionally fall through.
457  case RAY:
458  if (expr.inhomogeneous_term() != 0) {
459 #ifndef NDEBUG
460  std::cerr << "Lines must have a zero inhomogeneous term!"
461  << std::endl;
462 #endif
463  return false;
464  }
465  if (!is_necessarily_closed() && epsilon_coefficient() != 0) {
466 #ifndef NDEBUG
467  std::cerr << "Lines and rays must have a zero coefficient "
468  << "for the epsilon dimension!"
469  << std::endl;
470 #endif
471  return false;
472  }
473  // The following test is correct, since we already checked
474  // that the epsilon coordinate is zero.
475  if (expr.all_homogeneous_terms_are_zero()) {
476 #ifndef NDEBUG
477  std::cerr << "The origin of the vector space cannot be a line or a ray!"
478  << std::endl;
479 #endif
480  return false;
481  }
482  break;
483 
484  case POINT:
485  if (expr.inhomogeneous_term() <= 0) {
486 #ifndef NDEBUG
487  std::cerr << "Points must have a positive divisor!"
488  << std::endl;
489 #endif
490  return false;
491  }
492  if (!is_necessarily_closed()) {
493  if (epsilon_coefficient() <= 0) {
494 #ifndef NDEBUG
495  std::cerr << "In the NNC topology, points must have epsilon > 0"
496  << std::endl;
497 #endif
498  return false;
499  }
500  }
501  break;
502 
503  case CLOSURE_POINT:
504  if (expr.inhomogeneous_term() <= 0) {
505 #ifndef NDEBUG
506  std::cerr << "Closure points must have a positive divisor!"
507  << std::endl;
508 #endif
509  return false;
510  }
511  break;
512  }
513 
514  // All tests passed.
515  return true;
516 }
void linear_combine(const Generator &y, dimension_type i)
Linearly combines *this with y so that i-th coefficient is 0.
Definition: Generator.cc:207
dimension_type space_dimension() const
Returns the dimension of the smallest vector space enclosing all the variables whose indexes are in t...
size_t dimension_type
An unsigned integral type for representing space dimensions.
An std::set of variables' indexes.
void throw_invalid_argument(const char *method, const char *reason) const
Throw a std::invalid_argument exception containing the appropriate error message. ...
Definition: Generator.cc:48
A line, ray, point or closure point.
Linear_Expression expr
The linear expression encoding *this.
Coefficient_traits::const_reference inhomogeneous_term() const
Returns the inhomogeneous term of *this.
#define PPL_DIRTY_TEMP_COEFFICIENT(id)
Declare a local variable named id, of type Coefficient, and containing an unknown initial value...
static Generator ray(const Linear_Expression &e, Representation r=default_representation)
Returns the ray of direction e.
Definition: Generator.cc:128
bool is_equal_to(const Generator &y) const
Returns true if *this is identical to y.
Definition: Generator.cc:258
std::ostream & operator<<(std::ostream &s, const Ask_Tell< D > &x)
static const Generator * zero_dim_closure_point_p
Holds (between class initialization and finalization) a pointer to the origin of the zero-dimensional...
expr_type expression() const
Partial read access to the (adapted) internal expression.
dimension_type space_dimension() const
Returns the dimension of the vector space enclosing *this.
void fancy_print(std::ostream &s) const
A print function, with fancy, more human-friendly output.
Definition: Generator.cc:303
void set_inhomogeneous_term(Coefficient_traits::const_reference n)
Sets the inhomogeneous term of *this to n.
bool is_equivalent_to(const Generator &y) const
Returns true if and only if *this and y are equivalent generators.
Definition: Generator.cc:226
bool is_matching_closure_point(const Generator &p) const
Returns true if and only if the closure point *this has the same coordinates of the point p...
Definition: Generator.cc:399
void exact_div_assign(Checked_Number< T, Policy > &x, const Checked_Number< T, Policy > &y, const Checked_Number< T, Policy > &z)
static Generator closure_point(const Linear_Expression &e=Linear_Expression::zero(), Coefficient_traits::const_reference d=Coefficient_one(), Representation r=default_representation)
Returns the closure point at e / d.
Definition: Generator.cc:92
bool is_equal_to(const Linear_Expression &x) const
static Generator line(const Linear_Expression &e, Representation r=default_representation)
Returns the line of direction e.
Definition: Generator.cc:143
A dimension of the vector space.
dimension_type space_dimension() const
Returns the dimension of the vector space enclosing *this.
static void initialize()
Initializes the class.
Definition: Generator.cc:281
Type type() const
Returns the generator type of *this.
dimension_type space_dimension() const
Returns the dimension of the vector space enclosing *this.
void sign_normalize()
Normalizes the sign of the coefficients so that the first non-zero (homogeneous) coefficient of a lin...
Definition: Generator.cc:264
static void finalize()
Finalizes the class.
Definition: Generator.cc:292
PPL_COEFFICIENT_TYPE Coefficient
An alias for easily naming the type of PPL coefficients.
bool all_homogeneous_terms_are_zero() const
Returns true if and only if all the homogeneous terms of *this are .
int compare(const Linear_Expression &x, const Linear_Expression &y)
bool remove_space_dimensions(const Variables_Set &vars)
Removes all the specified dimensions from the generator.
Definition: Generator.cc:168
static Generator point(const Linear_Expression &e=Linear_Expression::zero(), Coefficient_traits::const_reference d=Coefficient_one(), Representation r=default_representation)
Returns the point at e / d.
Definition: Generator.cc:57
static const Generator * zero_dim_point_p
Holds (between class initialization and finalization) a pointer to the origin of the zero-dimensional...
bool check_strong_normalized() const
Returns true if and only if the coefficients are strongly normalized.
Definition: Generator.cc:271
void neg_assign(GMP_Integer &x)
void strong_normalize()
Strong normalization: ensures that different Generator objects represent different hyperplanes or hyp...
#define PPL_OUTPUT_DEFINITIONS(class_name)
The entire library is confined to this namespace.
Definition: version.hh:61
Topology topology() const
Returns the topological kind of *this.
void gcd_assign(GMP_Integer &x, const GMP_Integer &y, const GMP_Integer &z)
bool is_necessarily_closed() const
Returns true if and only if the topology of *this row is necessarily closed.
Coefficient c
Definition: PIP_Tree.cc:64
static const Linear_Expression & zero()
Returns the (zero-dimension space) constant 0.
void permute_space_dimensions(const std::vector< Variable > &cycle)
Permutes the space dimensions of the generator.
Definition: Generator.cc:192
Coefficient_traits::const_reference Coefficient_one()
Returns a const reference to a Coefficient with value 1.
bool is_line_or_equality() const
Returns true if and only if *this row represents a line or an equality.
void throw_dimension_incompatible(const char *method, const char *v_name, Variable v) const
Throw a std::invalid_argument exception containing the appropriate error message. ...
Definition: Generator.cc:37
void swap_space_dimensions(Variable v1, Variable v2)
Swaps the coefficients of the variables v1 and v2 .
Definition: Generator.cc:158
Topology topology_
The topology of *this.