PPL  0.12.1
Polyhedron_chdims.cc
Go to the documentation of this file.
00001 /* Polyhedron class implementation
00002    (non-inline operators that may change the dimension of the vector space).
00003    Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it>
00004    Copyright (C) 2010-2012 BUGSENG srl (http://bugseng.com)
00005 
00006 This file is part of the Parma Polyhedra Library (PPL).
00007 
00008 The PPL is free software; you can redistribute it and/or modify it
00009 under the terms of the GNU General Public License as published by the
00010 Free Software Foundation; either version 3 of the License, or (at your
00011 option) any later version.
00012 
00013 The PPL is distributed in the hope that it will be useful, but WITHOUT
00014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00015 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00016 for more details.
00017 
00018 You should have received a copy of the GNU General Public License
00019 along with this program; if not, write to the Free Software Foundation,
00020 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA.
00021 
00022 For the most up-to-date information see the Parma Polyhedra Library
00023 site: http://bugseng.com/products/ppl/ . */
00024 
00025 #include "ppl-config.h"
00026 #include "Polyhedron.defs.hh"
00027 #include "Variables_Set.defs.hh"
00028 #include "assert.hh"
00029 
00030 #define BE_LAZY 1
00031 
00032 namespace PPL = Parma_Polyhedra_Library;
00033 
00034 void
00035 PPL::Polyhedron::add_space_dimensions(Linear_System& sys1,
00036                                       Linear_System& sys2,
00037                                       Bit_Matrix& sat1,
00038                                       Bit_Matrix& sat2,
00039                                       dimension_type add_dim) {
00040   PPL_ASSERT(sys1.topology() == sys2.topology());
00041   PPL_ASSERT(sys1.num_columns() == sys2.num_columns());
00042   PPL_ASSERT(add_dim != 0);
00043   using std::swap;
00044 
00045   sys1.add_zero_columns(add_dim);
00046   dimension_type old_index = sys2.first_pending_row();
00047   sys2.add_rows_and_columns(add_dim);
00048   // The added rows are in the non-pending part.
00049   sys2.set_index_first_pending_row(old_index + add_dim);
00050 
00051   // The resulting saturation matrix will be as follows:
00052   // from row    0    to      add_dim-1       : only zeroes
00053   //          add_dim     add_dim+num_rows-1  : old saturation matrix
00054 
00055   // In fact all the old generators saturate all the new constraints
00056   // because the polyhedron has not been embedded in the new space.
00057   sat1.resize(sat1.num_rows() + add_dim, sat1.num_columns());
00058   // The old matrix is moved to the end of the new matrix.
00059   for (dimension_type i = sat1.num_rows() - add_dim; i-- > 0; )
00060     swap(sat1[i], sat1[i+add_dim]);
00061   // Computes the "sat_c", too.
00062   sat2.transpose_assign(sat1);
00063 
00064   if (!sys1.is_necessarily_closed()) {
00065     // Moving the epsilon coefficients to the new last column.
00066     dimension_type new_eps_index = sys1.num_columns() - 1;
00067     dimension_type old_eps_index = new_eps_index - add_dim;
00068     // This swap preserves sortedness of `sys1'.
00069     sys1.swap_columns(old_eps_index, new_eps_index);
00070 
00071     // Try to preserve sortedness of `sys2'.
00072     if (!sys2.is_sorted())
00073       sys2.swap_columns(old_eps_index, new_eps_index);
00074     else {
00075       for (dimension_type i = sys2.num_rows(); i-- > add_dim; ) {
00076         Linear_Row& r = sys2[i];
00077         swap(r[old_eps_index], r[new_eps_index]);
00078       }
00079       // The upper-right corner of `sys2' contains the J matrix:
00080       // swap coefficients to preserve sortedness.
00081       for (dimension_type i = add_dim; i-- > 0; ++old_eps_index) {
00082         Linear_Row& r = sys2[i];
00083         swap(r[old_eps_index], r[old_eps_index + 1]);
00084       }
00085     }
00086     // NOTE: since we swapped columns in both `sys1' and `sys2',
00087     // no swapping is required for `sat1' and `sat2'.
00088   }
00089 }
00090 
00091 void
00092 PPL::Polyhedron::add_space_dimensions_and_embed(dimension_type m) {
00093   // The space dimension of the resulting polyhedron should not
00094   // overflow the maximum allowed space dimension.
00095   check_space_dimension_overflow(m, max_space_dimension() - space_dimension(),
00096                                  topology(),
00097                                  "add_space_dimensions_and_embed(m)",
00098                                  "adding m new space dimensions exceeds "
00099                                  "the maximum allowed space dimension");
00100 
00101   // Adding no dimensions to any polyhedron is a no-op.
00102   if (m == 0)
00103     return;
00104 
00105   // Adding dimensions to an empty polyhedron is obtained by adjusting
00106   // `space_dim' and clearing `con_sys' (since it can contain the
00107   // unsatisfiable constraint system of the wrong dimension).
00108   if (marked_empty()) {
00109     space_dim += m;
00110     con_sys.clear();
00111     return;
00112   }
00113 
00114   // The case of a zero-dimensional space polyhedron.
00115   if (space_dim == 0) {
00116     // Since it is not empty, it has to be the universe polyhedron.
00117     PPL_ASSERT(status.test_zero_dim_univ());
00118     // We swap `*this' with a newly created
00119     // universe polyhedron of dimension `m'.
00120     Polyhedron ph(topology(), m, UNIVERSE);
00121     m_swap(ph);
00122     return;
00123   }
00124 
00125   // To embed an n-dimension space polyhedron in a (n+m)-dimension space,
00126   // we just add `m' zero-columns to the rows in the system of constraints;
00127   // in contrast, the system of generators needs additional rows,
00128   // corresponding to the vectors of the canonical basis
00129   // for the added dimensions. That is, for each new dimension `x[k]'
00130   // we add the line having that direction. This is done by invoking
00131   // the function add_space_dimensions() giving the system of generators
00132   // as the second argument.
00133   if (constraints_are_up_to_date())
00134     if (generators_are_up_to_date()) {
00135       // `sat_c' must be up to date for add_space_dimensions().
00136       if (!sat_c_is_up_to_date())
00137         update_sat_c();
00138       // Adds rows and/or columns to both matrices.
00139       // `add_space_dimensions' correctly handles pending constraints
00140       // or generators.
00141       add_space_dimensions(con_sys, gen_sys, sat_c, sat_g, m);
00142     }
00143     else {
00144       // Only constraints are up-to-date: no need to modify the generators.
00145       con_sys.add_zero_columns(m);
00146       // If the polyhedron is not necessarily closed,
00147       // move the epsilon coefficients to the last column.
00148       if (!is_necessarily_closed())
00149         con_sys.swap_columns(space_dim + 1, space_dim + 1 + m);
00150     }
00151   else {
00152     // Only generators are up-to-date: no need to modify the constraints.
00153     PPL_ASSERT(generators_are_up_to_date());
00154     gen_sys.add_rows_and_columns(m);
00155     // The polyhedron does not support pending generators.
00156     gen_sys.unset_pending_rows();
00157     // If the polyhedron is not necessarily closed,
00158     // move the epsilon coefficients to the last column.
00159     if (!is_necessarily_closed()) {
00160       // Try to preserve sortedness of `gen_sys'.
00161       if (!gen_sys.is_sorted())
00162         gen_sys.swap_columns(space_dim + 1, space_dim + 1 + m);
00163       else {
00164         using std::swap;
00165         dimension_type old_eps_index = space_dim + 1;
00166         dimension_type new_eps_index = old_eps_index + m;
00167         for (dimension_type i = gen_sys.num_rows(); i-- > m; ) {
00168           Generator& r = gen_sys[i];
00169           swap(r[old_eps_index], r[new_eps_index]);
00170         }
00171         // The upper-right corner of `gen_sys' contains the J matrix:
00172         // swap coefficients to preserve sortedness.
00173         for (dimension_type i = m; i-- > 0; ++old_eps_index) {
00174           Generator& r = gen_sys[i];
00175           swap(r[old_eps_index], r[old_eps_index + 1]);
00176         }
00177       }
00178     }
00179   }
00180   // Update the space dimension.
00181   space_dim += m;
00182 
00183   // Note: we do not check for satisfiability, because the system of
00184   // constraints may be unsatisfiable.
00185   PPL_ASSERT_HEAVY(OK());
00186 }
00187 
00188 void
00189 PPL::Polyhedron::add_space_dimensions_and_project(dimension_type m) {
00190   // The space dimension of the resulting polyhedron should not
00191   // overflow the maximum allowed space dimension.
00192   check_space_dimension_overflow(m, max_space_dimension() - space_dimension(),
00193                                  topology(),
00194                                  "add_space_dimensions_and_project(m)",
00195                                  "adding m new space dimensions exceeds "
00196                                  "the maximum allowed space dimension");
00197 
00198   // Adding no dimensions to any polyhedron is a no-op.
00199   if (m == 0)
00200     return;
00201 
00202   // Adding dimensions to an empty polyhedron is obtained
00203   // by merely adjusting `space_dim'.
00204   if (marked_empty()) {
00205     space_dim += m;
00206     con_sys.clear();
00207     return;
00208   }
00209 
00210   if (space_dim == 0) {
00211     PPL_ASSERT(status.test_zero_dim_univ() && gen_sys.has_no_rows());
00212     // The system of generators for this polyhedron has only
00213     // the origin as a point.
00214     // In an NNC polyhedron, all points have to be accompanied
00215     // by the corresponding closure points
00216     // (this time, dimensions are automatically adjusted).
00217     if (!is_necessarily_closed())
00218       gen_sys.insert(Generator::zero_dim_closure_point());
00219     gen_sys.insert(Generator::zero_dim_point());
00220     gen_sys.adjust_topology_and_space_dimension(topology(), m);
00221     set_generators_minimized();
00222     space_dim = m;
00223     PPL_ASSERT_HEAVY(OK());
00224     return;
00225   }
00226 
00227   // To project an n-dimension space polyhedron in a (n+m)-dimension space,
00228   // we just add to the system of generators `m' zero-columns;
00229   // In contrast, in the system of constraints, new rows are needed
00230   // in order to avoid embedding the old polyhedron in the new space.
00231   // Thus, for each new dimensions `x[k]', we add the constraint
00232   // x[k] = 0: this is done by invoking the function add_space_dimensions()
00233   // giving the system of constraints as the second argument.
00234   if (constraints_are_up_to_date())
00235     if (generators_are_up_to_date()) {
00236       // `sat_g' must be up to date for add_space_dimensions().
00237       if (!sat_g_is_up_to_date())
00238         update_sat_g();
00239       // Adds rows and/or columns to both matrices.
00240       // `add_space_dimensions()' correctly handles pending constraints
00241       // or generators.
00242       add_space_dimensions(gen_sys, con_sys, sat_g, sat_c, m);
00243     }
00244     else {
00245       // Only constraints are up-to-date: no need to modify the generators.
00246       con_sys.add_rows_and_columns(m);
00247       // The polyhedron does not support pending constraints.
00248       con_sys.unset_pending_rows();
00249       // If the polyhedron is not necessarily closed,
00250       // move the epsilon coefficients to the last column.
00251       if (!is_necessarily_closed()) {
00252         // Try to preserve sortedness of `con_sys'.
00253         if (!con_sys.is_sorted())
00254           con_sys.swap_columns(space_dim + 1, space_dim + 1 + m);
00255         else {
00256           using std::swap;
00257           dimension_type old_eps_index = space_dim + 1;
00258           dimension_type new_eps_index = old_eps_index + m;
00259           for (dimension_type i = con_sys.num_rows(); i-- > m; ) {
00260             Constraint& r = con_sys[i];
00261             swap(r[old_eps_index], r[new_eps_index]);
00262           }
00263           // The upper-right corner of `con_sys' contains the J matrix:
00264           // swap coefficients to preserve sortedness.
00265           for (dimension_type i = m; i-- > 0; ++old_eps_index) {
00266             Constraint& r = con_sys[i];
00267             swap(r[old_eps_index], r[old_eps_index + 1]);
00268           }
00269         }
00270       }
00271     }
00272   else {
00273     // Only generators are up-to-date: no need to modify the constraints.
00274     PPL_ASSERT(generators_are_up_to_date());
00275     gen_sys.add_zero_columns(m);
00276     // If the polyhedron is not necessarily closed,
00277     // move the epsilon coefficients to the last column.
00278     if (!is_necessarily_closed())
00279       gen_sys.swap_columns(space_dim + 1, space_dim + 1 + m);
00280   }
00281   // Now we update the space dimension.
00282   space_dim += m;
00283 
00284   // Note: we do not check for satisfiability, because the system of
00285   // constraints may be unsatisfiable.
00286   PPL_ASSERT_HEAVY(OK());
00287 }
00288 
00289 void
00290 PPL::Polyhedron::concatenate_assign(const Polyhedron& y) {
00291   if (topology() != y.topology())
00292     throw_topology_incompatible("concatenate_assign(y)", "y", y);
00293 
00294   // The space dimension of the resulting polyhedron should not
00295   // overflow the maximum allowed space dimension.
00296   const dimension_type added_columns = y.space_dim;
00297   check_space_dimension_overflow(added_columns,
00298                                  max_space_dimension() - space_dimension(),
00299                                  topology(),
00300                                  "concatenate_assign(y)",
00301                                  "concatenation exceeds the maximum "
00302                                  "allowed space dimension");
00303 
00304   // If `*this' or `y' are empty polyhedra, it is sufficient to adjust
00305   // the dimension of the space.
00306   if (marked_empty() || y.marked_empty()) {
00307     space_dim += added_columns;
00308     set_empty();
00309     return;
00310   }
00311 
00312   // If `y' is a non-empty 0-dim space polyhedron, the result is `*this'.
00313   if (added_columns == 0)
00314     return;
00315 
00316   // If `*this' is a non-empty 0-dim space polyhedron, the result is `y'.
00317   if (space_dim == 0) {
00318     *this = y;
00319     return;
00320   }
00321 
00322   // TODO: this implementation is just an executable specification.
00323   Constraint_System cs = y.constraints();
00324 
00325   // The constraints of `x' (possibly with pending rows) are required.
00326   if (has_pending_generators())
00327     process_pending_generators();
00328   else if (!constraints_are_up_to_date())
00329     update_constraints();
00330 
00331   // The matrix for the new system of constraints is obtained
00332   // by leaving the old system of constraints in the upper left-hand side
00333   // and placing the constraints of `cs' in the lower right-hand side.
00334   // NOTE: here topologies agree, whereas dimensions may not agree.
00335   dimension_type old_num_rows = con_sys.num_rows();
00336   dimension_type old_num_columns = con_sys.num_columns();
00337   dimension_type added_rows = cs.num_rows();
00338 
00339   // We already dealt with the cases of an empty or zero-dim `y' polyhedron;
00340   // also, `cs' contains the low-level constraints, at least.
00341   PPL_ASSERT(added_rows > 0 && added_columns > 0);
00342 
00343   con_sys.add_zero_rows_and_columns(added_rows, added_columns,
00344                                     Linear_Row::Flags(topology(),
00345                                                       Linear_Row::RAY_OR_POINT_OR_INEQUALITY));
00346   // Move the epsilon coefficient to the last column, if needed.
00347   if (!is_necessarily_closed())
00348     con_sys.swap_columns(old_num_columns - 1,
00349                          old_num_columns - 1 + added_columns);
00350   dimension_type cs_num_columns = cs.num_columns();
00351   // Steal the constraints from `cs' and put them in `con_sys'
00352   // using the right displacement for coefficients.
00353   for (dimension_type i = added_rows; i-- > 0; ) {
00354     Constraint& c_old = cs[i];
00355     Constraint& c_new = con_sys[old_num_rows + i];
00356     // Method `add_zero_rows_and_columns', by default, added inequalities.
00357     if (c_old.is_equality())
00358       c_new.set_is_equality();
00359     // The inhomogeneous term is not displaced.
00360     using std::swap;
00361     swap(c_new[0], c_old[0]);
00362     // All homogeneous terms (included the epsilon coefficient,
00363     // if present) are displaced by `space_dim' columns.
00364     for (dimension_type j = 1; j < cs_num_columns; ++j)
00365       swap(c_old[j], c_new[space_dim + j]);
00366   }
00367 
00368   if (can_have_something_pending()) {
00369     // If `*this' can support pending constraints, then, since we have
00370     // resized the system of constraints, we must also add to the generator
00371     // system those lines corresponding to the newly added dimensions,
00372     // because the non-pending parts of `con_sys' and `gen_sys' must still
00373     // be a DD pair in minimal form.
00374     gen_sys.add_rows_and_columns(added_columns);
00375     gen_sys.set_sorted(false);
00376     if (!is_necessarily_closed())
00377       gen_sys.swap_columns(old_num_columns - 1,
00378                            old_num_columns - 1 + added_columns);
00379     // The added lines are not pending.
00380     gen_sys.unset_pending_rows();
00381     // Since we added new lines at the beginning of `x.gen_sys',
00382     // we also have to adjust the saturation matrix `sat_c'.
00383     // FIXME: if `sat_c' is not up-to-date, could not we directly update
00384     // `sat_g' by resizing it and shifting its columns?
00385     if (!sat_c_is_up_to_date()) {
00386       sat_c.transpose_assign(sat_g);
00387       set_sat_c_up_to_date();
00388     }
00389     clear_sat_g_up_to_date();
00390     sat_c.resize(sat_c.num_rows() + added_columns, sat_c.num_columns());
00391     // The old saturation rows are copied at the end of the matrix.
00392     // The newly introduced lines saturate all the non-pending constraints,
00393     // thus their saturation rows are made of zeroes.
00394     using std::swap;
00395     for (dimension_type i = sat_c.num_rows() - added_columns; i-- > 0; )
00396       swap(sat_c[i], sat_c[i+added_columns]);
00397     // Since `added_rows > 0', we now have pending constraints.
00398     set_constraints_pending();
00399   }
00400   else {
00401     // The polyhedron cannot have pending constraints.
00402     con_sys.unset_pending_rows();
00403 #if BE_LAZY
00404     con_sys.set_sorted(false);
00405 #else
00406     con_sys.sort_rows();
00407 #endif
00408     clear_constraints_minimized();
00409     clear_generators_up_to_date();
00410     clear_sat_g_up_to_date();
00411     clear_sat_c_up_to_date();
00412   }
00413   // Update space dimension.
00414   space_dim += added_columns;
00415 
00416   // The system of constraints may be unsatisfiable,
00417   // thus we do not check for satisfiability.
00418   PPL_ASSERT_HEAVY(OK());
00419 }
00420 
00421 void
00422 PPL::Polyhedron::remove_space_dimensions(const Variables_Set& vars) {
00423   // The removal of no dimensions from any polyhedron is a no-op.
00424   // Note that this case also captures the only legal removal of
00425   // dimensions from a polyhedron in a 0-dim space.
00426   if (vars.empty()) {
00427     PPL_ASSERT_HEAVY(OK());
00428     return;
00429   }
00430 
00431   // Dimension-compatibility check.
00432   const dimension_type min_space_dim = vars.space_dimension();
00433   if (space_dim < min_space_dim)
00434     throw_dimension_incompatible("remove_space_dimensions(vs)", min_space_dim);
00435 
00436   const dimension_type new_space_dim = space_dim - vars.size();
00437 
00438   // We need updated generators; note that keeping pending generators
00439   // is useless because the constraints will be dropped anyway.
00440   if (marked_empty()
00441       || (has_something_pending() && !remove_pending_to_obtain_generators())
00442       || (!generators_are_up_to_date() && !update_generators())) {
00443     // Removing dimensions from the empty polyhedron:
00444     // we clear `con_sys' since it could have contained the
00445     // unsatisfiable constraint of the wrong dimension.
00446     con_sys.clear();
00447     // Update the space dimension.
00448     space_dim = new_space_dim;
00449     PPL_ASSERT_HEAVY(OK());
00450     return;
00451   }
00452 
00453   // When removing _all_ dimensions from a non-empty polyhedron,
00454   // we obtain the zero-dimensional universe polyhedron.
00455   if (new_space_dim == 0) {
00456     set_zero_dim_univ();
00457     return;
00458   }
00459 
00460   // For each variable to be removed, we fill the corresponding column
00461   // by shifting left those columns that will not be removed.
00462   Variables_Set::const_iterator vsi = vars.begin();
00463   Variables_Set::const_iterator vsi_end = vars.end();
00464   dimension_type dst_col = *vsi + 1;
00465   dimension_type src_col = dst_col + 1;
00466   for (++vsi; vsi != vsi_end; ++vsi) {
00467     const dimension_type vsi_col = *vsi + 1;
00468     // All columns in between are moved to the left.
00469     while (src_col < vsi_col)
00470       gen_sys.Dense_Matrix::swap_columns(dst_col++, src_col++);
00471     ++src_col;
00472   }
00473   // Moving the remaining columns.
00474   const dimension_type gen_sys_num_columns = gen_sys.num_columns();
00475   while (src_col < gen_sys_num_columns)
00476     gen_sys.Dense_Matrix::swap_columns(dst_col++, src_col++);
00477 
00478   // The number of remaining columns is `dst_col'.
00479   // Note that resizing also calls `set_sorted(false)'.
00480   gen_sys.remove_trailing_columns(gen_sys_num_columns - dst_col);
00481   // We may have invalid lines and rays now.
00482   gen_sys.remove_invalid_lines_and_rays();
00483 
00484   // Constraints are not up-to-date and generators are not minimized.
00485   clear_constraints_up_to_date();
00486   clear_generators_minimized();
00487 
00488   // Update the space dimension.
00489   space_dim = new_space_dim;
00490 
00491   PPL_ASSERT_HEAVY(OK(true));
00492 }
00493 
00494 void
00495 PPL::Polyhedron::remove_higher_space_dimensions(dimension_type new_dimension) {
00496   // Dimension-compatibility check.
00497   if (new_dimension > space_dim)
00498     throw_dimension_incompatible("remove_higher_space_dimensions(nd)",
00499                                  new_dimension);
00500 
00501   // The removal of no dimensions from any polyhedron is a no-op.
00502   // Note that this case also captures the only legal removal of
00503   // dimensions from a polyhedron in a 0-dim space.
00504   if (new_dimension == space_dim) {
00505     PPL_ASSERT_HEAVY(OK());
00506     return;
00507   }
00508 
00509   // We need updated generators; note that keeping pending generators
00510   // is useless because constraints will be dropped anyway.
00511   if (marked_empty()
00512       || (has_something_pending() && !remove_pending_to_obtain_generators())
00513       || (!generators_are_up_to_date() && !update_generators())) {
00514     // Removing dimensions from the empty polyhedron:
00515     // just updates the space dimension.
00516     space_dim = new_dimension;
00517     con_sys.clear();
00518     PPL_ASSERT_HEAVY(OK());
00519     return;
00520   }
00521 
00522   if (new_dimension == 0) {
00523     // Removing all dimensions from a non-empty polyhedron:
00524     // just return the zero-dimensional universe polyhedron.
00525     set_zero_dim_univ();
00526     return;
00527   }
00528 
00529   dimension_type new_num_cols = new_dimension + 1;
00530   if (!is_necessarily_closed()) {
00531     // The polyhedron is not necessarily closed: move the column
00532     // of the epsilon coefficients to its new place.
00533     gen_sys.swap_columns(gen_sys.num_columns() - 1, new_num_cols);
00534     // The number of remaining columns is `new_dimension + 2'.
00535     ++new_num_cols;
00536   }
00537   // Note that resizing also calls `set_sorted(false)'.
00538   gen_sys.remove_trailing_columns(space_dim - new_dimension);
00539   // We may have invalid lines and rays now.
00540   gen_sys.remove_invalid_lines_and_rays();
00541 
00542   // Constraints are not up-to-date and generators are not minimized.
00543   clear_constraints_up_to_date();
00544   clear_generators_minimized();
00545 
00546   // Update the space dimension.
00547   space_dim = new_dimension;
00548 
00549   PPL_ASSERT_HEAVY(OK(true));
00550 }
00551 
00552 void
00553 PPL::Polyhedron::expand_space_dimension(Variable var, dimension_type m) {
00554   // TODO: this implementation is _really_ an executable specification.
00555 
00556   // `var' should be one of the dimensions of the vector space.
00557   if (var.space_dimension() > space_dim)
00558     throw_dimension_incompatible("expand_space_dimension(v, m)", "v", var);
00559 
00560   // The space dimension of the resulting polyhedron should not
00561   // overflow the maximum allowed space dimension.
00562   check_space_dimension_overflow(m, max_space_dimension() - space_dimension(),
00563                                  topology(),
00564                                  "expand_dimension(v, m)",
00565                                  "adding m new space dimensions exceeds "
00566                                  "the maximum allowed space dimension");
00567 
00568   // Nothing to do, if no dimensions must be added.
00569   if (m == 0)
00570     return;
00571 
00572   // Keep track of the dimension before adding the new ones.
00573   dimension_type old_dim = space_dim;
00574 
00575   // Add the required new dimensions.
00576   add_space_dimensions_and_embed(m);
00577 
00578   const dimension_type src_d = var.id();
00579   const Constraint_System& cs = constraints();
00580   Constraint_System new_constraints;
00581   for (Constraint_System::const_iterator i = cs.begin(),
00582          cs_end = cs.end(); i != cs_end; ++i) {
00583     const Constraint& c = *i;
00584 
00585     // If `c' does not constrain `var', skip it.
00586     if (c.coefficient(var) == 0)
00587       continue;
00588 
00589     // Each relevant constraint results in `m' new constraints.
00590     for (dimension_type dst_d = old_dim; dst_d < old_dim+m; ++dst_d) {
00591       Linear_Expression e;
00592       for (dimension_type j = old_dim; j-- > 0; )
00593         e +=
00594           c.coefficient(Variable(j))
00595           * ((j == src_d) ? Variable(dst_d) : Variable(j));
00596       e += c.inhomogeneous_term();
00597       new_constraints.insert(c.is_equality()
00598                              ? (e == 0)
00599                              : (c.is_nonstrict_inequality()
00600                                 ? (e >= 0)
00601                                 : (e > 0)));
00602     }
00603   }
00604   add_recycled_constraints(new_constraints);
00605   PPL_ASSERT_HEAVY(OK());
00606 }
00607 
00608 void
00609 PPL::Polyhedron::fold_space_dimensions(const Variables_Set& vars,
00610                                        Variable dest) {
00611   // TODO: this implementation is _really_ an executable specification.
00612 
00613   // `dest' should be one of the dimensions of the polyhedron.
00614   if (dest.space_dimension() > space_dim)
00615     throw_dimension_incompatible("fold_space_dimensions(vs, v)", "v", dest);
00616 
00617   // The folding of no dimensions is a no-op.
00618   if (vars.empty())
00619     return;
00620 
00621   // All variables in `vars' should be dimensions of the polyhedron.
00622   if (vars.space_dimension() > space_dim)
00623     throw_dimension_incompatible("fold_space_dimensions(vs, v)",
00624                                  "vs.space_dimension()",
00625                                  vars.space_dimension());
00626 
00627   // Moreover, `dest.id()' should not occur in `vars'.
00628   if (vars.find(dest.id()) != vars.end())
00629     throw_invalid_argument("fold_space_dimensions(vs, v)",
00630                            "v should not occur in vs");
00631 
00632   // All of the affine images we are going to compute are not invertible,
00633   // hence we will need to compute the generators of the polyhedron.
00634   // Since we keep taking copies, make sure that a single conversion
00635   // from constraints to generators is computed.
00636   (void) generators();
00637   // Having generators, we now know if the polyhedron is empty:
00638   // in that case, folding is equivalent to just removing space dimensions.
00639   if (!marked_empty()) {
00640     for (Variables_Set::const_iterator i = vars.begin(),
00641            vs_end = vars.end(); i != vs_end; ++i) {
00642       Polyhedron copy = *this;
00643       copy.affine_image(dest, Linear_Expression(Variable(*i)));
00644       poly_hull_assign(copy);
00645     }
00646   }
00647   remove_space_dimensions(vars);
00648   PPL_ASSERT_HEAVY(OK());
00649 }