|
PPL
0.12.1
|
00001 /* Grid class implementation (non-inline public functions). 00002 Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it> 00003 Copyright (C) 2010-2012 BUGSENG srl (http://bugseng.com) 00004 00005 This file is part of the Parma Polyhedra Library (PPL). 00006 00007 The PPL is free software; you can redistribute it and/or modify it 00008 under the terms of the GNU General Public License as published by the 00009 Free Software Foundation; either version 3 of the License, or (at your 00010 option) any later version. 00011 00012 The PPL is distributed in the hope that it will be useful, but WITHOUT 00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 00015 for more details. 00016 00017 You should have received a copy of the GNU General Public License 00018 along with this program; if not, write to the Free Software Foundation, 00019 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA. 00020 00021 For the most up-to-date information see the Parma Polyhedra Library 00022 site: http://bugseng.com/products/ppl/ . */ 00023 00024 #include "ppl-config.h" 00025 #include "Grid.defs.hh" 00026 #include "Topology.types.hh" 00027 #include "Scalar_Products.defs.hh" 00028 #include "Polyhedron.defs.hh" 00029 #include "assert.hh" 00030 #include <iostream> 00031 00032 namespace PPL = Parma_Polyhedra_Library; 00033 00034 // TODO: In the Grid constructors adapt and use the given system if it 00035 // is modifiable, instead of using a copy. 00036 00037 PPL::Grid::Grid(const Grid& y, Complexity_Class) 00038 : con_sys(), 00039 gen_sys(), 00040 status(y.status), 00041 space_dim(y.space_dim), 00042 dim_kinds(y.dim_kinds) { 00043 if (space_dim == 0) { 00044 con_sys = y.con_sys; 00045 gen_sys = y.gen_sys; 00046 } 00047 else { 00048 if (y.congruences_are_up_to_date()) 00049 con_sys = y.con_sys; 00050 else 00051 con_sys.increase_space_dimension(space_dim); 00052 if (y.generators_are_up_to_date()) 00053 gen_sys = y.gen_sys; 00054 else 00055 gen_sys = Grid_Generator_System(y.space_dim); 00056 } 00057 } 00058 00059 PPL::Grid::Grid(const Constraint_System& cs) 00060 : con_sys(check_space_dimension_overflow(cs.space_dimension(), 00061 max_space_dimension(), 00062 "PPL::Grid::", 00063 "Grid(cs)", 00064 "the space dimension of cs " 00065 "exceeds the maximum allowed " 00066 "space dimension")), 00067 gen_sys(cs.space_dimension()) { 00068 space_dim = cs.space_dimension(); 00069 00070 if (space_dim == 0) { 00071 // See if an inconsistent constraint has been passed. 00072 for (Constraint_System::const_iterator i = cs.begin(), 00073 cs_end = cs.end(); i != cs_end; ++i) 00074 if (i->is_inconsistent()) { 00075 // Inconsistent constraint found: the grid is empty. 00076 status.set_empty(); 00077 // Insert the zero dim false congruence system into `con_sys'. 00078 // `gen_sys' is already in empty form. 00079 con_sys.insert(Congruence::zero_dim_false()); 00080 PPL_ASSERT(OK()); 00081 return; 00082 } 00083 set_zero_dim_univ(); 00084 PPL_ASSERT(OK()); 00085 return; 00086 } 00087 00088 Congruence_System cgs; 00089 cgs.insert(0*Variable(space_dim - 1) %= 1); 00090 for (Constraint_System::const_iterator i = cs.begin(), 00091 cs_end = cs.end(); i != cs_end; ++i) 00092 if (i->is_equality()) 00093 cgs.insert(*i); 00094 else 00095 throw_invalid_constraints("Grid(cs)", "cs"); 00096 construct(cgs); 00097 } 00098 00099 PPL::Grid::Grid(Constraint_System& cs, Recycle_Input) 00100 : con_sys(check_space_dimension_overflow(cs.space_dimension(), 00101 max_space_dimension(), 00102 "PPL::Grid::", 00103 "Grid(cs, recycle)", 00104 "the space dimension of cs " 00105 "exceeds the maximum allowed " 00106 "space dimension")), 00107 gen_sys(cs.space_dimension()) { 00108 space_dim = cs.space_dimension(); 00109 00110 if (space_dim == 0) { 00111 // See if an inconsistent constraint has been passed. 00112 for (Constraint_System::const_iterator i = cs.begin(), 00113 cs_end = cs.end(); i != cs_end; ++i) 00114 if (i->is_inconsistent()) { 00115 // Inconsistent constraint found: the grid is empty. 00116 status.set_empty(); 00117 // Insert the zero dim false congruence system into `con_sys'. 00118 // `gen_sys' is already in empty form. 00119 con_sys.insert(Congruence::zero_dim_false()); 00120 PPL_ASSERT(OK()); 00121 return; 00122 } 00123 set_zero_dim_univ(); 00124 PPL_ASSERT(OK()); 00125 return; 00126 } 00127 00128 Congruence_System cgs; 00129 cgs.insert(0*Variable(space_dim - 1) %= 1); 00130 for (Constraint_System::const_iterator i = cs.begin(), 00131 cs_end = cs.end(); i != cs_end; ++i) 00132 if (i->is_equality()) 00133 cgs.insert(*i); 00134 else 00135 throw_invalid_constraint("Grid(cs)", "cs"); 00136 construct(cgs); 00137 } 00138 00139 PPL::Grid::Grid(const Polyhedron& ph, 00140 Complexity_Class complexity) 00141 : con_sys(check_space_dimension_overflow(ph.space_dimension(), 00142 max_space_dimension(), 00143 "PPL::Grid::", 00144 "Grid(ph)", 00145 "the space dimension of ph " 00146 "exceeds the maximum allowed " 00147 "space dimension")), 00148 gen_sys(ph.space_dimension()) { 00149 space_dim = ph.space_dimension(); 00150 00151 // A zero-dim polyhedron causes no complexity problems. 00152 if (space_dim == 0) { 00153 if (ph.is_empty()) 00154 set_empty(); 00155 else 00156 set_zero_dim_univ(); 00157 return; 00158 } 00159 00160 // A polyhedron known to be empty causes no complexity problems. 00161 if (ph.marked_empty()) { 00162 set_empty(); 00163 return; 00164 } 00165 00166 bool use_constraints = ph.constraints_are_minimized() 00167 || !ph.generators_are_up_to_date(); 00168 00169 // Minimize the constraint description if it is needed and 00170 // the complexity allows it. 00171 if (use_constraints && complexity == ANY_COMPLEXITY) 00172 if (!ph.minimize()) { 00173 set_empty(); 00174 return; 00175 } 00176 00177 if (use_constraints) { 00178 // Only the equality constraints need be used. 00179 PPL_ASSERT(ph.constraints_are_up_to_date()); 00180 const Constraint_System& cs = ph.constraints(); 00181 Congruence_System cgs; 00182 cgs.insert(0*Variable(space_dim - 1) %= 1); 00183 for (Constraint_System::const_iterator i = cs.begin(), 00184 cs_end = cs.end(); i != cs_end; ++i) 00185 if (i->is_equality()) 00186 cgs.insert(*i); 00187 construct(cgs); 00188 } 00189 else { 00190 // First find a point or closure point and convert it to a 00191 // grid point and add to the (initially empty) set of grid generators. 00192 PPL_ASSERT(ph.generators_are_up_to_date()); 00193 const Generator_System& gs = ph.generators(); 00194 Grid_Generator_System ggs(space_dim); 00195 Linear_Expression point_expr; 00196 PPL_DIRTY_TEMP_COEFFICIENT(point_divisor); 00197 for (Generator_System::const_iterator g = gs.begin(), 00198 gs_end = gs.end(); g != gs_end; ++g) { 00199 if (g->is_point() || g->is_closure_point()) { 00200 for (dimension_type i = space_dim; i-- > 0; ) { 00201 const Variable v(i); 00202 point_expr += g->coefficient(v) * v; 00203 point_divisor = g->divisor(); 00204 } 00205 ggs.insert(grid_point(point_expr, point_divisor)); 00206 break; 00207 } 00208 } 00209 // Add grid lines for all the other generators. 00210 // If the polyhedron's generator is a (closure) point, the grid line must 00211 // have the direction given by a line that joins the grid point already 00212 // inserted and the new point. 00213 PPL_DIRTY_TEMP_COEFFICIENT(coeff); 00214 PPL_DIRTY_TEMP_COEFFICIENT(g_divisor); 00215 for (Generator_System::const_iterator g = gs.begin(), 00216 gs_end = gs.end(); g != gs_end; ++g) { 00217 Linear_Expression e; 00218 if (g->is_point() || g->is_closure_point()) { 00219 g_divisor = g->divisor(); 00220 for (dimension_type i = space_dim; i-- > 0; ) { 00221 const Variable v(i); 00222 coeff = point_expr.coefficient(v) * g_divisor; 00223 coeff -= g->coefficient(v) * point_divisor; 00224 e += coeff * v; 00225 } 00226 if (e.all_homogeneous_terms_are_zero()) 00227 continue; 00228 } 00229 else 00230 for (dimension_type i = space_dim; i-- > 0; ) { 00231 const Variable v(i); 00232 e += g->coefficient(v) * v; 00233 } 00234 ggs.insert(grid_line(e)); 00235 } 00236 construct(ggs); 00237 } 00238 PPL_ASSERT(OK()); 00239 } 00240 00241 PPL::Grid& 00242 PPL::Grid::operator=(const Grid& y) { 00243 space_dim = y.space_dim; 00244 dim_kinds = y.dim_kinds; 00245 if (y.marked_empty()) 00246 set_empty(); 00247 else if (space_dim == 0) 00248 set_zero_dim_univ(); 00249 else { 00250 status = y.status; 00251 if (y.congruences_are_up_to_date()) 00252 con_sys = y.con_sys; 00253 if (y.generators_are_up_to_date()) 00254 gen_sys = y.gen_sys; 00255 } 00256 return *this; 00257 } 00258 00259 PPL::dimension_type 00260 PPL::Grid::affine_dimension() const { 00261 if (space_dim == 0 || is_empty()) 00262 return 0; 00263 00264 if (generators_are_up_to_date()) { 00265 if (generators_are_minimized()) 00266 return gen_sys.num_rows() - 1; 00267 if (!(congruences_are_up_to_date() && congruences_are_minimized())) 00268 return minimized_grid_generators().num_rows() - 1; 00269 } 00270 else 00271 minimized_congruences(); 00272 PPL_ASSERT(congruences_are_minimized()); 00273 dimension_type d = space_dim; 00274 for (dimension_type i = con_sys.num_rows(); i-- > 0; ) 00275 if (con_sys[i].is_equality()) 00276 --d; 00277 return d; 00278 } 00279 00280 const PPL::Congruence_System& 00281 PPL::Grid::congruences() const { 00282 if (marked_empty()) 00283 return con_sys; 00284 00285 if (space_dim == 0) { 00286 // Zero-dimensional universe. 00287 PPL_ASSERT(con_sys.num_rows() == 0 && con_sys.num_columns() == 2); 00288 return con_sys; 00289 } 00290 00291 if (!congruences_are_up_to_date()) 00292 update_congruences(); 00293 00294 return con_sys; 00295 } 00296 00297 const PPL::Congruence_System& 00298 PPL::Grid::minimized_congruences() const { 00299 if (congruences_are_up_to_date() && !congruences_are_minimized()) { 00300 // Minimize the congruences. 00301 Grid& gr = const_cast<Grid&>(*this); 00302 if (gr.simplify(gr.con_sys, gr.dim_kinds)) 00303 gr.set_empty(); 00304 else 00305 gr.set_congruences_minimized(); 00306 } 00307 return congruences(); 00308 } 00309 00310 const PPL::Grid_Generator_System& 00311 PPL::Grid::grid_generators() const { 00312 if (space_dim == 0) { 00313 PPL_ASSERT(gen_sys.space_dimension() == 0 00314 && gen_sys.num_rows() == (marked_empty() ? 0U : 1U)); 00315 return gen_sys; 00316 } 00317 00318 if (marked_empty()) { 00319 PPL_ASSERT(gen_sys.has_no_rows()); 00320 return gen_sys; 00321 } 00322 00323 if (!generators_are_up_to_date() && !update_generators()) { 00324 // Updating found the grid empty. 00325 const_cast<Grid&>(*this).set_empty(); 00326 return gen_sys; 00327 } 00328 00329 return gen_sys; 00330 } 00331 00332 const PPL::Grid_Generator_System& 00333 PPL::Grid::minimized_grid_generators() const { 00334 if (space_dim == 0) { 00335 PPL_ASSERT(gen_sys.space_dimension() == 0 00336 && gen_sys.num_rows() == (marked_empty() ? 0U : 1U)); 00337 return gen_sys; 00338 } 00339 00340 if (marked_empty()) { 00341 PPL_ASSERT(gen_sys.has_no_rows()); 00342 return gen_sys; 00343 } 00344 00345 if (generators_are_up_to_date()) { 00346 if (!generators_are_minimized()) { 00347 // Minimize the generators. 00348 Grid& gr = const_cast<Grid&>(*this); 00349 gr.simplify(gr.gen_sys, gr.dim_kinds); 00350 gr.set_generators_minimized(); 00351 } 00352 } 00353 else if (!update_generators()) { 00354 // Updating found the grid empty. 00355 const_cast<Grid&>(*this).set_empty(); 00356 return gen_sys; 00357 } 00358 00359 return gen_sys; 00360 } 00361 00362 PPL::Poly_Con_Relation 00363 PPL::Grid::relation_with(const Congruence& cg) const { 00364 // Dimension-compatibility check. 00365 if (space_dim < cg.space_dimension()) 00366 throw_dimension_incompatible("relation_with(cg)", "cg", cg); 00367 00368 if (marked_empty()) 00369 return Poly_Con_Relation::saturates() 00370 && Poly_Con_Relation::is_included() 00371 && Poly_Con_Relation::is_disjoint(); 00372 00373 if (space_dim == 0) { 00374 if (cg.is_inconsistent()) 00375 return Poly_Con_Relation::is_disjoint(); 00376 else if (cg.is_equality()) 00377 return Poly_Con_Relation::saturates() 00378 && Poly_Con_Relation::is_included(); 00379 else if (cg.inhomogeneous_term() % cg.modulus() == 0) 00380 return Poly_Con_Relation::saturates() 00381 && Poly_Con_Relation::is_included(); 00382 } 00383 00384 if (!generators_are_up_to_date() && !update_generators()) 00385 // Updating found the grid empty. 00386 return Poly_Con_Relation::saturates() 00387 && Poly_Con_Relation::is_included() 00388 && Poly_Con_Relation::is_disjoint(); 00389 00390 // Return one of the relations 00391 // 'strictly_intersects' a strict subset of the grid points satisfy cg 00392 // 'is_included' every grid point satisfies cg 00393 // 'is_disjoint' cg and the grid occupy separate spaces. 00394 00395 // There is always a point. 00396 00397 // Scalar product of the congruence and the first point that 00398 // satisfies the congruence. 00399 PPL_DIRTY_TEMP_COEFFICIENT(point_sp); 00400 point_sp = 0; 00401 00402 PPL_DIRTY_TEMP_COEFFICIENT(div); 00403 div = cg.modulus(); 00404 00405 PPL_DIRTY_TEMP_COEFFICIENT(sp); 00406 00407 bool known_to_intersect = false; 00408 00409 for (Grid_Generator_System::const_iterator i = gen_sys.begin(), 00410 i_end = gen_sys.end(); i != i_end; ++i) { 00411 const Grid_Generator& g = *i; 00412 Scalar_Products::assign(sp, cg, g); 00413 00414 switch (g.type()) { 00415 00416 case Grid_Generator::POINT: 00417 if (cg.is_proper_congruence()) 00418 sp %= div; 00419 if (sp == 0) { 00420 // The point satisfies the congruence. 00421 if (point_sp == 0) 00422 // Any previous points satisfied the congruence. 00423 known_to_intersect = true; 00424 else 00425 return Poly_Con_Relation::strictly_intersects(); 00426 } 00427 else { 00428 if (point_sp == 0) { 00429 if (known_to_intersect) 00430 return Poly_Con_Relation::strictly_intersects(); 00431 // Assign `sp' to `point_sp' as `sp' is the scalar product 00432 // of cg and a point g and is non-zero. 00433 point_sp = sp; 00434 } 00435 else { 00436 // A previously considered point p failed to satisfy cg such that 00437 // `point_sp' = `scalar_prod(p, cg)' 00438 // so, if we consider the parameter g-p instead of g, we have 00439 // scalar_prod(g-p, cg) = scalar_prod(g, cg) - scalar_prod(p, cg) 00440 // = sp - point_sp 00441 sp -= point_sp; 00442 00443 if (sp != 0) { 00444 // Find the GCD between sp and the previous GCD. 00445 gcd_assign(div, div, sp); 00446 if (point_sp % div == 0) 00447 // There is a point in the grid satisfying cg. 00448 return Poly_Con_Relation::strictly_intersects(); 00449 } 00450 } 00451 } 00452 break; 00453 00454 case Grid_Generator::PARAMETER: 00455 if (cg.is_proper_congruence()) 00456 sp %= (div * g.divisor()); 00457 if (sp == 0) 00458 // Parameter g satisfies the cg so the relation depends 00459 // entirely on the other generators. 00460 break; 00461 if (known_to_intersect) 00462 // At least one point satisfies cg. However, the sum of such 00463 // a point and the parameter g fails to satisfy cg (due to g). 00464 return Poly_Con_Relation::strictly_intersects(); 00465 // Find the GCD between sp and the previous GCD. 00466 gcd_assign(div, div, sp); 00467 if (point_sp != 0) { 00468 // At least one of any previously encountered points fails to 00469 // satisfy cg. 00470 if (point_sp % div == 0) 00471 // There is also a grid point that satisfies cg. 00472 return Poly_Con_Relation::strictly_intersects(); 00473 } 00474 break; 00475 00476 case Grid_Generator::LINE: 00477 if (sp == 0) 00478 // Line g satisfies the cg so the relation depends entirely on 00479 // the other generators. 00480 break; 00481 00482 // Line g intersects the congruence. 00483 // 00484 // There is a point p in the grid. Suppose <p*cg> = p_sp. Then 00485 // (-p_sp/sp)*g + p is a point that satisfies cg: <((-p_sp/sp)*g 00486 // + p).cg> = -(p_sp/sp)*sp + p_sp) = 0. If p does not satisfy 00487 // `cg' and hence is not in the grid defined by `cg', the grid 00488 // `*this' strictly intersects the `cg' grid. On the other 00489 // hand, if `p' is in the grid defined by `cg' so that p_sp = 0, 00490 // then <p+g.cg> = p_sp + sp != 0; thus `p+g' is a point in 00491 // *this that does not satisfy `cg' and hence `p+g' is a point 00492 // in *this not in the grid defined by `cg'; therefore `*this' 00493 // strictly intersects the `cg' grid. 00494 return Poly_Con_Relation::strictly_intersects(); 00495 } 00496 } 00497 00498 if (point_sp == 0) { 00499 if (cg.is_equality()) 00500 // Every generator satisfied the cg. 00501 return Poly_Con_Relation::is_included() 00502 && Poly_Con_Relation::saturates(); 00503 else 00504 // Every generator satisfied the cg. 00505 return Poly_Con_Relation::is_included(); 00506 } 00507 00508 PPL_ASSERT(!known_to_intersect); 00509 return Poly_Con_Relation::is_disjoint(); 00510 } 00511 00512 PPL::Poly_Gen_Relation 00513 PPL::Grid::relation_with(const Grid_Generator& g) const { 00514 // Dimension-compatibility check. 00515 if (space_dim < g.space_dimension()) 00516 throw_dimension_incompatible("relation_with(g)", "g", g); 00517 00518 // The empty grid cannot subsume a generator. 00519 if (marked_empty()) 00520 return Poly_Gen_Relation::nothing(); 00521 00522 // A universe grid in a zero-dimensional space subsumes all the 00523 // generators of a zero-dimensional space. 00524 if (space_dim == 0) 00525 return Poly_Gen_Relation::subsumes(); 00526 00527 if (!congruences_are_up_to_date()) 00528 update_congruences(); 00529 00530 return 00531 con_sys.satisfies_all_congruences(g) 00532 ? Poly_Gen_Relation::subsumes() 00533 : Poly_Gen_Relation::nothing(); 00534 } 00535 00536 PPL::Poly_Gen_Relation 00537 PPL::Grid::relation_with(const Generator& g) const { 00538 dimension_type g_space_dim = g.space_dimension(); 00539 00540 // Dimension-compatibility check. 00541 if (space_dim < g_space_dim) 00542 throw_dimension_incompatible("relation_with(g)", "g", g); 00543 00544 // The empty grid cannot subsume a generator. 00545 if (marked_empty()) 00546 return Poly_Gen_Relation::nothing(); 00547 00548 // A universe grid in a zero-dimensional space subsumes all the 00549 // generators of a zero-dimensional space. 00550 if (space_dim == 0) 00551 return Poly_Gen_Relation::subsumes(); 00552 00553 if (!congruences_are_up_to_date()) 00554 update_congruences(); 00555 00556 Linear_Expression expr; 00557 for (dimension_type i = g_space_dim; i-- > 0; ) { 00558 const Variable v(i); 00559 expr += g.coefficient(v) * v; 00560 } 00561 Grid_Generator gg(grid_point()); 00562 if (g.is_point() || g.is_closure_point()) 00563 // Points and closure points are converted to grid points. 00564 gg = grid_point(expr, g.divisor()); 00565 else 00566 // The generator is a ray or line. 00567 // In both cases, we convert it to a grid line 00568 gg = grid_line(expr); 00569 00570 return 00571 con_sys.satisfies_all_congruences(gg) 00572 ? Poly_Gen_Relation::subsumes() 00573 : Poly_Gen_Relation::nothing(); 00574 } 00575 00576 PPL::Poly_Con_Relation 00577 PPL::Grid::relation_with(const Constraint& c) const { 00578 // Dimension-compatibility check. 00579 if (space_dim < c.space_dimension()) 00580 throw_dimension_incompatible("relation_with(c)", "c", c); 00581 00582 if (c.is_equality()) { 00583 Congruence cg(c); 00584 return relation_with(cg); 00585 } 00586 00587 if (marked_empty()) 00588 return Poly_Con_Relation::saturates() 00589 && Poly_Con_Relation::is_included() 00590 && Poly_Con_Relation::is_disjoint(); 00591 00592 if (space_dim == 0) { 00593 if (c.is_inconsistent()) 00594 if (c.is_strict_inequality() && c.inhomogeneous_term() == 0) 00595 // The constraint 0 > 0 implicitly defines the hyperplane 0 = 0; 00596 // thus, the zero-dimensional point also saturates it. 00597 return Poly_Con_Relation::saturates() 00598 && Poly_Con_Relation::is_disjoint(); 00599 else 00600 return Poly_Con_Relation::is_disjoint(); 00601 else if (c.inhomogeneous_term() == 0) 00602 return Poly_Con_Relation::saturates() 00603 && Poly_Con_Relation::is_included(); 00604 else 00605 // The zero-dimensional point saturates 00606 // neither the positivity constraint 1 >= 0, 00607 // nor the strict positivity constraint 1 > 0. 00608 return Poly_Con_Relation::is_included(); 00609 } 00610 00611 if (!generators_are_up_to_date() && !update_generators()) 00612 // Updating found the grid empty. 00613 return Poly_Con_Relation::saturates() 00614 && Poly_Con_Relation::is_included() 00615 && Poly_Con_Relation::is_disjoint(); 00616 00617 // Return one of the relations 00618 // 'strictly_intersects' a strict subset of the grid points satisfy c 00619 // 'is_included' every grid point satisfies c 00620 // 'is_disjoint' c and the grid occupy separate spaces. 00621 00622 // There is always a point. 00623 00624 bool point_is_included = false; 00625 bool point_saturates = false; 00626 const Grid_Generator* first_point = 0; 00627 00628 for (Grid_Generator_System::const_iterator i = gen_sys.begin(), 00629 i_end = gen_sys.end(); i != i_end; ++i) { 00630 const Grid_Generator& g = *i; 00631 switch (g.type()) { 00632 case Grid_Generator::POINT: 00633 { 00634 if (first_point == 0) { 00635 first_point = &g; 00636 const int sign = Scalar_Products::sign(c, g); 00637 if (sign == 0) 00638 point_saturates = !c.is_strict_inequality(); 00639 else if (sign > 0) 00640 point_is_included = !c.is_equality(); 00641 break; 00642 } 00643 // Not the first point: convert `g' to be a parameter 00644 // and fall through into the parameter case. 00645 Grid_Generator& gen = const_cast<Grid_Generator&>(g); 00646 const Grid_Generator& point = *first_point; 00647 const Coefficient& p_div = g[0]; 00648 if (p_div != Coefficient_one()) { 00649 for (dimension_type j = gen.size() - 1; j-- > 1; ) 00650 gen[j] *= p_div; 00651 } 00652 const Coefficient& g_div = gen[0]; 00653 if (g_div == Coefficient_one()) { 00654 for (dimension_type j = gen.size() - 1; j-- > 1; ) 00655 gen[j] -= point[j]; 00656 } 00657 else { 00658 for (dimension_type j = gen.size() - 1; j-- > 1; ) 00659 sub_mul_assign(gen[j], g_div, point[j]); 00660 } 00661 gen[0] *= p_div; 00662 gen.strong_normalize(); 00663 gen.set_is_parameter(); 00664 PPL_ASSERT(gen.OK()); 00665 } 00666 // Intentionally fall through. 00667 00668 case Grid_Generator::PARAMETER: 00669 case Grid_Generator::LINE: 00670 { 00671 const int sign = c.is_strict_inequality() 00672 ? Scalar_Products::reduced_sign(c, g) 00673 : Scalar_Products::sign(c, g); 00674 if (sign != 0) 00675 return Poly_Con_Relation::strictly_intersects(); 00676 } 00677 break; 00678 } // switch 00679 } 00680 00681 // If this program point is reached, then all lines and parameters 00682 // saturate the constraint. Hence, the result is determined by 00683 // the previosly computed relation with the point. 00684 if (point_saturates) 00685 return Poly_Con_Relation::saturates() 00686 && Poly_Con_Relation::is_included(); 00687 00688 if (point_is_included) 00689 return Poly_Con_Relation::is_included(); 00690 00691 return Poly_Con_Relation::is_disjoint(); 00692 } 00693 00694 bool 00695 PPL::Grid::is_empty() const { 00696 if (marked_empty()) 00697 return true; 00698 // Try a fast-fail test: if generators are up-to-date then the 00699 // generator system (since it is well formed) contains a point. 00700 if (generators_are_up_to_date()) 00701 return false; 00702 if (space_dim == 0) 00703 return false; 00704 if (congruences_are_minimized()) 00705 // If the grid was empty it would be marked empty. 00706 return false; 00707 // Minimize the congruences to check if the grid is empty. 00708 Grid& gr = const_cast<Grid&>(*this); 00709 if (gr.simplify(gr.con_sys, gr.dim_kinds)) { 00710 gr.set_empty(); 00711 return true; 00712 } 00713 gr.set_congruences_minimized(); 00714 return false; 00715 } 00716 00717 bool 00718 PPL::Grid::is_universe() const { 00719 if (marked_empty()) 00720 return false; 00721 00722 if (space_dim == 0) 00723 return true; 00724 00725 if (congruences_are_up_to_date()) { 00726 if (congruences_are_minimized()) 00727 // The minimized universe congruence system has only one row, 00728 // the integrality congruence. 00729 return con_sys.num_rows() == 1 && con_sys[0].is_tautological(); 00730 } 00731 else { 00732 update_congruences(); 00733 return con_sys.num_rows() == 1 && con_sys[0].is_tautological(); 00734 } 00735 00736 // Test con_sys's inclusion in a universe generator system. 00737 00738 // The zero dimension cases are handled above. 00739 Variable var(space_dim - 1); 00740 for (dimension_type i = space_dim; i-- > 0; ) 00741 if (!con_sys.satisfies_all_congruences(grid_line(Variable(i) + var))) 00742 return false; 00743 PPL_ASSERT(con_sys.satisfies_all_congruences(grid_point(0*var))); 00744 return true; 00745 } 00746 00747 bool 00748 PPL::Grid::is_bounded() const { 00749 // A zero-dimensional or empty grid is bounded. 00750 if (space_dim == 0 00751 || marked_empty() 00752 || (!generators_are_up_to_date() && !update_generators())) 00753 return true; 00754 00755 // TODO: Consider using con_sys when gen_sys is out of date. 00756 00757 if (gen_sys.num_rows() > 1) { 00758 // Check if all generators are the same point. 00759 const Grid_Generator& first_point = gen_sys[0]; 00760 if (first_point.is_line_or_parameter()) 00761 return false; 00762 for (dimension_type row = gen_sys.num_rows(); row-- > 0; ) { 00763 const Grid_Generator& gen = gen_sys[row]; 00764 if (gen.is_line_or_parameter() || gen != first_point) 00765 return false; 00766 } 00767 } 00768 return true; 00769 } 00770 00771 bool 00772 PPL::Grid::is_discrete() const { 00773 // A zero-dimensional or empty grid is discrete. 00774 if (space_dim == 0 00775 || marked_empty() 00776 || (!generators_are_up_to_date() && !update_generators())) 00777 return true; 00778 00779 // Search for lines in the generator system. 00780 for (dimension_type row = gen_sys.num_rows(); row-- > 1; ) 00781 if (gen_sys[row].is_line()) 00782 return false; 00783 00784 // The system of generators is composed only by 00785 // points and parameters: the grid is discrete. 00786 return true; 00787 } 00788 00789 bool 00790 PPL::Grid::is_topologically_closed() const { 00791 return true; 00792 } 00793 00794 bool 00795 PPL::Grid::contains_integer_point() const { 00796 // Empty grids have no points. 00797 if (marked_empty()) 00798 return false; 00799 00800 // A zero-dimensional, universe grid has, by convention, an 00801 // integer point. 00802 if (space_dim == 0) 00803 return true; 00804 00805 // A grid has an integer point if its intersection with the integer 00806 // grid is non-empty. 00807 Congruence_System cgs; 00808 for (dimension_type var_index = space_dim; var_index-- > 0; ) 00809 cgs.insert(Variable(var_index) %= 0); 00810 00811 Grid gr = *this; 00812 gr.add_recycled_congruences(cgs); 00813 return !gr.is_empty(); 00814 } 00815 00816 bool 00817 PPL::Grid::constrains(const Variable var) const { 00818 // `var' should be one of the dimensions of the grid. 00819 const dimension_type var_space_dim = var.space_dimension(); 00820 if (space_dim < var_space_dim) 00821 throw_dimension_incompatible("constrains(v)", "v", var); 00822 00823 // An empty grid constrains all variables. 00824 if (marked_empty()) 00825 return true; 00826 00827 if (generators_are_up_to_date()) { 00828 // Since generators are up-to-date, the generator system (since it is 00829 // well formed) contains a point. Hence the grid is not empty. 00830 if (congruences_are_up_to_date()) 00831 // Here a variable is constrained if and only if it is 00832 // syntactically constrained. 00833 goto syntactic_check; 00834 00835 if (generators_are_minimized()) { 00836 // Try a quick, incomplete check for the universe grid: 00837 // a universe grid constrains no variable. 00838 // Count the number of lines (they are linearly independent). 00839 dimension_type num_lines = 0; 00840 for (dimension_type i = gen_sys.num_rows(); i-- > 0; ) 00841 if (gen_sys[i].is_line()) 00842 ++num_lines; 00843 00844 if (num_lines == space_dim) 00845 return false; 00846 } 00847 00848 // Scan generators: perhaps we will find line(var). 00849 const dimension_type var_id = var.id(); 00850 for (dimension_type i = gen_sys.num_rows(); i-- > 0; ) { 00851 const Grid_Generator& g_i = gen_sys[i]; 00852 if (g_i.is_line()) { 00853 if (sgn(g_i.coefficient(var)) != 0) { 00854 for (dimension_type j = 0; j < space_dim; ++j) 00855 if (g_i.coefficient(Variable(j)) != 0 && j != var_id) 00856 goto next; 00857 return true; 00858 } 00859 } 00860 next: 00861 ; 00862 } 00863 00864 // We are still here: at least we know that the grid is not empty. 00865 update_congruences(); 00866 goto syntactic_check; 00867 } 00868 00869 // We must minimize to detect emptiness and obtain constraints. 00870 if (!minimize()) 00871 return true; 00872 00873 syntactic_check: 00874 for (dimension_type i = con_sys.num_rows(); i-- > 0; ) 00875 if (con_sys[i].coefficient(var) != 0) 00876 return true; 00877 return false; 00878 } 00879 00880 bool 00881 PPL::Grid::OK(bool check_not_empty) const { 00882 #ifndef NDEBUG 00883 using std::endl; 00884 using std::cerr; 00885 #endif 00886 00887 // Check whether the status information is legal. 00888 if (!status.OK()) 00889 goto fail; 00890 00891 if (marked_empty()) { 00892 if (check_not_empty) { 00893 // The caller does not want the grid to be empty. 00894 #ifndef NDEBUG 00895 cerr << "Empty grid!" << endl; 00896 #endif 00897 goto fail; 00898 } 00899 00900 if (con_sys.space_dimension() != space_dim) { 00901 #ifndef NDEBUG 00902 cerr << "The grid is in a space of dimension " << space_dim 00903 << " while the system of congruences is in a space of dimension " 00904 << con_sys.space_dimension() 00905 << endl; 00906 #endif 00907 goto fail; 00908 } 00909 return true; 00910 } 00911 00912 // A zero-dimensional universe grid is legal only if the system of 00913 // congruences `con_sys' is empty, and the generator system contains 00914 // one point. 00915 if (space_dim == 0) { 00916 if (con_sys.has_no_rows()) 00917 if (gen_sys.num_rows() == 1 && gen_sys[0].is_point()) 00918 return true; 00919 #ifndef NDEBUG 00920 cerr << "Zero-dimensional grid should have an empty congruence" << endl 00921 << "system and a generator system of a single point." << endl; 00922 #endif 00923 goto fail; 00924 } 00925 00926 // A grid is defined by a system of congruences or a system of 00927 // generators. At least one of them must be up to date. 00928 if (!congruences_are_up_to_date() && !generators_are_up_to_date()) { 00929 #ifndef NDEBUG 00930 cerr << "Grid not empty, not zero-dimensional" << endl 00931 << "and with neither congruences nor generators up-to-date!" 00932 << endl; 00933 #endif 00934 goto fail; 00935 } 00936 00937 { 00938 // The expected number of columns in the congruence and generator 00939 // systems, if they are not empty. 00940 const dimension_type num_columns = space_dim + 1; 00941 00942 // Here we check if the size of the matrices is consistent. 00943 // Let us suppose that all the matrices are up-to-date; this means: 00944 // `con_sys' : number of congruences x poly_num_columns 00945 // `gen_sys' : number of generators x poly_num_columns 00946 if (congruences_are_up_to_date()) 00947 if (con_sys.num_columns() != num_columns + 1 /* moduli */) { 00948 #ifndef NDEBUG 00949 cerr << "Incompatible size! (con_sys and space_dim)" 00950 << endl; 00951 #endif 00952 goto fail; 00953 } 00954 00955 if (generators_are_up_to_date()) { 00956 if (gen_sys.space_dimension() + 1 != num_columns) { 00957 #ifndef NDEBUG 00958 cerr << "Incompatible size! (gen_sys and space_dim)" 00959 << endl; 00960 #endif 00961 goto fail; 00962 } 00963 00964 // Check if the system of generators is well-formed. 00965 if (!gen_sys.OK()) 00966 goto fail; 00967 00968 // Check each generator in the system. 00969 for (dimension_type i = gen_sys.num_rows(); i-- > 0; ) { 00970 const Grid_Generator& g = gen_sys[i]; 00971 00972 if (g.size() < 1) { 00973 #ifndef NDEBUG 00974 cerr << "Parameter should have coefficients." << endl; 00975 #endif 00976 goto fail; 00977 } 00978 } 00979 00980 // A non-empty system of generators describing a grid is valid 00981 // if and only if it contains a point. 00982 if (!gen_sys.has_no_rows() && !gen_sys.has_points()) { 00983 #ifndef NDEBUG 00984 cerr << "Non-empty generator system declared up-to-date " 00985 << "has no points!" 00986 << endl; 00987 #endif 00988 goto fail; 00989 } 00990 00991 if (generators_are_minimized()) { 00992 Grid_Generator_System gs = gen_sys; 00993 00994 if (dim_kinds.size() != num_columns) { 00995 #ifndef NDEBUG 00996 cerr << "Size of dim_kinds should equal the number of columns." 00997 << endl; 00998 #endif 00999 goto fail; 01000 } 01001 01002 if (!upper_triangular(gs, dim_kinds)) { 01003 #ifndef NDEBUG 01004 cerr << "Reduced generators should be upper triangular." 01005 << endl; 01006 #endif 01007 goto fail; 01008 } 01009 01010 // Check that dim_kinds corresponds to the row kinds in gen_sys. 01011 for (dimension_type dim = space_dim, 01012 row = gen_sys.num_rows(); dim > 0; --dim) { 01013 if (dim_kinds[dim] == GEN_VIRTUAL) 01014 goto ok; 01015 if (gen_sys[--row].is_parameter_or_point() 01016 && dim_kinds[dim] == PARAMETER) 01017 goto ok; 01018 PPL_ASSERT(gen_sys[row].is_line()); 01019 if (dim_kinds[dim] == LINE) 01020 goto ok; 01021 #ifndef NDEBUG 01022 cerr << "Kinds in dim_kinds should match those in gen_sys." 01023 << endl; 01024 #endif 01025 goto fail; 01026 ok: 01027 PPL_ASSERT(row <= dim); 01028 } 01029 01030 // A reduced generator system must be the same as a temporary 01031 // reduced copy. 01032 Dimension_Kinds dim_kinds_copy = dim_kinds; 01033 // `gs' is minimized and marked_empty returned false, so `gs' 01034 // should contain rows. 01035 PPL_ASSERT(!gs.has_no_rows()); 01036 simplify(gs, dim_kinds_copy); 01037 // gs contained rows before being reduced, so it should 01038 // contain at least a single point afterward. 01039 PPL_ASSERT(!gs.has_no_rows()); 01040 for (dimension_type row = gen_sys.num_rows(); row-- > 0; ) { 01041 Grid_Generator& g = gs[row]; 01042 const Grid_Generator& g_copy = gen_sys[row]; 01043 if (g.is_equal_to(g_copy)) 01044 continue; 01045 #ifndef NDEBUG 01046 cerr << "Generators are declared minimized," 01047 " but they change under reduction.\n" 01048 << "Here is the generator system:\n"; 01049 gen_sys.ascii_dump(cerr); 01050 cerr << "and here is the minimized form of the temporary copy:\n"; 01051 gs.ascii_dump(cerr); 01052 #endif 01053 goto fail; 01054 } 01055 } 01056 01057 } // if (congruences_are_up_to_date()) 01058 } 01059 01060 if (congruences_are_up_to_date()) { 01061 // Check if the system of congruences is well-formed. 01062 if (!con_sys.OK()) 01063 goto fail; 01064 01065 Grid tmp_gr = *this; 01066 // Make a copy here, before changing tmp_gr, to check later. 01067 const Congruence_System cs_copy = tmp_gr.con_sys; 01068 01069 // Clear the generators in tmp_gr. 01070 Grid_Generator_System gs(space_dim); 01071 tmp_gr.gen_sys.m_swap(gs); 01072 tmp_gr.clear_generators_up_to_date(); 01073 01074 if (!tmp_gr.update_generators()) { 01075 if (check_not_empty) { 01076 // Want to know the satisfiability of the congruences. 01077 #ifndef NDEBUG 01078 cerr << "Unsatisfiable system of congruences!" 01079 << endl; 01080 #endif 01081 goto fail; 01082 } 01083 // The grid is empty, all checks are done. 01084 return true; 01085 } 01086 01087 if (congruences_are_minimized()) { 01088 // A reduced congruence system must be lower triangular. 01089 if (!lower_triangular(con_sys, dim_kinds)) { 01090 #ifndef NDEBUG 01091 cerr << "Reduced congruences should be lower triangular." << endl; 01092 #endif 01093 goto fail; 01094 } 01095 01096 // If the congruences are minimized, all the elements in the 01097 // congruence system must be the same as those in the temporary, 01098 // minimized system `cs_copy'. 01099 if (!con_sys.is_equal_to(cs_copy)) { 01100 #ifndef NDEBUG 01101 cerr << "Congruences are declared minimized, but they change under reduction!" 01102 << endl 01103 << "Here is the minimized form of the congruence system:" 01104 << endl; 01105 cs_copy.ascii_dump(cerr); 01106 cerr << endl; 01107 #endif 01108 goto fail; 01109 } 01110 01111 if (dim_kinds.size() != con_sys.num_columns() - 1 /* modulus */) { 01112 #ifndef NDEBUG 01113 cerr << "Size of dim_kinds should equal the number of columns." 01114 << endl; 01115 #endif 01116 goto fail; 01117 } 01118 01119 // Check that dim_kinds corresponds to the row kinds in con_sys. 01120 for (dimension_type dim = space_dim, row = 0; dim > 0; --dim) { 01121 if (dim_kinds[dim] == CON_VIRTUAL) 01122 continue; 01123 if (con_sys[row++].is_proper_congruence() 01124 && dim_kinds[dim] == PROPER_CONGRUENCE) 01125 continue; 01126 PPL_ASSERT(con_sys[row-1].is_equality()); 01127 if (dim_kinds[dim] == EQUALITY) 01128 continue; 01129 #ifndef NDEBUG 01130 cerr << "Kinds in dim_kinds should match those in con_sys." << endl; 01131 #endif 01132 goto fail; 01133 } 01134 } 01135 } 01136 01137 return true; 01138 01139 fail: 01140 #ifndef NDEBUG 01141 cerr << "Here is the grid under check:" << endl; 01142 ascii_dump(cerr); 01143 #endif 01144 return false; 01145 } 01146 01147 void 01148 PPL::Grid::add_constraints(const Constraint_System& cs) { 01149 // The dimension of `cs' must be at most `space_dim'. 01150 if (space_dim < cs.space_dimension()) 01151 throw_dimension_incompatible("add_constraints(cs)", "cs", cs); 01152 if (marked_empty()) 01153 return; 01154 01155 for (Constraint_System::const_iterator i = cs.begin(), 01156 cs_end = cs.end(); i != cs_end; ++i) { 01157 add_constraint_no_check(*i); 01158 if (marked_empty()) 01159 return; 01160 } 01161 } 01162 01163 void 01164 PPL::Grid::add_grid_generator(const Grid_Generator& g) { 01165 // The dimension of `g' must be at most space_dim. 01166 const dimension_type g_space_dim = g.space_dimension(); 01167 if (space_dim < g_space_dim) 01168 throw_dimension_incompatible("add_grid_generator(g)", "g", g); 01169 01170 // Deal with zero-dimension case first. 01171 if (space_dim == 0) { 01172 // Points and parameters are the only zero-dimension generators 01173 // that can be created. 01174 if (marked_empty()) { 01175 if (g.is_parameter()) 01176 throw_invalid_generator("add_grid_generator(g)", "g"); 01177 set_zero_dim_univ(); 01178 } 01179 PPL_ASSERT(OK()); 01180 return; 01181 } 01182 01183 if (marked_empty() 01184 || (!generators_are_up_to_date() && !update_generators())) { 01185 // Here the grid is empty: the specification says we can only 01186 // insert a point. 01187 if (g.is_line_or_parameter()) 01188 throw_invalid_generator("add_grid_generator(g)", "g"); 01189 gen_sys.insert(g); 01190 clear_empty(); 01191 } 01192 else { 01193 PPL_ASSERT(generators_are_up_to_date()); 01194 gen_sys.insert(g); 01195 if (g.is_parameter_or_point()) 01196 normalize_divisors(gen_sys); 01197 } 01198 01199 // With the added generator, congruences are out of date. 01200 clear_congruences_up_to_date(); 01201 01202 clear_generators_minimized(); 01203 set_generators_up_to_date(); 01204 PPL_ASSERT(OK()); 01205 } 01206 01207 void 01208 PPL::Grid::add_recycled_congruences(Congruence_System& cgs) { 01209 // Dimension-compatibility check. 01210 const dimension_type cgs_space_dim = cgs.space_dimension(); 01211 if (space_dim < cgs_space_dim) 01212 throw_dimension_incompatible("add_recycled_congruences(cgs)", "cgs", cgs); 01213 01214 if (cgs.has_no_rows()) 01215 return; 01216 01217 if (marked_empty()) 01218 return; 01219 01220 if (space_dim == 0) { 01221 // In a 0-dimensional space the congruences are trivial (e.g., 0 01222 // == 0 or 1 %= 0) or false (e.g., 1 == 0). In a system of 01223 // congruences `begin()' and `end()' are equal if and only if the 01224 // system contains only trivial congruences. 01225 if (cgs.begin() != cgs.end()) 01226 // There is a congruence, it must be false, the grid becomes empty. 01227 set_empty(); 01228 return; 01229 } 01230 01231 // The congruences are required. 01232 if (!congruences_are_up_to_date()) 01233 update_congruences(); 01234 01235 // Swap (instead of copying) the coefficients of `cgs' (which is 01236 // writable). 01237 con_sys.recycling_insert(cgs); 01238 01239 // Congruences may not be minimized and generators are out of date. 01240 clear_congruences_minimized(); 01241 clear_generators_up_to_date(); 01242 // Note: the congruence system may have become unsatisfiable, thus 01243 // we do not check for satisfiability. 01244 PPL_ASSERT(OK()); 01245 } 01246 01247 void 01248 PPL::Grid::add_recycled_grid_generators(Grid_Generator_System& gs) { 01249 // Dimension-compatibility check. 01250 const dimension_type gs_space_dim = gs.space_dimension(); 01251 if (space_dim < gs_space_dim) 01252 throw_dimension_incompatible("add_recycled_grid_generators(gs)", "gs", gs); 01253 01254 // Adding no generators leaves the grid the same. 01255 if (gs.has_no_rows()) 01256 return; 01257 01258 // Adding valid generators to a zero-dimensional grid transforms it 01259 // to the zero-dimensional universe grid. 01260 if (space_dim == 0) { 01261 if (marked_empty()) 01262 set_zero_dim_univ(); 01263 else { 01264 PPL_ASSERT(gs.has_points()); 01265 } 01266 PPL_ASSERT(OK(true)); 01267 return; 01268 } 01269 01270 if (!marked_empty()) { 01271 // The grid contains at least one point. 01272 01273 if (!generators_are_up_to_date()) 01274 update_generators(); 01275 normalize_divisors(gs, gen_sys); 01276 01277 gen_sys.recycling_insert(gs); 01278 01279 // Congruences are out of date and generators are not minimized. 01280 clear_congruences_up_to_date(); 01281 clear_generators_minimized(); 01282 01283 PPL_ASSERT(OK(true)); 01284 return; 01285 } 01286 01287 // The grid is empty. 01288 01289 // `gs' must contain at least one point. 01290 if (!gs.has_points()) 01291 throw_invalid_generators("add_recycled_grid_generators(gs)", "gs"); 01292 01293 // Adjust `gs' to the right dimension. 01294 gs.insert(parameter(0*Variable(space_dim-1))); 01295 01296 gen_sys.m_swap(gs); 01297 01298 normalize_divisors(gen_sys); 01299 01300 // The grid is no longer empty and generators are up-to-date. 01301 set_generators_up_to_date(); 01302 clear_empty(); 01303 01304 PPL_ASSERT(OK()); 01305 } 01306 01307 void 01308 PPL::Grid::add_grid_generators(const Grid_Generator_System& gs) { 01309 // TODO: this is just an executable specification. 01310 Grid_Generator_System gs_copy = gs; 01311 add_recycled_grid_generators(gs_copy); 01312 } 01313 01314 void 01315 PPL::Grid::refine_with_constraint(const Constraint& c) { 01316 // The dimension of `c' must be at most `space_dim'. 01317 if (space_dim < c.space_dimension()) 01318 throw_dimension_incompatible("refine_with_constraint(c)", "c", c); 01319 if (marked_empty()) 01320 return; 01321 refine_no_check(c); 01322 } 01323 01324 void 01325 PPL::Grid::refine_with_constraints(const Constraint_System& cs) { 01326 // The dimension of `cs' must be at most `space_dim'. 01327 if (space_dim < cs.space_dimension()) 01328 throw_dimension_incompatible("refine_with_constraints(cs)", "cs", cs); 01329 01330 for (Constraint_System::const_iterator i = cs.begin(), 01331 cs_end = cs.end(); !marked_empty() && i != cs_end; ++i) 01332 refine_no_check(*i); 01333 } 01334 01335 void 01336 PPL::Grid::unconstrain(const Variable var) { 01337 // Dimension-compatibility check. 01338 if (space_dim < var.space_dimension()) 01339 throw_dimension_incompatible("unconstrain(var)", var.space_dimension()); 01340 01341 // Do something only if the grid is non-empty. 01342 if (marked_empty() 01343 || (!generators_are_up_to_date() && !update_generators())) 01344 // Empty: do nothing. 01345 return; 01346 01347 PPL_ASSERT(generators_are_up_to_date()); 01348 Grid_Generator l = grid_line(var); 01349 gen_sys.recycling_insert(l); 01350 // With the added generator, congruences are out of date. 01351 clear_congruences_up_to_date(); 01352 clear_generators_minimized(); 01353 PPL_ASSERT(OK()); 01354 } 01355 01356 void 01357 PPL::Grid::unconstrain(const Variables_Set& vars) { 01358 // The cylindrification with respect to no dimensions is a no-op. 01359 // This case also captures the only legal cylindrification 01360 // of a grid in a 0-dim space. 01361 if (vars.empty()) 01362 return; 01363 01364 // Dimension-compatibility check. 01365 const dimension_type min_space_dim = vars.space_dimension(); 01366 if (space_dim < min_space_dim) 01367 throw_dimension_incompatible("unconstrain(vs)", min_space_dim); 01368 01369 // Do something only if the grid is non-empty. 01370 if (marked_empty() 01371 || (!generators_are_up_to_date() && !update_generators())) 01372 // Empty: do nothing. 01373 return; 01374 01375 PPL_ASSERT(generators_are_up_to_date()); 01376 // Since `gen_sys' is not empty, the space dimension of the inserted 01377 // generators are automatically adjusted. 01378 for (Variables_Set::const_iterator vsi = vars.begin(), 01379 vsi_end = vars.end(); vsi != vsi_end; ++vsi) { 01380 Grid_Generator l = grid_line(Variable(*vsi)); 01381 gen_sys.recycling_insert(l); 01382 } 01383 // Constraints are no longer up-to-date. 01384 clear_generators_minimized(); 01385 clear_congruences_up_to_date(); 01386 PPL_ASSERT(OK()); 01387 } 01388 01389 void 01390 PPL::Grid::intersection_assign(const Grid& y) { 01391 Grid& x = *this; 01392 // Dimension-compatibility check. 01393 if (x.space_dim != y.space_dim) 01394 throw_dimension_incompatible("intersection_assign(y)", "y", y); 01395 01396 // If one of the two grids is empty, the intersection is empty. 01397 if (x.marked_empty()) 01398 return; 01399 if (y.marked_empty()) { 01400 x.set_empty(); 01401 return; 01402 } 01403 01404 // If both grids are zero-dimensional, then at this point they are 01405 // necessarily universe, so the intersection is also universe. 01406 if (x.space_dim == 0) 01407 return; 01408 01409 // The congruences must be up-to-date. 01410 if (!x.congruences_are_up_to_date()) 01411 x.update_congruences(); 01412 if (!y.congruences_are_up_to_date()) 01413 y.update_congruences(); 01414 01415 if (!y.con_sys.has_no_rows()) { 01416 x.con_sys.insert(y.con_sys); 01417 // Grid_Generators may be out of date and congruences may have changed 01418 // from minimal form. 01419 x.clear_generators_up_to_date(); 01420 x.clear_congruences_minimized(); 01421 } 01422 01423 PPL_ASSERT(x.OK() && y.OK()); 01424 } 01425 01426 void 01427 PPL::Grid::upper_bound_assign(const Grid& y) { 01428 Grid& x = *this; 01429 // Dimension-compatibility check. 01430 if (x.space_dim != y.space_dim) 01431 throw_dimension_incompatible("upper_bound_assign(y)", "y", y); 01432 01433 // The join of a grid `gr' with an empty grid is `gr'. 01434 if (y.marked_empty()) 01435 return; 01436 if (x.marked_empty()) { 01437 x = y; 01438 return; 01439 } 01440 01441 // If both grids are zero-dimensional, then they are necessarily 01442 // universe grids, and so is their join. 01443 if (x.space_dim == 0) 01444 return; 01445 01446 // The generators must be up-to-date. 01447 if (!x.generators_are_up_to_date() && !x.update_generators()) { 01448 // Discovered `x' empty when updating generators. 01449 x = y; 01450 return; 01451 } 01452 if (!y.generators_are_up_to_date() && !y.update_generators()) 01453 // Discovered `y' empty when updating generators. 01454 return; 01455 01456 // Match the divisors of the x and y generator systems. 01457 Grid_Generator_System gs(y.gen_sys); 01458 normalize_divisors(x.gen_sys, gs); 01459 x.gen_sys.recycling_insert(gs); 01460 // Congruences may be out of date and generators may have lost 01461 // minimal form. 01462 x.clear_congruences_up_to_date(); 01463 x.clear_generators_minimized(); 01464 01465 // At this point both `x' and `y' are not empty. 01466 PPL_ASSERT(x.OK(true) && y.OK(true)); 01467 } 01468 01469 bool 01470 PPL::Grid::upper_bound_assign_if_exact(const Grid& y) { 01471 Grid& x = *this; 01472 01473 // Dimension-compatibility check. 01474 if (x.space_dim != y.space_dim) 01475 throw_dimension_incompatible("upper_bound_assign_if_exact(y)", "y", y); 01476 01477 if (x.marked_empty() 01478 || y.marked_empty() 01479 || x.space_dim == 0 01480 || x.is_included_in(y) 01481 || y.is_included_in(x)) { 01482 upper_bound_assign(y); 01483 return true; 01484 } 01485 01486 // The above test 'x.is_included_in(y)' will ensure the generators of x 01487 // are up to date. 01488 PPL_ASSERT(generators_are_up_to_date()); 01489 01490 Grid x_copy = x; 01491 x_copy.upper_bound_assign(y); 01492 x_copy.difference_assign(y); 01493 if (x_copy.is_included_in(x)) { 01494 upper_bound_assign(y); 01495 return true; 01496 } 01497 01498 return false; 01499 } 01500 01501 void 01502 PPL::Grid::difference_assign(const Grid& y) { 01503 Grid& x = *this; 01504 // Dimension-compatibility check. 01505 if (x.space_dim != y.space_dim) 01506 throw_dimension_incompatible("difference_assign(y)", "y", y); 01507 01508 if (y.marked_empty() || x.marked_empty()) 01509 return; 01510 01511 // If both grids are zero-dimensional, then they are necessarily 01512 // universe grids, so the result is empty. 01513 if (x.space_dim == 0) { 01514 x.set_empty(); 01515 return; 01516 } 01517 01518 if (y.contains(x)) { 01519 x.set_empty(); 01520 return; 01521 } 01522 01523 Grid new_grid(x.space_dim, EMPTY); 01524 01525 const Congruence_System& y_cgs = y.congruences(); 01526 for (Congruence_System::const_iterator i = y_cgs.begin(), 01527 y_cgs_end = y_cgs.end(); i != y_cgs_end; ++i) { 01528 const Congruence& cg = *i; 01529 01530 // The 2-complement cg2 of cg = ((e %= 0) / m) is the congruence 01531 // defining the sets of points exactly half-way between successive 01532 // hyperplanes e = km and e = (k+1)m, for any integer k; that is, 01533 // the hyperplanes defined by 2e = (2k + 1)m, for any integer k. 01534 // Thus `cg2' is the congruence ((2e %= m) / 2m). 01535 01536 // As the grid difference must be a grid, only add the 01537 // 2-complement congruence to x if the resulting grid includes all 01538 // the points in x that did not satisfy `cg'. 01539 01540 // The 2-complement of cg can be included in the result only if x 01541 // holds points other than those in cg. 01542 if (x.relation_with(cg).implies(Poly_Con_Relation::is_included())) 01543 continue; 01544 01545 if (cg.is_proper_congruence()) { 01546 const Linear_Expression e = Linear_Expression(cg); 01547 // Congruence cg is ((e %= 0) / m). 01548 const Coefficient& m = cg.modulus(); 01549 // If x is included in the grid defined by the congruences cg 01550 // and its 2-complement (i.e. the grid defined by the congruence 01551 // (2e %= 0) / m) then add the 2-complement to the potential 01552 // result. 01553 if (x.relation_with((2*e %= 0) / m) 01554 .implies(Poly_Con_Relation::is_included())) { 01555 Grid z = x; 01556 z.add_congruence_no_check((2*e %= m) / (2*m)); 01557 new_grid.upper_bound_assign(z); 01558 continue; 01559 } 01560 } 01561 return; 01562 } 01563 01564 *this = new_grid; 01565 01566 PPL_ASSERT(OK()); 01567 } 01568 01569 namespace { 01570 01571 struct Ruled_Out_Pair { 01572 PPL::dimension_type congruence_index; 01573 PPL::dimension_type num_ruled_out; 01574 }; 01575 01576 struct Ruled_Out_Less_Than { 01577 bool operator()(const Ruled_Out_Pair& x, 01578 const Ruled_Out_Pair& y) const { 01579 return x.num_ruled_out > y.num_ruled_out; 01580 } 01581 }; 01582 01583 } // namespace 01584 01585 bool 01586 PPL::Grid::simplify_using_context_assign(const Grid& y) { 01587 Grid& x = *this; 01588 // Dimension-compatibility check. 01589 if (x.space_dim != y.space_dim) 01590 throw_dimension_incompatible("simplify_using_context_assign(y)", "y", y); 01591 01592 // Filter away the zero-dimensional case. 01593 if (x.space_dim == 0) { 01594 if (y.is_empty()) { 01595 set_zero_dim_univ(); 01596 PPL_ASSERT(OK()); 01597 return false; 01598 } 01599 else 01600 return !x.is_empty(); 01601 } 01602 01603 // If `y' is empty, the biggest enlargement for `x' is the universe. 01604 if (!y.minimize()) { 01605 Grid gr(x.space_dim, UNIVERSE); 01606 m_swap(gr); 01607 return false; 01608 } 01609 01610 // If `x' is empty, the intersection is empty. 01611 if (!x.minimize()) { 01612 // Search for a congruence of `y' that is not a tautology. 01613 PPL_ASSERT(y.congruences_are_up_to_date()); 01614 Grid gr(x.space_dim, UNIVERSE); 01615 for (dimension_type i = y.con_sys.num_rows(); i-- > 0; ) { 01616 const Congruence& y_con_sys_i = y.con_sys[i]; 01617 if (!y_con_sys_i.is_tautological()) { 01618 // Found: we obtain a congruence `c' contradicting the one we 01619 // found, and assign to `x' the grid `gr' with `c' as 01620 // the only congruence. 01621 const Linear_Expression le(y_con_sys_i); 01622 if (y_con_sys_i.is_equality()) { 01623 gr.refine_no_check(le == 1); 01624 break; 01625 } 01626 else { 01627 const Coefficient& y_modulus_i = y_con_sys_i.modulus(); 01628 if (y_modulus_i > 1) 01629 gr.refine_no_check(le == 1); 01630 else { 01631 Linear_Expression le2; 01632 for (dimension_type j = le.space_dimension(); j-- > 0; ) 01633 le2 += 2 * le.coefficient(Variable(j)) * Variable(j); 01634 le2 += 2 * le.inhomogeneous_term(); 01635 gr.refine_no_check(le2 == y_modulus_i); 01636 } 01637 break; 01638 } 01639 } 01640 } 01641 m_swap(gr); 01642 PPL_ASSERT(OK()); 01643 return false; 01644 } 01645 01646 PPL_ASSERT(x.congruences_are_minimized() 01647 && y.generators_are_minimized()); 01648 01649 const Congruence_System& x_cs = x.con_sys; 01650 const dimension_type x_cs_num_rows = x_cs.num_rows(); 01651 01652 // Record into `redundant_by_y' the info about which congruences of 01653 // `x' are redundant in the context `y'. Count the number of 01654 // redundancies found. 01655 std::vector<bool> redundant_by_y(x_cs_num_rows, false); 01656 dimension_type num_redundant_by_y = 0; 01657 for (dimension_type i = 0; i < x_cs_num_rows; ++i) { 01658 if (y.relation_with(x_cs[i]).implies(Poly_Con_Relation::is_included())) { 01659 redundant_by_y[i] = true; 01660 ++num_redundant_by_y; 01661 } 01662 } 01663 01664 if (num_redundant_by_y < x_cs_num_rows) { 01665 01666 // Some congruences were not identified as redundant. 01667 01668 Congruence_System result_cs; 01669 const Congruence_System& y_cs = y.con_sys; 01670 const dimension_type y_cs_num_rows = y_cs.num_rows(); 01671 // Compute into `z' the intersection of `x' and `y'. 01672 const bool x_first = (x_cs_num_rows > y_cs_num_rows); 01673 Grid z(x_first ? x : y); 01674 if (x_first) 01675 z.add_congruences(y_cs); 01676 else { 01677 // Only copy (and then recycle) the non-redundant congruences. 01678 Congruence_System tmp_cs; 01679 for (dimension_type i = 0; i < x_cs_num_rows; ++i) { 01680 if (!redundant_by_y[i]) 01681 tmp_cs.insert(x_cs[i]); 01682 } 01683 z.add_recycled_congruences(tmp_cs); 01684 } 01685 01686 // Congruences are added to `w' until it equals `z'. 01687 // We do not care about minimization or maximization, since 01688 // we are only interested in satisfiability. 01689 Grid w; 01690 w.add_space_dimensions_and_embed(x.space_dim); 01691 // First add the congruences for `y'. 01692 w.add_congruences(y_cs); 01693 01694 // We apply the following heuristics here: congruences of `x' that 01695 // are not made redundant by `y' are added to `w' depending on 01696 // the number of generators of `y' they rule out (the more generators) 01697 // (they rule out, the sooner they are added). Of course, as soon 01698 // as `w' becomes empty, we stop adding. 01699 std::vector<Ruled_Out_Pair> 01700 ruled_out_vec(x_cs_num_rows - num_redundant_by_y); 01701 01702 PPL_DIRTY_TEMP_COEFFICIENT(sp); 01703 PPL_DIRTY_TEMP_COEFFICIENT(div); 01704 01705 for (dimension_type i = 0, j = 0; i < x_cs_num_rows; ++i) { 01706 if (!redundant_by_y[i]) { 01707 const Congruence& c = x_cs[i]; 01708 const Coefficient& modulus = c.modulus(); 01709 div = modulus; 01710 01711 const Grid_Generator_System& y_gs = y.gen_sys; 01712 dimension_type num_ruled_out_generators = 0; 01713 for (Grid_Generator_System::const_iterator k = y_gs.begin(), 01714 y_gs_end = y_gs.end(); k != y_gs_end; ++k) { 01715 const Grid_Generator& g = *k; 01716 // If the generator is not to be ruled out, 01717 // it must saturate the congruence. 01718 Scalar_Products::assign(sp, c, g); 01719 // If `c' is a proper congruence the scalar product must be 01720 // reduced modulo a (possibly scaled) modulus. 01721 if (c.is_proper_congruence()) { 01722 // If `g' is a parameter the congruence modulus must be scaled 01723 // up by the divisor of the generator. 01724 if (g.is_parameter()) 01725 sp %= (div * g.divisor()); 01726 else 01727 if (g.is_point()) 01728 sp %= div; 01729 } 01730 if (sp == 0) 01731 continue; 01732 ++num_ruled_out_generators; 01733 } 01734 ruled_out_vec[j].congruence_index = i; 01735 ruled_out_vec[j].num_ruled_out = num_ruled_out_generators; 01736 ++j; 01737 } 01738 } 01739 std::sort(ruled_out_vec.begin(), ruled_out_vec.end(), 01740 Ruled_Out_Less_Than()); 01741 01742 bool empty_intersection = (!z.minimize()); 01743 01744 // Add the congruences in the "ruled out" order to `w' 01745 // until the result is the intersection. 01746 for (std::vector<Ruled_Out_Pair>::const_iterator 01747 j = ruled_out_vec.begin(), ruled_out_vec_end = ruled_out_vec.end(); 01748 j != ruled_out_vec_end; 01749 ++j) { 01750 const Congruence& c = x_cs[j->congruence_index]; 01751 result_cs.insert(c); 01752 w.add_congruence(c); 01753 if ((empty_intersection && w.is_empty()) 01754 || (!empty_intersection && w.is_included_in(z))) { 01755 Grid result_gr(x.space_dim, UNIVERSE); 01756 result_gr.add_congruences(result_cs); 01757 x.m_swap(result_gr); 01758 PPL_ASSERT(x.OK()); 01759 return !empty_intersection; 01760 } 01761 } 01762 // Cannot exit from here. 01763 PPL_UNREACHABLE; 01764 } 01765 01766 // All the congruences are redundant so that the simplified grid 01767 // is the universe. 01768 Grid result_gr(x.space_dim, UNIVERSE); 01769 x.m_swap(result_gr); 01770 PPL_ASSERT(x.OK()); 01771 return true; 01772 } 01773 01774 void 01775 PPL::Grid::affine_image(const Variable var, 01776 const Linear_Expression& expr, 01777 Coefficient_traits::const_reference denominator) { 01778 // The denominator cannot be zero. 01779 if (denominator == 0) 01780 throw_invalid_argument("affine_image(v, e, d)", "d == 0"); 01781 01782 // Dimension-compatibility checks. 01783 // The dimension of `expr' must be at most the dimension of `*this'. 01784 const dimension_type expr_space_dim = expr.space_dimension(); 01785 if (space_dim < expr_space_dim) 01786 throw_dimension_incompatible("affine_image(v, e, d)", "e", expr); 01787 // `var' must be one of the dimensions of the grid. 01788 const dimension_type var_space_dim = var.space_dimension(); 01789 if (space_dim < var_space_dim) 01790 throw_dimension_incompatible("affine_image(v, e, d)", "v", var); 01791 01792 if (marked_empty()) 01793 return; 01794 01795 if (var_space_dim <= expr_space_dim && expr[var_space_dim] != 0) { 01796 // The transformation is invertible. 01797 if (generators_are_up_to_date()) { 01798 // Grid_Generator_System::affine_image() requires the third argument 01799 // to be a positive Coefficient. 01800 if (denominator > 0) 01801 gen_sys.affine_image(var_space_dim, expr, denominator); 01802 else 01803 gen_sys.affine_image(var_space_dim, -expr, -denominator); 01804 clear_generators_minimized(); 01805 // Strong normalization in gs::affine_image may have modified 01806 // divisors. 01807 normalize_divisors(gen_sys); 01808 } 01809 if (congruences_are_up_to_date()) { 01810 // To build the inverse transformation, 01811 // after copying and negating `expr', 01812 // we exchange the roles of `expr[var_space_dim]' and `denominator'. 01813 Linear_Expression inverse; 01814 if (expr[var_space_dim] > 0) { 01815 inverse = -expr; 01816 inverse[var_space_dim] = denominator; 01817 con_sys.affine_preimage(var_space_dim, inverse, expr[var_space_dim]); 01818 } 01819 else { 01820 // The new denominator is negative: we negate everything once 01821 // more, as Congruence_System::affine_preimage() requires the 01822 // third argument to be positive. 01823 inverse = expr; 01824 inverse[var_space_dim] = denominator; 01825 neg_assign(inverse[var_space_dim]); 01826 con_sys.affine_preimage(var_space_dim, inverse, -expr[var_space_dim]); 01827 } 01828 clear_congruences_minimized(); 01829 } 01830 } 01831 else { 01832 // The transformation is not invertible. 01833 // We need an up-to-date system of generators. 01834 if (!generators_are_up_to_date()) 01835 minimize(); 01836 if (!marked_empty()) { 01837 // Grid_Generator_System::affine_image() requires the third argument 01838 // to be a positive Coefficient. 01839 if (denominator > 0) 01840 gen_sys.affine_image(var_space_dim, expr, denominator); 01841 else 01842 gen_sys.affine_image(var_space_dim, -expr, -denominator); 01843 01844 clear_congruences_up_to_date(); 01845 clear_generators_minimized(); 01846 // Strong normalization in gs::affine_image may have modified 01847 // divisors. 01848 normalize_divisors(gen_sys); 01849 } 01850 } 01851 PPL_ASSERT(OK()); 01852 } 01853 01854 void 01855 PPL::Grid:: 01856 affine_preimage(const Variable var, 01857 const Linear_Expression& expr, 01858 Coefficient_traits::const_reference denominator) { 01859 // The denominator cannot be zero. 01860 if (denominator == 0) 01861 throw_invalid_argument("affine_preimage(v, e, d)", "d == 0"); 01862 01863 // Dimension-compatibility checks. 01864 // The dimension of `expr' should not be greater than the dimension 01865 // of `*this'. 01866 const dimension_type expr_space_dim = expr.space_dimension(); 01867 if (space_dim < expr_space_dim) 01868 throw_dimension_incompatible("affine_preimage(v, e, d)", "e", expr); 01869 // `var' should be one of the dimensions of the grid. 01870 const dimension_type var_space_dim = var.space_dimension(); 01871 if (space_dim < var_space_dim) 01872 throw_dimension_incompatible("affine_preimage(v, e, d)", "v", var); 01873 01874 if (marked_empty()) 01875 return; 01876 01877 if (var_space_dim <= expr_space_dim && expr[var_space_dim] != 0) { 01878 // The transformation is invertible. 01879 if (congruences_are_up_to_date()) { 01880 // Congruence_System::affine_preimage() requires the third argument 01881 // to be a positive Coefficient. 01882 if (denominator > 0) 01883 con_sys.affine_preimage(var_space_dim, expr, denominator); 01884 else 01885 con_sys.affine_preimage(var_space_dim, -expr, -denominator); 01886 clear_congruences_minimized(); 01887 } 01888 if (generators_are_up_to_date()) { 01889 // To build the inverse transformation, 01890 // after copying and negating `expr', 01891 // we exchange the roles of `expr[var_space_dim]' and `denominator'. 01892 Linear_Expression inverse; 01893 if (expr[var_space_dim] > 0) { 01894 inverse = -expr; 01895 inverse[var_space_dim] = denominator; 01896 gen_sys.affine_image(var_space_dim, inverse, expr[var_space_dim]); 01897 } 01898 else { 01899 // The new denominator is negative: we negate everything once 01900 // more, as Grid_Generator_System::affine_image() requires the 01901 // third argument to be positive. 01902 inverse = expr; 01903 inverse[var_space_dim] = denominator; 01904 neg_assign(inverse[var_space_dim]); 01905 gen_sys.affine_image(var_space_dim, inverse, -expr[var_space_dim]); 01906 } 01907 clear_generators_minimized(); 01908 } 01909 } 01910 else { 01911 // The transformation is not invertible. 01912 // We need an up-to-date system of congruences. 01913 if (!congruences_are_up_to_date()) 01914 minimize(); 01915 // Congruence_System::affine_preimage() requires the third argument 01916 // to be a positive Coefficient. 01917 if (denominator > 0) 01918 con_sys.affine_preimage(var_space_dim, expr, denominator); 01919 else 01920 con_sys.affine_preimage(var_space_dim, -expr, -denominator); 01921 01922 clear_generators_up_to_date(); 01923 clear_congruences_minimized(); 01924 } 01925 PPL_ASSERT(OK()); 01926 } 01927 01928 void 01929 PPL::Grid:: 01930 generalized_affine_image(const Variable var, 01931 const Relation_Symbol relsym, 01932 const Linear_Expression& expr, 01933 Coefficient_traits::const_reference denominator, 01934 Coefficient_traits::const_reference modulus) { 01935 // The denominator cannot be zero. 01936 if (denominator == 0) 01937 throw_invalid_argument("generalized_affine_image(v, r, e, d, m)", 01938 "d == 0"); 01939 01940 // Dimension-compatibility checks. 01941 // The dimension of `expr' should not be greater than the dimension 01942 // of `*this'. 01943 const dimension_type expr_space_dim = expr.space_dimension(); 01944 if (space_dim < expr_space_dim) 01945 throw_dimension_incompatible("generalized_affine_image(v, r, e, d, m)", 01946 "e", expr); 01947 // `var' should be one of the dimensions of the grid. 01948 const dimension_type var_space_dim = var.space_dimension(); 01949 if (space_dim < var_space_dim) 01950 throw_dimension_incompatible("generalized_affine_image(v, r, e, d, m)", 01951 "v", var); 01952 // The relation symbol cannot be a disequality. 01953 if (relsym == NOT_EQUAL) 01954 throw_invalid_argument("generalized_affine_image(v, r, e, d, m)", 01955 "r is the disequality relation symbol"); 01956 01957 // Any image of an empty grid is empty. 01958 if (marked_empty()) 01959 return; 01960 01961 // If relsym is not EQUAL, then we return a safe approximation 01962 // by adding a line in the direction of var. 01963 if (relsym != EQUAL) { 01964 01965 if (modulus != 0) 01966 throw_invalid_argument("generalized_affine_image(v, r, e, d, m)", 01967 "r != EQUAL && m != 0"); 01968 01969 if (!generators_are_up_to_date()) 01970 minimize(); 01971 01972 // Any image of an empty grid is empty. 01973 if (marked_empty()) 01974 return; 01975 01976 add_grid_generator(grid_line(var)); 01977 01978 PPL_ASSERT(OK()); 01979 return; 01980 } 01981 01982 PPL_ASSERT(relsym == EQUAL); 01983 01984 affine_image(var, expr, denominator); 01985 01986 if (modulus == 0) 01987 return; 01988 01989 // Modulate dimension `var' according to `modulus'. 01990 01991 if (!generators_are_up_to_date()) 01992 minimize(); 01993 01994 // Test if minimization, possibly in affine_image, found an empty 01995 // grid. 01996 if (marked_empty()) 01997 return; 01998 01999 if (modulus < 0) 02000 gen_sys.insert(parameter(-modulus * var)); 02001 else 02002 gen_sys.insert(parameter(modulus * var)); 02003 02004 normalize_divisors(gen_sys); 02005 02006 clear_generators_minimized(); 02007 clear_congruences_up_to_date(); 02008 02009 PPL_ASSERT(OK()); 02010 } 02011 02012 void 02013 PPL::Grid:: 02014 generalized_affine_preimage(const Variable var, 02015 const Relation_Symbol relsym, 02016 const Linear_Expression& expr, 02017 Coefficient_traits::const_reference denominator, 02018 Coefficient_traits::const_reference modulus) { 02019 // The denominator cannot be zero. 02020 if (denominator == 0) 02021 throw_invalid_argument("generalized_affine_preimage(v, r, e, d, m)", 02022 "d == 0"); 02023 02024 // The dimension of `expr' should be at most the dimension of 02025 // `*this'. 02026 const dimension_type expr_space_dim = expr.space_dimension(); 02027 if (space_dim < expr_space_dim) 02028 throw_dimension_incompatible("generalized_affine_preimage(v, r, e, d, m)", 02029 "e", expr); 02030 // `var' should be one of the dimensions of the grid. 02031 const dimension_type var_space_dim = var.space_dimension(); 02032 if (space_dim < var_space_dim) 02033 throw_dimension_incompatible("generalized_affine_preimage(v, r, e, d, m)", 02034 "v", var); 02035 // The relation symbol cannot be a disequality. 02036 if (relsym == NOT_EQUAL) 02037 throw_invalid_argument("generalized_affine_preimage(v, r, e, d, m)", 02038 "r is the disequality relation symbol"); 02039 02040 // If relsym is not EQUAL, then we return a safe approximation 02041 // by adding a line in the direction of var. 02042 if (relsym != EQUAL) { 02043 02044 if (modulus != 0) 02045 throw_invalid_argument("generalized_affine_preimage(v, r, e, d, m)", 02046 "r != EQUAL && m != 0"); 02047 02048 if (!generators_are_up_to_date()) 02049 minimize(); 02050 02051 // Any image of an empty grid is empty. 02052 if (marked_empty()) 02053 return; 02054 02055 add_grid_generator(grid_line(var)); 02056 02057 PPL_ASSERT(OK()); 02058 return; 02059 } 02060 02061 PPL_ASSERT(relsym == EQUAL); 02062 // Any image of an empty grid is empty. 02063 if (marked_empty()) 02064 return; 02065 02066 // Check whether the affine relation is an affine function. 02067 if (modulus == 0) { 02068 affine_preimage(var, expr, denominator); 02069 return; 02070 } 02071 02072 // Check whether the preimage of this affine relation can be easily 02073 // computed as the image of its inverse relation. 02074 const Coefficient& var_coefficient = expr.coefficient(var); 02075 if (var_space_dim <= expr_space_dim && var_coefficient != 0) { 02076 Linear_Expression inverse_expr 02077 = expr - (denominator + var_coefficient) * var; 02078 PPL_DIRTY_TEMP_COEFFICIENT(inverse_denominator); 02079 neg_assign(inverse_denominator, var_coefficient); 02080 if (modulus < 0) 02081 generalized_affine_image(var, EQUAL, inverse_expr, inverse_denominator, 02082 - modulus); 02083 else 02084 generalized_affine_image(var, EQUAL, inverse_expr, inverse_denominator, 02085 modulus); 02086 return; 02087 } 02088 02089 // Here `var_coefficient == 0', so that the preimage cannot be 02090 // easily computed by inverting the affine relation. Add the 02091 // congruence induced by the affine relation. 02092 { 02093 Congruence cg((denominator*var %= expr) / denominator); 02094 if (modulus < 0) 02095 cg /= -modulus; 02096 else 02097 cg /= modulus; 02098 add_congruence_no_check(cg); 02099 } 02100 02101 // If the resulting grid is empty, its preimage is empty too. 02102 // Note: DO check for emptiness here, as we will later add a line. 02103 if (is_empty()) 02104 return; 02105 add_grid_generator(grid_line(var)); 02106 PPL_ASSERT(OK()); 02107 } 02108 02109 void 02110 PPL::Grid:: 02111 generalized_affine_image(const Linear_Expression& lhs, 02112 const Relation_Symbol relsym, 02113 const Linear_Expression& rhs, 02114 Coefficient_traits::const_reference modulus) { 02115 // Dimension-compatibility checks. 02116 // The dimension of `lhs' should be at most the dimension of 02117 // `*this'. 02118 dimension_type lhs_space_dim = lhs.space_dimension(); 02119 if (space_dim < lhs_space_dim) 02120 throw_dimension_incompatible("generalized_affine_image(e1, r, e2, m)", 02121 "e1", lhs); 02122 // The dimension of `rhs' should be at most the dimension of 02123 // `*this'. 02124 const dimension_type rhs_space_dim = rhs.space_dimension(); 02125 if (space_dim < rhs_space_dim) 02126 throw_dimension_incompatible("generalized_affine_image(e1, r, e2, m)", 02127 "e2", rhs); 02128 // The relation symbol cannot be a disequality. 02129 if (relsym == NOT_EQUAL) 02130 throw_invalid_argument("generalized_affine_image(e1, r, e2, m)", 02131 "r is the disequality relation symbol"); 02132 02133 // Any image of an empty grid is empty. 02134 if (marked_empty()) 02135 return; 02136 02137 // If relsym is not EQUAL, then we return a safe approximation 02138 // by adding a line in the direction of var. 02139 if (relsym != EQUAL) { 02140 02141 if (modulus != 0) 02142 throw_invalid_argument("generalized_affine_image(e1, r, e2, m)", 02143 "r != EQUAL && m != 0"); 02144 02145 if (!generators_are_up_to_date()) 02146 minimize(); 02147 02148 // Any image of an empty grid is empty. 02149 if (marked_empty()) 02150 return; 02151 02152 for (dimension_type i = space_dim; i-- > 0; ) 02153 if (lhs.coefficient(Variable(i)) != 0) 02154 add_grid_generator(grid_line(Variable(i))); 02155 02156 PPL_ASSERT(OK()); 02157 return; 02158 } 02159 02160 PPL_ASSERT(relsym == EQUAL); 02161 02162 PPL_DIRTY_TEMP_COEFFICIENT(tmp_modulus); 02163 tmp_modulus = modulus; 02164 if (tmp_modulus < 0) 02165 neg_assign(tmp_modulus); 02166 02167 // Compute the actual space dimension of `lhs', 02168 // i.e., the highest dimension having a non-zero coefficient in `lhs'. 02169 do { 02170 if (lhs_space_dim == 0) { 02171 // All variables have zero coefficients, so `lhs' is a constant. 02172 add_congruence_no_check((lhs %= rhs) / tmp_modulus); 02173 return; 02174 } 02175 } 02176 while (lhs.coefficient(Variable(--lhs_space_dim)) == 0); 02177 02178 // Gather in `new_lines' the collections of all the lines having the 02179 // direction of variables occurring in `lhs'. While at it, check 02180 // whether there exists a variable occurring in both `lhs' and 02181 // `rhs'. 02182 Grid_Generator_System new_lines; 02183 bool lhs_vars_intersect_rhs_vars = false; 02184 for (dimension_type i = lhs_space_dim + 1; i-- > 0; ) 02185 if (lhs.coefficient(Variable(i)) != 0) { 02186 new_lines.insert(grid_line(Variable(i))); 02187 if (rhs.coefficient(Variable(i)) != 0) 02188 lhs_vars_intersect_rhs_vars = true; 02189 } 02190 02191 if (lhs_vars_intersect_rhs_vars) { 02192 // Some variables in `lhs' also occur in `rhs'. 02193 // To ease the computation, add an additional dimension. 02194 const Variable new_var(space_dim); 02195 add_space_dimensions_and_embed(1); 02196 02197 // Constrain the new dimension to be equal to the right hand side. 02198 // TODO: Use add_congruence() when it has been updated. 02199 Congruence_System new_cgs1(new_var == rhs); 02200 add_recycled_congruences(new_cgs1); 02201 if (!is_empty()) { 02202 // The grid still contains points. 02203 02204 // Existentially quantify all the variables occurring in the left 02205 // hand side expression. 02206 02207 // Adjust `new_lines' to the right dimension. 02208 new_lines.insert(parameter(0*Variable(space_dim-1))); 02209 // Add the lines to `gen_sys' (first make sure they are up-to-date). 02210 update_generators(); 02211 gen_sys.recycling_insert(new_lines); 02212 normalize_divisors(gen_sys); 02213 // Update the flags. 02214 clear_congruences_up_to_date(); 02215 clear_generators_minimized(); 02216 02217 // Constrain the new dimension so that it is congruent to the left 02218 // hand side expression modulo `modulus'. 02219 // TODO: Use add_congruence() when it has been updated. 02220 Congruence_System new_cgs2((lhs %= new_var) / tmp_modulus); 02221 add_recycled_congruences(new_cgs2); 02222 } 02223 02224 // Remove the temporarily added dimension. 02225 remove_higher_space_dimensions(space_dim-1); 02226 } 02227 else { 02228 // `lhs' and `rhs' variables are disjoint: 02229 // there is no need to add a further dimension. 02230 02231 // Only add the lines and congruence if there are points. 02232 if (is_empty()) 02233 return; 02234 02235 // Existentially quantify all the variables occurring in the left hand 02236 // side expression. 02237 add_recycled_grid_generators(new_lines); 02238 02239 // Constrain the left hand side expression so that it is congruent to 02240 // the right hand side expression modulo `modulus'. 02241 add_congruence_no_check((lhs %= rhs) / tmp_modulus); 02242 } 02243 02244 PPL_ASSERT(OK()); 02245 } 02246 02247 void 02248 PPL::Grid:: 02249 generalized_affine_preimage(const Linear_Expression& lhs, 02250 const Relation_Symbol relsym, 02251 const Linear_Expression& rhs, 02252 Coefficient_traits::const_reference modulus) { 02253 // The dimension of `lhs' must be at most the dimension of `*this'. 02254 dimension_type lhs_space_dim = lhs.space_dimension(); 02255 if (space_dim < lhs_space_dim) 02256 throw_dimension_incompatible("generalized_affine_preimage(e1, r, e2, m)", 02257 "lhs", lhs); 02258 // The dimension of `rhs' must be at most the dimension of `*this'. 02259 const dimension_type rhs_space_dim = rhs.space_dimension(); 02260 if (space_dim < rhs_space_dim) 02261 throw_dimension_incompatible("generalized_affine_preimage(e1, r, e2, m)", 02262 "e2", rhs); 02263 // The relation symbol cannot be a disequality. 02264 if (relsym == NOT_EQUAL) 02265 throw_invalid_argument("generalized_affine_preimage(e1, r, e2, m)", 02266 "r is the disequality relation symbol"); 02267 02268 // Any preimage of an empty grid is empty. 02269 if (marked_empty()) 02270 return; 02271 02272 // If relsym is not EQUAL, then we return a safe approximation 02273 // by adding a line in the direction of var. 02274 if (relsym != EQUAL) { 02275 02276 if (modulus != 0) 02277 throw_invalid_argument("generalized_affine_preimage(e1, r, e2, m)", 02278 "r != EQUAL && m != 0"); 02279 02280 if (!generators_are_up_to_date()) 02281 minimize(); 02282 02283 // Any image of an empty grid is empty. 02284 if (marked_empty()) 02285 return; 02286 02287 for (dimension_type i = lhs_space_dim + 1; i-- > 0; ) 02288 if (lhs.coefficient(Variable(i)) != 0) 02289 add_grid_generator(grid_line(Variable(i))); 02290 02291 PPL_ASSERT(OK()); 02292 return; 02293 } 02294 02295 PPL_ASSERT(relsym == EQUAL); 02296 02297 PPL_DIRTY_TEMP_COEFFICIENT(tmp_modulus); 02298 tmp_modulus = modulus; 02299 if (tmp_modulus < 0) 02300 neg_assign(tmp_modulus); 02301 02302 // Compute the actual space dimension of `lhs', 02303 // i.e., the highest dimension having a non-zero coefficient in `lhs'. 02304 do { 02305 if (lhs_space_dim == 0) { 02306 // All variables have zero coefficients, so `lhs' is a constant. 02307 // In this case, preimage and image happen to be the same. 02308 add_congruence_no_check((lhs %= rhs) / tmp_modulus); 02309 return; 02310 } 02311 } 02312 while (lhs.coefficient(Variable(--lhs_space_dim)) == 0); 02313 02314 // Gather in `new_lines' the collections of all the lines having 02315 // the direction of variables occurring in `lhs'. 02316 // While at it, check whether or not there exists a variable 02317 // occurring in both `lhs' and `rhs'. 02318 Grid_Generator_System new_lines; 02319 bool lhs_vars_intersect_rhs_vars = false; 02320 for (dimension_type i = lhs_space_dim + 1; i-- > 0; ) 02321 if (lhs.coefficient(Variable(i)) != 0) { 02322 new_lines.insert(grid_line(Variable(i))); 02323 if (rhs.coefficient(Variable(i)) != 0) 02324 lhs_vars_intersect_rhs_vars = true; 02325 } 02326 02327 if (lhs_vars_intersect_rhs_vars) { 02328 // Some variables in `lhs' also occur in `rhs'. 02329 // To ease the computation, add an additional dimension. 02330 const Variable new_var(space_dim); 02331 add_space_dimensions_and_embed(1); 02332 02333 // Constrain the new dimension to be equal to `lhs' 02334 // TODO: Use add_congruence() when it has been updated. 02335 Congruence_System new_cgs1(new_var == lhs); 02336 add_recycled_congruences(new_cgs1); 02337 if (!is_empty()) { 02338 // The grid still contains points. 02339 02340 // Existentially quantify all the variables occurring in the left 02341 // hand side 02342 02343 // Adjust `new_lines' to the right dimension. 02344 new_lines.insert(parameter(0*Variable(space_dim-1))); 02345 // Add the lines to `gen_sys' (first make sure they are up-to-date). 02346 update_generators(); 02347 gen_sys.recycling_insert(new_lines); 02348 normalize_divisors(gen_sys); 02349 // Update the flags. 02350 clear_congruences_up_to_date(); 02351 clear_generators_minimized(); 02352 02353 // Constrain the new dimension so that it is related to 02354 // the right hand side modulo `modulus'. 02355 // TODO: Use add_congruence() when it has been updated. 02356 Congruence_System new_cgs2((rhs %= new_var) / tmp_modulus); 02357 add_recycled_congruences(new_cgs2); 02358 } 02359 02360 // Remove the temporarily added dimension. 02361 remove_higher_space_dimensions(space_dim-1); 02362 } 02363 else { 02364 // `lhs' and `rhs' variables are disjoint: 02365 // there is no need to add a further dimension. 02366 02367 // Constrain the left hand side expression so that it is congruent to 02368 // the right hand side expression modulo `mod'. 02369 add_congruence_no_check((lhs %= rhs) / tmp_modulus); 02370 02371 // Any image of an empty grid is empty. 02372 if (is_empty()) 02373 return; 02374 02375 // Existentially quantify all the variables occurring in `lhs'. 02376 add_recycled_grid_generators(new_lines); 02377 } 02378 PPL_ASSERT(OK()); 02379 } 02380 02381 void 02382 PPL::Grid:: 02383 bounded_affine_image(const Variable var, 02384 const Linear_Expression& lb_expr, 02385 const Linear_Expression& ub_expr, 02386 Coefficient_traits::const_reference denominator) { 02387 02388 // The denominator cannot be zero. 02389 if (denominator == 0) 02390 throw_invalid_argument("bounded_affine_image(v, lb, ub, d)", "d == 0"); 02391 02392 // Dimension-compatibility checks. 02393 // `var' should be one of the dimensions of the grid. 02394 const dimension_type var_space_dim = var.space_dimension(); 02395 if (space_dim < var_space_dim) 02396 throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)", 02397 "v", var); 02398 // The dimension of `lb_expr' and `ub_expr' should not be 02399 // greater than the dimension of `*this'. 02400 const dimension_type lb_space_dim = lb_expr.space_dimension(); 02401 if (space_dim < lb_space_dim) 02402 throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)", 02403 "lb", lb_expr); 02404 const dimension_type ub_space_dim = ub_expr.space_dimension(); 02405 if (space_dim < ub_space_dim) 02406 throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)", 02407 "ub", ub_expr); 02408 02409 // Any image of an empty grid is empty. 02410 if (marked_empty()) 02411 return; 02412 02413 // In all other cases, generalized_affine_preimage() must 02414 // just add a line in the direction of var. 02415 generalized_affine_image(var, 02416 LESS_OR_EQUAL, 02417 ub_expr, 02418 denominator); 02419 02420 PPL_ASSERT(OK()); 02421 } 02422 02423 02424 void 02425 PPL::Grid:: 02426 bounded_affine_preimage(const Variable var, 02427 const Linear_Expression& lb_expr, 02428 const Linear_Expression& ub_expr, 02429 Coefficient_traits::const_reference denominator) { 02430 02431 // The denominator cannot be zero. 02432 if (denominator == 0) 02433 throw_invalid_argument("bounded_affine_preimage(v, lb, ub, d)", "d == 0"); 02434 02435 // Dimension-compatibility checks. 02436 // `var' should be one of the dimensions of the grid. 02437 const dimension_type var_space_dim = var.space_dimension(); 02438 if (space_dim < var_space_dim) 02439 throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)", 02440 "v", var); 02441 // The dimension of `lb_expr' and `ub_expr' should not be 02442 // greater than the dimension of `*this'. 02443 const dimension_type lb_space_dim = lb_expr.space_dimension(); 02444 if (space_dim < lb_space_dim) 02445 throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)", 02446 "lb", lb_expr); 02447 const dimension_type ub_space_dim = ub_expr.space_dimension(); 02448 if (space_dim < ub_space_dim) 02449 throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)", 02450 "ub", ub_expr); 02451 02452 // Any preimage of an empty grid is empty. 02453 if (marked_empty()) 02454 return; 02455 02456 // In all other cases, generalized_affine_preimage() must 02457 // just add a line in the direction of var. 02458 generalized_affine_preimage(var, 02459 LESS_OR_EQUAL, 02460 ub_expr, 02461 denominator); 02462 02463 PPL_ASSERT(OK()); 02464 } 02465 02466 void 02467 PPL::Grid::time_elapse_assign(const Grid& y) { 02468 Grid& x = *this; 02469 // Check dimension-compatibility. 02470 if (x.space_dim != y.space_dim) 02471 throw_dimension_incompatible("time_elapse_assign(y)", "y", y); 02472 02473 // Deal with the zero-dimensional case. 02474 if (x.space_dim == 0) { 02475 if (y.marked_empty()) 02476 x.set_empty(); 02477 return; 02478 } 02479 02480 // If either one of `x' or `y' is empty, the result is empty too. 02481 if (x.marked_empty()) 02482 return; 02483 if (y.marked_empty() 02484 || (!x.generators_are_up_to_date() && !x.update_generators()) 02485 || (!y.generators_are_up_to_date() && !y.update_generators())) { 02486 x.set_empty(); 02487 return; 02488 } 02489 02490 // At this point both generator systems are up-to-date. 02491 Grid_Generator_System gs = y.gen_sys; 02492 dimension_type gs_num_rows = gs.num_rows(); 02493 02494 normalize_divisors(gs, gen_sys); 02495 02496 for (dimension_type i = gs_num_rows; i-- > 0; ) { 02497 Grid_Generator& g = gs[i]; 02498 if (g.is_point()) 02499 // Transform the point into a parameter. 02500 g.set_is_parameter(); 02501 } 02502 02503 if (gs_num_rows == 0) 02504 // `y' was the grid containing a single point at the origin, so 02505 // the result is `x'. 02506 return; 02507 02508 // Append `gs' to the generators of `x'. 02509 02510 gen_sys.recycling_insert(gs); 02511 02512 x.clear_congruences_up_to_date(); 02513 x.clear_generators_minimized(); 02514 02515 PPL_ASSERT(x.OK(true) && y.OK(true)); 02516 } 02517 02518 bool 02519 PPL::Grid::frequency(const Linear_Expression& expr, 02520 Coefficient& freq_n, Coefficient& freq_d, 02521 Coefficient& val_n, Coefficient& val_d) const { 02522 // The dimension of `expr' must be at most the dimension of *this. 02523 if (space_dim < expr.space_dimension()) 02524 throw_dimension_incompatible("frequency(e, ...)", "e", expr); 02525 02526 // Space dimension is 0: if empty, then return false; 02527 // otherwise the frequency is 1 and the value is 0. 02528 if (space_dim == 0) { 02529 if (is_empty()) 02530 return false; 02531 freq_n = 0; 02532 freq_d = 1; 02533 val_n = 0; 02534 val_d = 1; 02535 return true; 02536 } 02537 if (!generators_are_minimized() && !minimize()) 02538 // Minimizing found `this' empty. 02539 return false; 02540 02541 return frequency_no_check(expr, freq_n, freq_d, val_n, val_d); 02542 } 02543 02545 bool 02546 PPL::operator==(const Grid& x, const Grid& y) { 02547 if (x.space_dim != y.space_dim) 02548 return false; 02549 02550 if (x.marked_empty()) 02551 return y.is_empty(); 02552 if (y.marked_empty()) 02553 return x.is_empty(); 02554 if (x.space_dim == 0) 02555 return true; 02556 02557 switch (x.quick_equivalence_test(y)) { 02558 case Grid::TVB_TRUE: 02559 return true; 02560 02561 case Grid::TVB_FALSE: 02562 return false; 02563 02564 default: 02565 if (x.is_included_in(y)) { 02566 if (x.marked_empty()) 02567 return y.is_empty(); 02568 return y.is_included_in(x); 02569 } 02570 return false; 02571 } 02572 } 02573 02574 bool 02575 PPL::Grid::contains(const Grid& y) const { 02576 const Grid& x = *this; 02577 02578 // Dimension-compatibility check. 02579 if (x.space_dim != y.space_dim) 02580 throw_dimension_incompatible("contains(y)", "y", y); 02581 02582 if (y.marked_empty()) 02583 return true; 02584 if (x.marked_empty()) 02585 return y.is_empty(); 02586 if (y.space_dim == 0) 02587 return true; 02588 if (x.quick_equivalence_test(y) == Grid::TVB_TRUE) 02589 return true; 02590 return y.is_included_in(x); 02591 } 02592 02593 bool 02594 PPL::Grid::is_disjoint_from(const Grid& y) const { 02595 // Dimension-compatibility check. 02596 if (space_dim != y.space_dim) 02597 throw_dimension_incompatible("is_disjoint_from(y)", "y", y); 02598 Grid z = *this; 02599 z.intersection_assign(y); 02600 return z.is_empty(); 02601 } 02602 02603 void 02604 PPL::Grid::ascii_dump(std::ostream& s) const { 02605 using std::endl; 02606 02607 s << "space_dim " 02608 << space_dim 02609 << endl; 02610 status.ascii_dump(s); 02611 s << "con_sys (" 02612 << (congruences_are_up_to_date() ? "" : "not_") 02613 << "up-to-date)" 02614 << endl; 02615 con_sys.ascii_dump(s); 02616 s << "gen_sys (" 02617 << (generators_are_up_to_date() ? "" : "not_") 02618 << "up-to-date)" 02619 << endl; 02620 gen_sys.ascii_dump(s); 02621 s << "dimension_kinds"; 02622 if ((generators_are_up_to_date() && generators_are_minimized()) 02623 || (congruences_are_up_to_date() && congruences_are_minimized())) 02624 for (Dimension_Kinds::const_iterator i = dim_kinds.begin(); 02625 i != dim_kinds.end(); 02626 ++i) 02627 s << " " << *i; 02628 s << endl; 02629 } 02630 02631 PPL_OUTPUT_DEFINITIONS(Grid) 02632 02633 bool 02634 PPL::Grid::ascii_load(std::istream& s) { 02635 std::string str; 02636 02637 if (!(s >> str) || str != "space_dim") 02638 return false; 02639 02640 if (!(s >> space_dim)) 02641 return false; 02642 02643 if (!status.ascii_load(s)) 02644 return false; 02645 02646 if (!(s >> str) || str != "con_sys") 02647 return false; 02648 02649 if (!(s >> str)) 02650 return false; 02651 if (str == "(up-to-date)") 02652 set_congruences_up_to_date(); 02653 else if (str != "(not_up-to-date)") 02654 return false; 02655 02656 if (!con_sys.ascii_load(s)) 02657 return false; 02658 02659 if (!(s >> str) || str != "gen_sys") 02660 return false; 02661 02662 if (!(s >> str)) 02663 return false; 02664 if (str == "(up-to-date)") 02665 set_generators_up_to_date(); 02666 else if (str != "(not_up-to-date)") 02667 return false; 02668 02669 if (!gen_sys.ascii_load(s)) 02670 return false; 02671 02672 if (!(s >> str) || str != "dimension_kinds") 02673 return false; 02674 02675 if (!marked_empty() 02676 && ((generators_are_up_to_date() && generators_are_minimized()) 02677 || (congruences_are_up_to_date() && congruences_are_minimized()))) { 02678 dim_kinds.resize(space_dim + 1); 02679 for (Dimension_Kinds::size_type dim = 0; dim <= space_dim; ++dim) { 02680 short unsigned int dim_kind; 02681 if (!(s >> dim_kind)) 02682 return false; 02683 switch(dim_kind) { 02684 case 0: dim_kinds[dim] = PARAMETER; break; 02685 case 1: dim_kinds[dim] = LINE; break; 02686 case 2: dim_kinds[dim] = GEN_VIRTUAL; break; 02687 default: return false; 02688 } 02689 } 02690 } 02691 02692 // Check invariants. 02693 PPL_ASSERT(OK()); 02694 return true; 02695 } 02696 02697 PPL::memory_size_type 02698 PPL::Grid::external_memory_in_bytes() const { 02699 return 02700 con_sys.external_memory_in_bytes() 02701 + gen_sys.external_memory_in_bytes(); 02702 } 02703 02704 void 02705 PPL::Grid::wrap_assign(const Variables_Set& vars, 02706 Bounded_Integer_Type_Width w, 02707 Bounded_Integer_Type_Representation r, 02708 Bounded_Integer_Type_Overflow o, 02709 const Constraint_System* cs_p, 02710 unsigned /* complexity_threshold */, 02711 bool /* wrap_individually */) { 02712 02713 // Dimension-compatibility check of `*cs_p', if any. 02714 if (cs_p != 0) { 02715 const dimension_type cs_p_space_dim = cs_p->space_dimension(); 02716 if (cs_p->space_dimension() > space_dim) 02717 throw_dimension_incompatible("wrap_assign(vs, ...)", cs_p_space_dim); 02718 } 02719 02720 // Wrapping no variable is a no-op. 02721 if (vars.empty()) 02722 return; 02723 02724 // Dimension-compatibility check of `vars'. 02725 const dimension_type min_space_dim = vars.space_dimension(); 02726 if (space_dim < min_space_dim) 02727 throw_dimension_incompatible("wrap_assign(vs, ...)", min_space_dim); 02728 02729 // Wrapping an empty grid is a no-op. 02730 if (marked_empty()) 02731 return; 02732 if (!generators_are_minimized() && !minimize()) 02733 // Minimizing found `this' empty. 02734 return; 02735 02736 // Set the wrap frequency for variables of width `w'. 02737 // This is independent of the signedness `s'. 02738 PPL_DIRTY_TEMP_COEFFICIENT(wrap_frequency); 02739 mul_2exp_assign(wrap_frequency, Coefficient_one(), w); 02740 // Set `min_value' and `max_value' to the minimum and maximum values 02741 // a variable of width `w' and signedness `s' can take. 02742 PPL_DIRTY_TEMP_COEFFICIENT(min_value); 02743 PPL_DIRTY_TEMP_COEFFICIENT(max_value); 02744 if (r == UNSIGNED) { 02745 min_value = 0; 02746 mul_2exp_assign(max_value, Coefficient_one(), w); 02747 --max_value; 02748 } 02749 else { 02750 PPL_ASSERT(r == SIGNED_2_COMPLEMENT); 02751 mul_2exp_assign(max_value, Coefficient_one(), w-1); 02752 neg_assign(min_value, max_value); 02753 --max_value; 02754 } 02755 02756 // Generators are up-to-date and minimized. 02757 const Grid gr = *this; 02758 02759 // Overflow is impossible or wraps. 02760 if (o == OVERFLOW_IMPOSSIBLE || o == OVERFLOW_WRAPS) { 02761 PPL_DIRTY_TEMP_COEFFICIENT(f_n); 02762 PPL_DIRTY_TEMP_COEFFICIENT(f_d); 02763 PPL_DIRTY_TEMP_COEFFICIENT(v_n); 02764 PPL_DIRTY_TEMP_COEFFICIENT(v_d); 02765 for (Variables_Set::const_iterator i = vars.begin(), 02766 vars_end = vars.end(); i != vars_end; ++i) { 02767 const Variable x(*i); 02768 // Find the frequency and a value for `x' in `gr'. 02769 if (!gr.frequency_no_check(x, f_n, f_d, v_n, v_d)) 02770 continue; 02771 if (f_n == 0) { 02772 02773 // `x' is a constant in `gr'. 02774 if (v_d != 1) { 02775 // The value for `x' is not integral (`frequency_no_check()' 02776 // ensures that `v_n' and `v_d' have no common divisors). 02777 set_empty(); 02778 return; 02779 } 02780 02781 // `x' is a constant and has an integral value. 02782 if ((v_n > max_value) || (v_n < min_value)) { 02783 // The value is outside the range of the bounded integer type. 02784 if (o == OVERFLOW_IMPOSSIBLE) { 02785 // Then `x' has no possible value and hence set empty. 02786 set_empty(); 02787 return; 02788 } 02789 PPL_ASSERT(o == OVERFLOW_WRAPS); 02790 // The value v_n for `x' is wrapped modulo the 'wrap_frequency'. 02791 v_n %= wrap_frequency; 02792 // `v_n' is the value closest to 0 and may be negative. 02793 if (r == UNSIGNED && v_n < 0) 02794 v_n += wrap_frequency; 02795 unconstrain(x); 02796 add_constraint(x == v_n); 02797 } 02798 continue; 02799 } 02800 02801 // `x' is not a constant in `gr'. 02802 PPL_ASSERT(f_n != 0); 02803 02804 if (f_d % v_d != 0) { 02805 // Then `x' has no integral value and hence `gr' is set empty. 02806 set_empty(); 02807 return; 02808 } 02809 if (f_d != 1) { 02810 // `x' has non-integral values, so add the integrality 02811 // congruence for `x'. 02812 add_congruence((x %= 0) / 1); 02813 } 02814 if (o == OVERFLOW_WRAPS && f_n != wrap_frequency) 02815 // We know that `x' is not a constant, so, if overflow wraps, 02816 // `x' may wrap to a value modulo the `wrap_frequency'. 02817 add_grid_generator(parameter(wrap_frequency * x)); 02818 else if ((o == OVERFLOW_IMPOSSIBLE && 2*f_n >= wrap_frequency) 02819 || (f_n == wrap_frequency)) { 02820 // In these cases, `x' can only take a unique (ie constant) 02821 // value. 02822 if (r == UNSIGNED && v_n < 0) { 02823 // `v_n' is the value closest to 0 and may be negative. 02824 v_n += f_n; 02825 } 02826 unconstrain(x); 02827 add_constraint(x == v_n); 02828 } 02829 else { 02830 // If overflow is impossible but the grid frequency is less than 02831 // half the wrap frequency, then there is more than one possible 02832 // value for `x' in the range of the bounded integer type, 02833 // so the grid is unchanged. 02834 PPL_ASSERT(o == OVERFLOW_IMPOSSIBLE && 2*f_n < wrap_frequency); 02835 } 02836 } 02837 return; 02838 } 02839 02840 PPL_ASSERT(o == OVERFLOW_UNDEFINED); 02841 // If overflow is undefined, then all we know is that the variable 02842 // may take any integer within the range of the bounded integer type. 02843 const Grid_Generator& point = gr.gen_sys[0]; 02844 const Coefficient& div = point.divisor(); 02845 max_value *= div; 02846 min_value *= div; 02847 for (Variables_Set::const_iterator i = vars.begin(), 02848 vars_end = vars.end(); i != vars_end; ++i) { 02849 const Variable x(*i); 02850 if (!gr.bounds_no_check(x)) { 02851 // `x' is not a constant in `gr'. 02852 // We know that `x' is not a constant, so `x' may wrap to any 02853 // value `x + z' where z is an integer. 02854 if (point.coefficient(x) % div != 0) { 02855 // We know that `x' can take non-integral values, so enforce 02856 // integrality. 02857 unconstrain(x); 02858 add_congruence(x %= 0); 02859 } 02860 else 02861 // `x' has at least one integral value. 02862 // `x' may also take other non-integral values, 02863 // but checking could be costly so we ignore this. 02864 add_grid_generator(parameter(x)); 02865 } 02866 else { 02867 // `x' is a constant `v' in `gr'. 02868 const Coefficient& coeff_x = point.coefficient(x); 02869 // `x' should be integral. 02870 if (coeff_x % div != 0) { 02871 set_empty(); 02872 return; 02873 } 02874 // If the value `v' for `x' is not within the range for the 02875 // bounded integer type, then `x' may wrap to any value `v + z' 02876 // where `z' is an integer; otherwise `x' is unchanged. 02877 if (coeff_x > max_value || coeff_x < min_value) { 02878 add_grid_generator(parameter(x)); 02879 } 02880 } 02881 } 02882 } 02883 02884 void 02885 PPL::Grid::drop_some_non_integer_points(Complexity_Class) { 02886 if (marked_empty() || space_dim == 0) 02887 return; 02888 02889 // By adding integral congruences for each dimension to the 02890 // congruence system, defining \p *this, the grid will keep only 02891 // those points that have integral coordinates. All points in \p 02892 // *this with non-integral coordinates are removed. 02893 for (dimension_type i = space_dim; i-- > 0; ) 02894 add_congruence(Variable(i) %= 0); 02895 02896 PPL_ASSERT(OK()); 02897 } 02898 02899 void 02900 PPL::Grid::drop_some_non_integer_points(const Variables_Set& vars, 02901 Complexity_Class) { 02902 // Dimension-compatibility check. 02903 const dimension_type min_space_dim = vars.space_dimension(); 02904 if (space_dimension() < min_space_dim) 02905 throw_dimension_incompatible("drop_some_non_integer_points(vs, cmpl)", 02906 min_space_dim); 02907 02908 if (marked_empty() || min_space_dim == 0) 02909 return; 02910 02911 // By adding the integral congruences for each dimension in vars to 02912 // the congruence system defining \p *this, the grid will keep only 02913 // those points that have integer coordinates for all the dimensions 02914 // in vars. All points in \p *this with non-integral coordinates for 02915 // the dimensions in vars are removed. 02916 for (Variables_Set::const_iterator i = vars.begin(), 02917 vars_end = vars.end(); i != vars_end; ++i) 02918 add_congruence(Variable(*i) %= 0); 02919 02920 PPL_ASSERT(OK()); 02921 } 02922 02924 std::ostream& 02925 PPL::IO_Operators::operator<<(std::ostream& s, const Grid& gr) { 02926 if (gr.is_empty()) 02927 s << "false"; 02928 else if (gr.is_universe()) 02929 s << "true"; 02930 else 02931 s << gr.minimized_congruences(); 02932 return s; 02933 }