summaryrefslogtreecommitdiff
path: root/src/Linear_System.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/Linear_System.cc')
-rw-r--r--src/Linear_System.cc935
1 files changed, 935 insertions, 0 deletions
diff --git a/src/Linear_System.cc b/src/Linear_System.cc
new file mode 100644
index 000000000..70238b906
--- /dev/null
+++ b/src/Linear_System.cc
@@ -0,0 +1,935 @@
+/* Linear_System class implementation (non-inline functions).
+ Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it>
+ Copyright (C) 2010-2011 BUGSENG srl (http://bugseng.com)
+
+This file is part of the Parma Polyhedra Library (PPL).
+
+The PPL is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3 of the License, or (at your
+option) any later version.
+
+The PPL is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA.
+
+For the most up-to-date information see the Parma Polyhedra Library
+site: http://www.cs.unipr.it/ppl/ . */
+
+#include <ppl-config.h>
+
+#include "Linear_System.defs.hh"
+#include "Coefficient.defs.hh"
+#include "Row.defs.hh"
+#include "Bit_Matrix.defs.hh"
+#include "Scalar_Products.defs.hh"
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <deque>
+
+#include "swapping_sort.icc"
+
+namespace PPL = Parma_Polyhedra_Library;
+
+PPL::dimension_type
+PPL::Linear_System::num_lines_or_equalities() const {
+ PPL_ASSERT(num_pending_rows() == 0);
+ const Linear_System& x = *this;
+ dimension_type n = 0;
+ for (dimension_type i = num_rows(); i-- > 0; )
+ if (x[i].is_line_or_equality())
+ ++n;
+ return n;
+}
+
+void
+PPL::Linear_System::merge_rows_assign(const Linear_System& y) {
+ PPL_ASSERT(row_size >= y.row_size);
+ // Both systems have to be sorted and have no pending rows.
+ PPL_ASSERT(check_sorted() && y.check_sorted());
+ PPL_ASSERT(num_pending_rows() == 0 && y.num_pending_rows() == 0);
+
+ Linear_System& x = *this;
+
+ // A temporary vector of rows...
+ std::vector<Row> tmp;
+ // ... with enough capacity not to require any reallocations.
+ tmp.reserve(compute_capacity(x.num_rows() + y.num_rows(), max_num_rows()));
+
+ dimension_type xi = 0;
+ dimension_type x_num_rows = x.num_rows();
+ dimension_type yi = 0;
+ dimension_type y_num_rows = y.num_rows();
+
+ while (xi < x_num_rows && yi < y_num_rows) {
+ const int comp = compare(x[xi], y[yi]);
+ if (comp <= 0) {
+ // Elements that can be taken from `x' are actually _stolen_ from `x'
+ std::swap(x[xi++], *tmp.insert(tmp.end(), Linear_Row()));
+ if (comp == 0)
+ // A duplicate element.
+ ++yi;
+ }
+ else {
+ // (comp > 0)
+ Linear_Row copy(y[yi++], row_size, row_capacity);
+ std::swap(copy, *tmp.insert(tmp.end(), Linear_Row()));
+ }
+ }
+ // Insert what is left.
+ if (xi < x_num_rows)
+ while (xi < x_num_rows)
+ std::swap(x[xi++], *tmp.insert(tmp.end(), Linear_Row()));
+ else
+ while (yi < y_num_rows) {
+ Linear_Row copy(y[yi++], row_size, row_capacity);
+ std::swap(copy, *tmp.insert(tmp.end(), Linear_Row()));
+ }
+
+ // We get the result vector and let the old one be destroyed.
+ std::swap(tmp, rows);
+ // There are no pending rows.
+ unset_pending_rows();
+ PPL_ASSERT(check_sorted());
+}
+
+void
+PPL::Linear_System::set_rows_topology() {
+ Linear_System& x = *this;
+ if (is_necessarily_closed())
+ for (dimension_type i = num_rows(); i-- > 0; )
+ x[i].set_necessarily_closed();
+ else
+ for (dimension_type i = num_rows(); i-- > 0; )
+ x[i].set_not_necessarily_closed();
+}
+
+void
+PPL::Linear_System::ascii_dump(std::ostream& s) const {
+ // Prints the topology, the number of rows, the number of columns
+ // and the sorted flag. The specialized methods provided by
+ // Constraint_System and Generator_System take care of properly
+ // printing the contents of the system.
+ const Linear_System& x = *this;
+ dimension_type x_num_rows = x.num_rows();
+ dimension_type x_num_columns = x.num_columns();
+ s << "topology " << (is_necessarily_closed()
+ ? "NECESSARILY_CLOSED"
+ : "NOT_NECESSARILY_CLOSED")
+ << "\n"
+ << x_num_rows << " x " << x_num_columns
+ << (x.sorted ? "(sorted)" : "(not_sorted)")
+ << "\n"
+ << "index_first_pending " << x.first_pending_row()
+ << "\n";
+ for (dimension_type i = 0; i < x_num_rows; ++i)
+ x[i].ascii_dump(s);
+}
+
+PPL_OUTPUT_DEFINITIONS_ASCII_ONLY(Linear_System)
+
+bool
+PPL::Linear_System::ascii_load(std::istream& s) {
+ std::string str;
+ if (!(s >> str) || str != "topology")
+ return false;
+ if (!(s >> str))
+ return false;
+ if (str == "NECESSARILY_CLOSED")
+ set_necessarily_closed();
+ else {
+ if (str != "NOT_NECESSARILY_CLOSED")
+ return false;
+ set_not_necessarily_closed();
+ }
+
+ dimension_type nrows;
+ dimension_type ncols;
+ if (!(s >> nrows))
+ return false;
+ if (!(s >> str) || str != "x")
+ return false;
+ if (!(s >> ncols))
+ return false;
+ resize_no_copy(nrows, ncols);
+
+ if (!(s >> str) || (str != "(sorted)" && str != "(not_sorted)"))
+ return false;
+ set_sorted(str == "(sorted)");
+ dimension_type index;
+ if (!(s >> str) || str != "index_first_pending")
+ return false;
+ if (!(s >> index))
+ return false;
+ set_index_first_pending_row(index);
+
+ Linear_System& x = *this;
+ for (dimension_type row = 0; row < nrows; ++row)
+ if (!x[row].ascii_load(s))
+ return false;
+
+ // Check invariants.
+ PPL_ASSERT(OK(true));
+ return true;
+}
+
+void
+PPL::Linear_System::insert(const Linear_Row& r) {
+ // The added row must be strongly normalized and have the same
+ // topology of the system.
+ PPL_ASSERT(r.check_strong_normalized());
+ PPL_ASSERT(topology() == r.topology());
+ // This method is only used when the system has no pending rows.
+ PPL_ASSERT(num_pending_rows() == 0);
+
+ const dimension_type old_num_rows = num_rows();
+ const dimension_type old_num_columns = num_columns();
+ const dimension_type r_size = r.size();
+
+ // Resize the system, if necessary.
+ if (r_size > old_num_columns) {
+ add_zero_columns(r_size - old_num_columns);
+ if (!is_necessarily_closed() && old_num_rows != 0)
+ // Move the epsilon coefficients to the last column
+ // (note: sorting is preserved).
+ swap_columns(old_num_columns - 1, r_size - 1);
+ add_row(r);
+ }
+ else if (r_size < old_num_columns) {
+ // Create a resized copy of the row.
+ Linear_Row tmp_row(r, old_num_columns, row_capacity);
+ // If needed, move the epsilon coefficient to the last position.
+ if (!is_necessarily_closed())
+ std::swap(tmp_row[r_size - 1], tmp_row[old_num_columns - 1]);
+ add_row(tmp_row);
+ }
+ else
+ // Here r_size == old_num_columns.
+ add_row(r);
+
+ // The added row was not a pending row.
+ PPL_ASSERT(num_pending_rows() == 0);
+ // Do not check for strong normalization,
+ // because no modification of rows has occurred.
+ PPL_ASSERT(OK(false));
+}
+
+void
+PPL::Linear_System::insert_pending(const Linear_Row& r) {
+ // The added row must be strongly normalized and have the same
+ // topology of the system.
+ PPL_ASSERT(r.check_strong_normalized());
+ PPL_ASSERT(topology() == r.topology());
+
+ const dimension_type old_num_rows = num_rows();
+ const dimension_type old_num_columns = num_columns();
+ const dimension_type r_size = r.size();
+
+ // Resize the system, if necessary.
+ if (r_size > old_num_columns) {
+ add_zero_columns(r_size - old_num_columns);
+ if (!is_necessarily_closed() && old_num_rows != 0)
+ // Move the epsilon coefficients to the last column
+ // (note: sorting is preserved).
+ swap_columns(old_num_columns - 1, r_size - 1);
+ add_pending_row(r);
+ }
+ else if (r_size < old_num_columns)
+ if (is_necessarily_closed() || old_num_rows == 0)
+ add_pending_row(Linear_Row(r, old_num_columns, row_capacity));
+ else {
+ // Create a resized copy of the row (and move the epsilon
+ // coefficient to its last position).
+ Linear_Row tmp_row(r, old_num_columns, row_capacity);
+ std::swap(tmp_row[r_size - 1], tmp_row[old_num_columns - 1]);
+ add_pending_row(tmp_row);
+ }
+ else
+ // Here r_size == old_num_columns.
+ add_pending_row(r);
+
+ // The added row was a pending row.
+ PPL_ASSERT(num_pending_rows() > 0);
+ // Do not check for strong normalization,
+ // because no modification of rows has occurred.
+ PPL_ASSERT(OK(false));
+}
+
+void
+PPL::Linear_System::add_pending_rows(const Linear_System& y) {
+ Linear_System& x = *this;
+ PPL_ASSERT(x.row_size == y.row_size);
+
+ const dimension_type x_n_rows = x.num_rows();
+ const dimension_type y_n_rows = y.num_rows();
+ // Grow to the required size without changing sortedness.
+ const bool was_sorted = sorted;
+ add_zero_rows(y_n_rows, Linear_Row::Flags(row_topology));
+ sorted = was_sorted;
+
+ // Copy the rows of `y', forcing size and capacity.
+ for (dimension_type i = y_n_rows; i-- > 0; ) {
+ Row copy(y[i], x.row_size, x.row_capacity);
+ std::swap(copy, x[x_n_rows+i]);
+ }
+ // Do not check for strong normalization,
+ // because no modification of rows has occurred.
+ PPL_ASSERT(OK(false));
+}
+
+void
+PPL::Linear_System::add_rows(const Linear_System& y) {
+ PPL_ASSERT(num_pending_rows() == 0);
+
+ // Adding no rows is a no-op.
+ if (y.has_no_rows())
+ return;
+
+ // Check if sortedness is preserved.
+ if (is_sorted()) {
+ if (!y.is_sorted() || y.num_pending_rows() > 0)
+ set_sorted(false);
+ else {
+ // `y' is sorted and has no pending rows.
+ const dimension_type n_rows = num_rows();
+ if (n_rows > 0)
+ set_sorted(compare((*this)[n_rows-1], y[0]) <= 0);
+ }
+ }
+
+ // Add the rows of `y' as if they were pending.
+ add_pending_rows(y);
+ // There are no pending_rows.
+ unset_pending_rows();
+
+ // Do not check for strong normalization,
+ // because no modification of rows has occurred.
+ PPL_ASSERT(OK(false));
+}
+
+void
+PPL::Linear_System::sort_rows() {
+ const dimension_type num_pending = num_pending_rows();
+ // We sort the non-pending rows only.
+ sort_rows(0, first_pending_row());
+ set_index_first_pending_row(num_rows() - num_pending);
+ sorted = true;
+ // Do not check for strong normalization,
+ // because no modification of rows has occurred.
+ PPL_ASSERT(OK(false));
+}
+
+void
+PPL::Linear_System::sort_rows(const dimension_type first_row,
+ const dimension_type last_row) {
+ PPL_ASSERT(first_row <= last_row && last_row <= num_rows());
+ // We cannot mix pending and non-pending rows.
+ PPL_ASSERT(first_row >= first_pending_row()
+ || last_row <= first_pending_row());
+
+ const dimension_type num_elems = last_row - first_row;
+ if (num_elems < 2)
+ return;
+
+ // Build the function objects implementing indirect sort comparison,
+ // indirect unique comparison and indirect swap operation.
+ typedef std::vector<Row> Cont;
+ Indirect_Sort_Compare<Cont, Row_Less_Than> sort_cmp(rows, first_row);
+ Indirect_Unique_Compare<Cont> unique_cmp(rows, first_row);
+ Indirect_Swapper<Cont> swapper(rows, first_row);
+
+ const dimension_type num_duplicates
+ = indirect_sort_and_unique(num_elems, sort_cmp, unique_cmp, swapper);
+
+ if (num_duplicates > 0)
+ rows.erase(rows.begin() + (last_row - num_duplicates),
+ rows.begin() + last_row);
+
+ // NOTE: we cannot check all invariants of the system here,
+ // because the caller still has to update `index_first_pending'.
+}
+
+void
+PPL::Linear_System::add_row(const Linear_Row& r) {
+ // The added row must be strongly normalized and have the same
+ // number of elements as the existing rows of the system.
+ PPL_ASSERT(r.check_strong_normalized());
+ PPL_ASSERT(r.size() == row_size);
+ // This method is only used when the system has no pending rows.
+ PPL_ASSERT(num_pending_rows() == 0);
+
+ const bool was_sorted = is_sorted();
+
+ Matrix::add_row(r);
+
+ // We update `index_first_pending', because it must be equal to
+ // `num_rows()'.
+ set_index_first_pending_row(num_rows());
+
+ if (was_sorted) {
+ const dimension_type nrows = num_rows();
+ // The added row may have caused the system to be not sorted anymore.
+ if (nrows > 1) {
+ // If the system is not empty and the inserted row is the
+ // greatest one, the system is set to be sorted.
+ // If it is not the greatest one then the system is no longer sorted.
+ Linear_System& x = *this;
+ set_sorted(compare(x[nrows-2], x[nrows-1]) <= 0);
+ }
+ else
+ // A system having only one row is sorted.
+ set_sorted(true);
+ }
+ // The added row was not a pending row.
+ PPL_ASSERT(num_pending_rows() == 0);
+ // Do not check for strong normalization, because no modification of
+ // rows has occurred.
+ PPL_ASSERT(OK(false));
+}
+
+void
+PPL::Linear_System::add_pending_row(const Linear_Row& r) {
+ // The added row must be strongly normalized and have the same
+ // number of elements of the existing rows of the system.
+ PPL_ASSERT(r.check_strong_normalized());
+ PPL_ASSERT(r.size() == row_size);
+
+ const dimension_type new_rows_size = rows.size() + 1;
+ if (rows.capacity() < new_rows_size) {
+ // Reallocation will take place.
+ std::vector<Row> new_rows;
+ new_rows.reserve(compute_capacity(new_rows_size, max_num_rows()));
+ new_rows.insert(new_rows.end(), new_rows_size, Row());
+ // Put the new row in place.
+ Row new_row(r, row_capacity);
+ dimension_type i = new_rows_size-1;
+ std::swap(new_rows[i], new_row);
+ // Steal the old rows.
+ while (i-- > 0)
+ new_rows[i].swap(rows[i]);
+ // Put the new rows into place.
+ std::swap(rows, new_rows);
+ }
+ else {
+ // Reallocation will NOT take place.
+ // Inserts a new empty row at the end, then substitutes it with a
+ // copy of the given row.
+ Row tmp(r, row_capacity);
+ std::swap(*rows.insert(rows.end(), Row()), tmp);
+ }
+
+ // The added row was a pending row.
+ PPL_ASSERT(num_pending_rows() > 0);
+ // Do not check for strong normalization, because no modification of
+ // rows has occurred.
+ PPL_ASSERT(OK(false));
+}
+
+void
+PPL::Linear_System::add_pending_row(const Linear_Row::Flags flags) {
+ const dimension_type new_rows_size = rows.size() + 1;
+ if (rows.capacity() < new_rows_size) {
+ // Reallocation will take place.
+ std::vector<Row> new_rows;
+ new_rows.reserve(compute_capacity(new_rows_size, max_num_rows()));
+ new_rows.insert(new_rows.end(), new_rows_size, Row());
+ // Put the new row in place.
+ Linear_Row new_row(row_size, row_capacity, flags);
+ dimension_type i = new_rows_size-1;
+ std::swap(new_rows[i], new_row);
+ // Steal the old rows.
+ while (i-- > 0)
+ new_rows[i].swap(rows[i]);
+ // Put the new vector into place.
+ std::swap(rows, new_rows);
+ }
+ else {
+ // Reallocation will NOT take place.
+ // Insert a new empty row at the end, then construct it assigning
+ // it the given type.
+ Row& new_row = *rows.insert(rows.end(), Row());
+ static_cast<Linear_Row&>(new_row).construct(row_size, row_capacity, flags);
+ }
+
+ // The added row was a pending row.
+ PPL_ASSERT(num_pending_rows() > 0);
+}
+
+void
+PPL::Linear_System::normalize() {
+ Linear_System& x = *this;
+ const dimension_type nrows = x.num_rows();
+ // We normalize also the pending rows.
+ for (dimension_type i = nrows; i-- > 0; )
+ x[i].normalize();
+ set_sorted(nrows <= 1);
+}
+
+void
+PPL::Linear_System::strong_normalize() {
+ Linear_System& x = *this;
+ const dimension_type nrows = x.num_rows();
+ // We strongly normalize also the pending rows.
+ for (dimension_type i = nrows; i-- > 0; )
+ x[i].strong_normalize();
+ set_sorted(nrows <= 1);
+}
+
+void
+PPL::Linear_System::sign_normalize() {
+ Linear_System& x = *this;
+ const dimension_type nrows = x.num_rows();
+ // We sign-normalize also the pending rows.
+ for (dimension_type i = num_rows(); i-- > 0; )
+ x[i].sign_normalize();
+ set_sorted(nrows <= 1);
+}
+
+/*! \relates Parma_Polyhedra_Library::Linear_System */
+bool
+PPL::operator==(const Linear_System& x, const Linear_System& y) {
+ if (x.num_columns() != y.num_columns())
+ return false;
+ const dimension_type x_num_rows = x.num_rows();
+ const dimension_type y_num_rows = y.num_rows();
+ if (x_num_rows != y_num_rows)
+ return false;
+ if (x.first_pending_row() != y.first_pending_row())
+ return false;
+ // Notice that calling operator==(const Matrix&, const Matrix&)
+ // would be wrong here, as equality of the type fields would
+ // not be checked.
+ for (dimension_type i = x_num_rows; i-- > 0; )
+ if (x[i] != y[i])
+ return false;
+ return true;
+}
+
+void
+PPL::Linear_System::sort_and_remove_with_sat(Bit_Matrix& sat) {
+ Linear_System& sys = *this;
+ // We can only sort the non-pending part of the system.
+ PPL_ASSERT(sys.first_pending_row() == sat.num_rows());
+ if (sys.first_pending_row() <= 1) {
+ sys.set_sorted(true);
+ return;
+ }
+
+ const dimension_type num_elems = sat.num_rows();
+ // Build the function objects implementing indirect sort comparison,
+ // indirect unique comparison and indirect swap operation.
+ typedef std::vector<Row> Cont;
+ Indirect_Sort_Compare<Cont, Row_Less_Than> sort_cmp(rows);
+ Indirect_Unique_Compare<Cont> unique_cmp(rows);
+ Indirect_Swapper2<Cont, Bit_Matrix> swapper(rows, sat);
+
+ const dimension_type num_duplicates
+ = indirect_sort_and_unique(num_elems, sort_cmp, unique_cmp, swapper);
+
+ const dimension_type new_first_pending_row
+ = sys.first_pending_row() - num_duplicates;
+
+ if (sys.num_pending_rows() > 0) {
+ // In this case, we must put the duplicates after the pending rows.
+ const dimension_type n_rows = sys.num_rows() - 1;
+ for (dimension_type i = 0; i < num_duplicates; ++i)
+ std::swap(sys[new_first_pending_row + i], sys[n_rows - i]);
+ }
+ // Erasing the duplicated rows...
+ sys.erase_to_end(sys.num_rows() - num_duplicates);
+ sys.set_index_first_pending_row(new_first_pending_row);
+ // ... and the corresponding rows of the saturation matrix.
+ sat.rows_erase_to_end(num_elems - num_duplicates);
+ PPL_ASSERT(sys.check_sorted());
+ // Now the system is sorted.
+ sys.set_sorted(true);
+}
+
+PPL::dimension_type
+PPL::Linear_System::gauss(const dimension_type n_lines_or_equalities) {
+ Linear_System& x = *this;
+ // This method is only applied to a well-formed linear system
+ // having no pending rows and exactly `n_lines_or_equalities'
+ // lines or equalities, all of which occur before the rays or points
+ // or inequalities.
+ PPL_ASSERT(x.OK(true));
+ PPL_ASSERT(x.num_pending_rows() == 0);
+ PPL_ASSERT(n_lines_or_equalities == x.num_lines_or_equalities());
+#ifndef NDEBUG
+ for (dimension_type i = n_lines_or_equalities; i-- > 0; )
+ PPL_ASSERT(x[i].is_line_or_equality());
+#endif
+
+ dimension_type rank = 0;
+ // Will keep track of the variations on the system of equalities.
+ bool changed = false;
+ for (dimension_type j = x.num_columns(); j-- > 0; )
+ for (dimension_type i = rank; i < n_lines_or_equalities; ++i) {
+ // Search for the first row having a non-zero coefficient
+ // (the pivot) in the j-th column.
+ if (x[i][j] == 0)
+ continue;
+ // Pivot found: if needed, swap rows so that this one becomes
+ // the rank-th row in the linear system.
+ if (i > rank) {
+ std::swap(x[i], x[rank]);
+ // After swapping the system is no longer sorted.
+ changed = true;
+ }
+ // Combine the row containing the pivot with all the lines or
+ // equalities following it, so that all the elements on the j-th
+ // column in these rows become 0.
+ for (dimension_type k = i + 1; k < n_lines_or_equalities; ++k)
+ if (x[k][j] != 0) {
+ x[k].linear_combine(x[rank], j);
+ changed = true;
+ }
+ // Already dealt with the rank-th row.
+ ++rank;
+ // Consider another column index `j'.
+ break;
+ }
+ if (changed)
+ x.set_sorted(false);
+ // A well-formed system is returned.
+ PPL_ASSERT(x.OK(true));
+ return rank;
+}
+
+void
+PPL::Linear_System
+::back_substitute(const dimension_type n_lines_or_equalities) {
+ Linear_System& x = *this;
+ // This method is only applied to a well-formed system
+ // having no pending rows and exactly `n_lines_or_equalities'
+ // lines or equalities, all of which occur before the first ray
+ // or point or inequality.
+ PPL_ASSERT(x.OK(true));
+ PPL_ASSERT(x.num_columns() >= 1);
+ PPL_ASSERT(x.num_pending_rows() == 0);
+ PPL_ASSERT(n_lines_or_equalities <= x.num_lines_or_equalities());
+#ifndef NDEBUG
+ for (dimension_type i = n_lines_or_equalities; i-- > 0; )
+ PPL_ASSERT(x[i].is_line_or_equality());
+#endif
+
+ const dimension_type nrows = x.num_rows();
+ const dimension_type ncols = x.num_columns();
+ // Trying to keep sortedness.
+ bool still_sorted = x.is_sorted();
+ // This deque of Booleans will be used to flag those rows that,
+ // before exiting, need to be re-checked for sortedness.
+ std::deque<bool> check_for_sortedness;
+ if (still_sorted)
+ check_for_sortedness.insert(check_for_sortedness.end(), nrows, false);
+
+ for (dimension_type k = n_lines_or_equalities; k-- > 0; ) {
+ // For each line or equality, starting from the last one,
+ // looks for the last non-zero element.
+ // `j' will be the index of such a element.
+ Linear_Row& x_k = x[k];
+ dimension_type j = ncols - 1;
+ while (j != 0 && x_k[j] == 0)
+ --j;
+
+ // Go through the equalities above `x_k'.
+ for (dimension_type i = k; i-- > 0; ) {
+ Linear_Row& x_i = x[i];
+ if (x_i[j] != 0) {
+ // Combine linearly `x_i' with `x_k'
+ // so that `x_i[j]' becomes zero.
+ x_i.linear_combine(x_k, j);
+ if (still_sorted) {
+ // Trying to keep sortedness: remember which rows
+ // have to be re-checked for sortedness at the end.
+ if (i > 0)
+ check_for_sortedness[i-1] = true;
+ check_for_sortedness[i] = true;
+ }
+ }
+ }
+
+ // Due to strong normalization during previous iterations,
+ // the pivot coefficient `x_k[j]' may now be negative.
+ // Since an inequality (or ray or point) cannot be multiplied
+ // by a negative factor, the coefficient of the pivot must be
+ // forced to be positive.
+ const bool have_to_negate = (x_k[j] < 0);
+ if (have_to_negate)
+ for (dimension_type h = ncols; h-- > 0; )
+ PPL::neg_assign(x_k[h]);
+ // Note: we do not mark index `k' in `check_for_sortedness',
+ // because we will later negate back the row.
+
+ // Go through all the other rows of the system.
+ for (dimension_type i = n_lines_or_equalities; i < nrows; ++i) {
+ Linear_Row& x_i = x[i];
+ if (x_i[j] != 0) {
+ // Combine linearly the `x_i' with `x_k'
+ // so that `x_i[j]' becomes zero.
+ x_i.linear_combine(x_k, j);
+ if (still_sorted) {
+ // Trying to keep sortedness: remember which rows
+ // have to be re-checked for sortedness at the end.
+ if (i > n_lines_or_equalities)
+ check_for_sortedness[i-1] = true;
+ check_for_sortedness[i] = true;
+ }
+ }
+ }
+ if (have_to_negate)
+ // Negate `x_k' to restore strong-normalization.
+ for (dimension_type h = ncols; h-- > 0; )
+ PPL::neg_assign(x_k[h]);
+ }
+
+ // Trying to keep sortedness.
+ for (dimension_type i = 0; still_sorted && i+1 < nrows; ++i)
+ if (check_for_sortedness[i])
+ // Have to check sortedness of `x[i]' with respect to `x[i+1]'.
+ still_sorted = (compare(x[i], x[i+1]) <= 0);
+ // Set the sortedness flag.
+ x.set_sorted(still_sorted);
+
+ // A well-formed system is returned.
+ PPL_ASSERT(x.OK(true));
+}
+
+void
+PPL::Linear_System::simplify() {
+ Linear_System& x = *this;
+ // This method is only applied to a well-formed system
+ // having no pending rows.
+ PPL_ASSERT(x.OK(true));
+ PPL_ASSERT(x.num_pending_rows() == 0);
+
+ // Partially sort the linear system so that all lines/equalities come first.
+ dimension_type nrows = x.num_rows();
+ dimension_type n_lines_or_equalities = 0;
+ for (dimension_type i = 0; i < nrows; ++i)
+ if (x[i].is_line_or_equality()) {
+ if (n_lines_or_equalities < i) {
+ std::swap(x[i], x[n_lines_or_equalities]);
+ // The system was not sorted.
+ PPL_ASSERT(!x.sorted);
+ }
+ ++n_lines_or_equalities;
+ }
+ // Apply Gaussian elimination to the subsystem of lines/equalities.
+ const dimension_type rank = x.gauss(n_lines_or_equalities);
+ // Eliminate any redundant line/equality that has been detected.
+ if (rank < n_lines_or_equalities) {
+ const dimension_type
+ n_rays_or_points_or_inequalities = nrows - n_lines_or_equalities;
+ const dimension_type
+ num_swaps = std::min(n_lines_or_equalities - rank,
+ n_rays_or_points_or_inequalities);
+ for (dimension_type i = num_swaps; i-- > 0; )
+ std::swap(x[--nrows], x[rank + i]);
+ x.erase_to_end(nrows);
+ x.unset_pending_rows();
+ if (n_rays_or_points_or_inequalities > num_swaps)
+ x.set_sorted(false);
+ n_lines_or_equalities = rank;
+ }
+ // Apply back-substitution to the system of rays/points/inequalities.
+ x.back_substitute(n_lines_or_equalities);
+ // A well-formed system is returned.
+ PPL_ASSERT(x.OK(true));
+}
+
+void
+PPL::Linear_System::add_rows_and_columns(const dimension_type n) {
+ PPL_ASSERT(n > 0);
+ const bool was_sorted = is_sorted();
+ const dimension_type old_n_rows = num_rows();
+ const dimension_type old_n_columns = num_columns();
+ add_zero_rows_and_columns(n, n, Linear_Row::Flags(row_topology));
+ Linear_System& x = *this;
+ // The old system is moved to the bottom.
+ for (dimension_type i = old_n_rows; i-- > 0; )
+ std::swap(x[i], x[i + n]);
+ for (dimension_type i = n, c = old_n_columns; i-- > 0; ) {
+ // The top right-hand sub-system (i.e., the system made of new
+ // rows and columns) is set to the specular image of the identity
+ // matrix.
+ Linear_Row& r = x[i];
+ r[c++] = 1;
+ r.set_is_line_or_equality();
+ // Note: `r' is strongly normalized.
+ }
+ // If the old system was empty, the last row added is either
+ // a positivity constraint or a point.
+ if (old_n_columns == 0) {
+ x[n-1].set_is_ray_or_point_or_inequality();
+ // Since ray, points and inequalities come after lines
+ // and equalities, this case implies the system is sorted.
+ set_sorted(true);
+ }
+ else if (was_sorted)
+ set_sorted(compare(x[n-1], x[n]) <= 0);
+
+ // A well-formed system has to be returned.
+ PPL_ASSERT(OK(true));
+}
+
+void
+PPL::Linear_System::sort_pending_and_remove_duplicates() {
+ PPL_ASSERT(num_pending_rows() > 0);
+ PPL_ASSERT(is_sorted());
+ Linear_System& x = *this;
+
+ // The non-pending part of the system is already sorted.
+ // Now sorting the pending part..
+ const dimension_type first_pending = x.first_pending_row();
+ x.sort_rows(first_pending, x.num_rows());
+ // Recompute the number of rows, because we may have removed
+ // some rows occurring more than once in the pending part.
+ dimension_type num_rows = x.num_rows();
+
+ dimension_type k1 = 0;
+ dimension_type k2 = first_pending;
+ dimension_type num_duplicates = 0;
+ // In order to erase them, put at the end of the system
+ // those pending rows that also occur in the non-pending part.
+ while (k1 < first_pending && k2 < num_rows) {
+ const int cmp = compare(x[k1], x[k2]);
+ if (cmp == 0) {
+ // We found the same row.
+ ++num_duplicates;
+ --num_rows;
+ // By initial sortedness, we can increment index `k1'.
+ ++k1;
+ // Do not increment `k2'; instead, swap there the next pending row.
+ if (k2 < num_rows)
+ std::swap(x[k2], x[k2 + num_duplicates]);
+ }
+ else if (cmp < 0)
+ // By initial sortedness, we can increment `k1'.
+ ++k1;
+ else {
+ // Here `cmp > 0'.
+ // Increment `k2' and, if we already found any duplicate,
+ // swap the next pending row in position `k2'.
+ ++k2;
+ if (num_duplicates > 0 && k2 < num_rows)
+ std::swap(x[k2], x[k2 + num_duplicates]);
+ }
+ }
+ // If needed, swap any duplicates found past the pending rows
+ // that has not been considered yet; then erase the duplicates.
+ if (num_duplicates > 0) {
+ if (k2 < num_rows)
+ for (++k2; k2 < num_rows; ++k2)
+ std::swap(x[k2], x[k2 + num_duplicates]);
+ x.erase_to_end(num_rows);
+ }
+ // Do not check for strong normalization,
+ // because no modification of rows has occurred.
+ PPL_ASSERT(OK(false));
+}
+
+bool
+PPL::Linear_System::check_sorted() const {
+ const Linear_System& x = *this;
+ for (dimension_type i = first_pending_row(); i-- > 1; )
+ if (compare(x[i], x[i-1]) < 0)
+ return false;
+ return true;
+}
+
+bool
+PPL::Linear_System::OK(const bool check_strong_normalized) const {
+#ifndef NDEBUG
+ using std::endl;
+ using std::cerr;
+#endif
+
+ // `index_first_pending' must be less than or equal to `num_rows()'.
+ if (first_pending_row() > num_rows()) {
+#ifndef NDEBUG
+ cerr << "Linear_System has a negative number of pending rows!"
+ << endl;
+#endif
+ return false;
+ }
+
+ // An empty system is OK,
+ // unless it is an NNC system with exactly one column.
+ if (has_no_rows()) {
+ if (is_necessarily_closed() || num_columns() != 1)
+ return true;
+ else {
+#ifndef NDEBUG
+ cerr << "NNC Linear_System has one column" << endl;
+#endif
+ return false;
+ }
+ }
+
+ // A non-empty system will contain constraints or generators; in
+ // both cases it must have at least one column for the inhomogeneous
+ // term and, if it is NNC, another one for the epsilon coefficient.
+ const dimension_type min_cols = is_necessarily_closed() ? 1 : 2;
+ if (num_columns() < min_cols) {
+#ifndef NDEBUG
+ cerr << "Linear_System has fewer columns than the minimum "
+ << "allowed by its topology:"
+ << endl
+ << "num_columns is " << num_columns()
+ << ", minimum is " << min_cols
+ << endl;
+#endif
+ return false;
+ }
+
+ const Linear_System& x = *this;
+ const dimension_type n_rows = num_rows();
+ for (dimension_type i = 0; i < n_rows; ++i) {
+ if (!x[i].OK(row_size, row_capacity))
+ return false;
+ // Checking for topology mismatches.
+ if (x.topology() != x[i].topology()) {
+#ifndef NDEBUG
+ cerr << "Topology mismatch between the system "
+ << "and one of its rows!"
+ << endl;
+#endif
+ return false;
+ }
+ }
+
+ if (check_strong_normalized) {
+ // Check for strong normalization of rows.
+ // Note: normalization cannot be checked inside the
+ // Linear_Row::OK() method, because a Linear_Row object may also
+ // implement a Linear_Expression object, which in general cannot
+ // be (strongly) normalized.
+ Linear_System tmp(x, With_Pending());
+ tmp.strong_normalize();
+ if (x != tmp) {
+#ifndef NDEBUG
+ cerr << "Linear_System rows are not strongly normalized!"
+ << endl;
+#endif
+ return false;
+ }
+ }
+
+ if (sorted && !check_sorted()) {
+#ifndef NDEBUG
+ cerr << "The system declares itself to be sorted but it is not!"
+ << endl;
+#endif
+ return false;
+ }
+
+ // All checks passed.
+ return true;
+}