1 /* Polyhedron class implementation: conversion().
2    Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it>
3    Copyright (C) 2010-2016 BUGSENG srl (http://bugseng.com)
4 
5 This file is part of the Parma Polyhedra Library (PPL).
6 
7 The PPL is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 The PPL is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software Foundation,
19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA.
20 
21 For the most up-to-date information see the Parma Polyhedra Library
22 site: http://bugseng.com/products/ppl/ . */
23 
24 #ifndef PPL_Polyhedron_conversion_templates_hh
25 #define PPL_Polyhedron_conversion_templates_hh 1
26 
27 #include "Bit_Row_defs.hh"
28 #include "Bit_Matrix_defs.hh"
29 #include "Polyhedron_defs.hh"
30 #include "Scalar_Products_defs.hh"
31 #include "Scalar_Products_inlines.hh"
32 #include "Temp_defs.hh"
33 #include "math_utilities_defs.hh"
34 
35 #include <cstddef>
36 #include <climits>
37 
38 // This flag turns on the quick non-adjacency test;
39 // the performance impact of this test was evaluated by H. Le Verge (1994);
40 // see also Corollary 4.2 in B. Genov's PhD thesis (2014).
41 #ifndef PPL_QUICK_NON_ADJ_TEST
42 #define PPL_QUICK_NON_ADJ_TEST 1
43 #endif
44 
45 // This flag turns on the quick adjacency test;
46 // for a justification, see Corollary 4.3 in B. Genov's PhD thesis (2014),
47 // where it is also said that the test was implemented in cddlib.
48 #ifndef PPL_QUICK_ADJ_TEST
49 #define PPL_QUICK_ADJ_TEST 1
50 #endif
51 
52 namespace Parma_Polyhedra_Library {
53 
54 /*!
55   \return
56   The number of lines of the polyhedron or the number of equality
57   constraints in the result of conversion.
58 
59   \param source
60   The system to use to convert \p dest: it may be modified;
61 
62   \param start
63   The index of \p source row from which conversion begin;
64 
65   \param dest
66   The result of the conversion;
67 
68   \param sat
69   The saturation matrix telling us, for each row in \p source, which
70   are the rows of \p dest that satisfy but do not saturate it;
71 
72   \param num_lines_or_equalities
73   The number of rows in the system \p dest that are either lines of
74   the polyhedron (when \p dest is a system of generators) or equality
75   constraints (when \p dest is a system of constraints).
76 
77   \if Include_Implementation_Details
78 
79   For simplicity, all the following comments assume we are converting a
80   constraint system \p source to a generator system \p dest;
81   the comments for the symmetric case can be obtained by duality.
82 
83   If some of the constraints in \p source are redundant, they will be removed.
84   This is why the \p source is not declared to be a constant parameter.
85 
86   If \p start is 0, then \p source is a sorted system; also, \p dest is
87   a generator system corresponding to an empty constraint system.
88   If otherwise \p start is greater than 0, then the two sub-systems of
89   \p source made by the non-pending rows and the pending rows, respectively,
90   are both sorted; also, \p dest is the generator system corresponding to
91   the non-pending constraints of \p source.
92 
93   Independently from the value of \p start, \p dest has lines from index 0
94   to index \p num_lines_or_equalities - 1 and rays/points from index
95   \p num_lines_or_equalities to the last of its rows.
96 
97   Note that here the rows of \p sat are indexed by rows of \p dest
98   and its columns are indexed by rows of \p source.
99 
100   We know that polyhedra can be represented by both a system of
101   constraints or a system of generators (points, rays and lines)
102   (see Section \ref representation).
103   When we have both descriptions for a polyhedron \f$P\f$
104   we have what is called a <EM>double description</EM>
105   (or <EM>DD pair</EM>) for \f$P\f$.
106 
107   Here, the <EM>representation system</EM> refers to the system \f$C\f$
108   whose rows represent the constraints that characterize \f$P\f$
109   and the <EM>generating system</EM>, the system \f$G\f$ whose rows
110   represent the generators of \f$P\f$.
111   We say that a pair \f$(C, G)\f$ of (real) systems is
112   a <EM>double description pair</EM> if
113   \f[
114     C\vect{x} \geq \vect{0}
115       \quad\iff\quad
116         \exists \vect{\lambda} \geq \vect{0} \mathrel{.}
117         \vect{x} = G\vect{\lambda}.
118   \f]
119 
120   The term "double description" is quite natural in the sense that
121   such a pair contains two different description of the same object.
122   In fact, if we refer to the cone representation of a polyhedron \f$P\f$
123   and we call \f$C\f$ and \f$G\f$ the systems of constraints and
124   rays respectively, we have
125   \f[
126     P = \{\, \vect{x} \in \Rset^n \mid C\vect{x} \geq \vect{0}\, \}
127       = \{\, \vect{x} \in \Rset^n \mid \vect{x} = G\vect{\lambda}
128       \text{ for some } \vect{\lambda} \geq \vect{0}\, \}.
129   \f]
130 
131   Because of the theorem of Minkowski (see Section \ref prelims),
132   we can say that, given a \f$m \times n\f$ representation system
133   \f$C\f$ such that
134   \f$\mathop{\mathrm{rank}}(C) = n = \mathit{dimension of the whole space}\f$
135   for a non-empty polyhedron \f$P\f$,
136   it is always possible to find a generating system \f$G\f$ for \f$P\f$
137   such that \f$(C, G)\f$ is a DD pair.
138   Conversely, Weyl's theorem ensures that, for each generating system
139   \f$G\f$, it is possible to find a representation system \f$C\f$
140   such that \f$(C, G)\f$ is a DD pair.
141 
142   For efficiency reasons, our representation of polyhedra makes use
143   of a double description.
144   We are thus left with two problems:
145     -# given \f$C\f$ find \f$G\f$ such that \f$(C, G)\f$ is a DD pair;
146     -# given \f$G\f$ find \f$C\f$ such that \f$(C, G)\f$ is a DD pair.
147 
148   Using Farkas' Lemma we can prove that these two problems are
149   computationally equivalent (i.e., linear-time reducible to each other).
150   Farkas' Lemma establishes a fundamental property of vectors in
151   \f$\Rset^n\f$ that, in a sense, captures the essence of duality.
152   Consider a matrix \f$A \in \Rset^{m \times n}\f$ and let
153   \f$\{ \vect{a}_1, \ldots, \vect{a}_m \}\f$ be its set of row vectors.
154   Consider also another vector \f$\vect{c} \in \Rset^n\f$ such that,
155   whenever a vector \f$\vect{y} \in \Rset^n\f$ has a non-negative projection
156   on the \f$\vect{a}_i\f$'s, it also has a non-negative projection
157   on \f$\vect{c}\f$.
158   The lemma states that \f$\vect{c}\f$ has this property if and only if
159   it is in the cone generated by the \f$\vect{a}_i\f$'s.
160   Formally, the lemma states the equivalence of the two following
161   assertions:
162     -# \f$
163          \forall \vect{y}
164            \mathrel{:} (A\vect{y} \geq 0 \implies
165            \langle \vect{y},\vect{c} \rangle \geq 0)
166        \f$;
167     -# \f$
168          \exists \vect{\lambda} \geq \vect{0}
169            \mathrel{.} \vect{c}^\mathrm{T} = \vect{\lambda}^\mathrm{T}A
170        \f$.
171 
172   With this result we can prove that \f$(C, G)\f$ is a DD pair
173   if and only if \f$(G^\mathrm{T}, C^\mathrm{T})\f$ is a DD pair.
174 
175   Suppose \f$(C, G)\f$ is a DD pair.
176   Thus, for each \f$x\f$ of the appropriate dimension,
177   \f$C\vect{x} \geq \vect{0}\f$ if and only if
178   \f$\exists \lambda \geq 0 \mathrel{.} \vect{x} = G\vect{\lambda}\f$,
179   which is of course equivalent to
180   \f$
181     \exists \vect{\lambda} \geq \vect{0}
182       \mathrel{.} \vect{x}^\mathrm{T} = \vect{\lambda}^\mathrm{T}G^\mathrm{T}
183   \f$.
184 
185   First, we assume that \f$\vect{z}\f$ is such that
186   \f$G^\mathrm{T}\vect{z} \geq \vect{0}\f$
187   and we will show that
188   \f$\exists \vect{\mu} \geq \vect{0} \mathrel{.}
189   \vect{z} = C^\mathrm{T}\vect{\mu}\f$.
190   Let \f$\vect{x}\f$ be such that \f$C\vect{x} \geq \vect{0}\f$.
191   Since \f$(C, G)\f$ is a DD pair, this is equivalent to
192   \f$
193     \exists \vect{\lambda} \geq \vect{0}
194       \mathrel{.} \vect{x}^\mathrm{T} = \vect{\lambda}^\mathrm{T}G^\mathrm{T}
195   \f$,
196   which, by Farkas' Lemma is equivalent to
197   \f$
198     \forall \vect{y} \mathrel{:} (G^\mathrm{T}\vect{y} \geq \vect{0} \implies
199                                  \langle \vect{y}, \vect{x} \rangle \geq 0)
200   \f$.
201   Taking \f$\vect{y} = \vect{z}\f$ and recalling our assumption that
202   \f$G^\mathrm{T}\vect{z} \geq \vect{0}\f$
203   we can conclude that \f$\langle \vect{z}, \vect{x} \rangle \geq 0\f$,
204   that is equivalent to \f$\langle \vect{x}, \vect{z} \rangle \geq 0\f$.
205   We have thus established that
206   \f$
207     \forall \vect{x} \mathrel{:} (C\vect{x} \geq \vect{0} \implies
208     \langle \vect{x}, \vect{z} \rangle \geq 0)
209   \f$.
210   By Farkas' Lemma, this is equivalent to
211   \f$\exists \vect{\mu} \geq \vect{0} \mathrel{.}
212   \vect{z}^\mathrm{T} = \vect{\mu}^\mathrm{T} C\f$,
213   which is equivalent to what we wanted to prove, that is,
214   \f$\exists \vect{\mu} \geq \vect{0} \mathrel{.}
215   \vect{z} = C^\mathrm{T}\vect{\mu}\f$.
216 
217   In order to prove the reverse implication, the following observation
218   turns out to be useful:
219   when \f$(C, G)\f$ is a DD pair, \f$CG \geq 0\f$.
220   In fact,
221   let \f$\vect{e}_j\f$ be the vector whose components are all \f$0\f$
222   apart from the \f$j\f$-th one, which is \f$1\f$.
223   Clearly \f$\vect{e}_j \geq \vect{0}\f$ and, taking
224   \f$\vect{\lambda} = \vect{e}_j\f$ and
225   \f$\vect{x} = G\vect{\lambda} = G \vect{e}_j\f$, we have
226   \f$C\vect{x} = C(G \vect{e}_j) = (CG)\vect{e}_j \geq \vect{0}\f$,
227   since \f$(C, G)\f$ is a DD pair.
228   Thus, as \f$(CG)\vect{e}_j\f$ is the \f$j\f$-th column of \f$CG\f$
229   and since the choice of \f$j\f$ was arbitrary, \f$CG \geq \vect{0}\f$.
230 
231   We now assume that \f$\vect{z}\f$ is such that
232   \f$\exists \vect{\mu} \geq \vect{0} \mathrel{.}
233   \vect{z} = C^\mathrm{T}\vect{\mu}\f$
234   and we will prove that \f$G^\mathrm{T}\vect{z} \geq \vect{0}\f$.
235   By Farkas' Lemma, the assumption
236   \f$\exists \vect{\mu} \geq \vect{0} \mathrel{.}
237   \vect{z}^\mathrm{T} = \vect{\mu}^\mathrm{T}C\f$,
238   is equivalent to
239   \f$\forall \vect{y} \mathrel{:} (C\vect{y} \geq \vect{0}
240   \implies \langle \vect{y}, \vect{z} \rangle \geq 0)\f$.
241   If we take \f$\vect{y} = G\vect{e}_j\f$ then \f$C\vect{y}
242                  = CG\vect{e}_j \geq 0\f$,
243   since \f$CG \geq \vect{0}\f$.
244   So
245   \f$
246     \langle \vect{y}, \vect{z} \rangle
247       = (\vect{e}_j^\mathrm{T}G^\mathrm{T}) \vect{z}
248       = \vect{e}_j^\mathrm{T}(G^\mathrm{T} \vect{z})
249       \geq 0
250   \f$,
251   that is, the \f$j\f$-th component of \f$G^\mathrm{T}\vect{z}\f$
252   is non-negative. The arbitrary choice of \f$j\f$ allows us to conclude
253   that \f$G^\mathrm{T}\vect{z} \geq \vect{0}\f$, as required.
254 
255   In view of this result, the following exposition assumes, for clarity,
256   that the conversion being performed is from constraints to generators.
257   Thus, even if the roles of \p source and \p dest can be interchanged,
258   in the sequel we assume the \p source system will contain the constraints
259   that represent the polyhedron and the \p dest system will contain
260   the generator that generates it.
261 
262   There are some observations that are useful to understand this function:
263 
264   Observation 1: Let \f$A\f$ be a system of constraints that generate
265   the polyhedron \f$P\f$ and \f$\vect{c}\f$ a new constraint that must
266   be added. Suppose that there is a line \f$\vect{z}\f$ that does not
267   saturate the constraint \f$\vect{c}\f$. If we combine the old lines
268   and rays that do not saturate \f$\vect{c}\f$ (except \f$\vect{z}\f$)
269   with \f$\vect{z}\f$ such that the new ones saturate \f$\vect{c}\f$,
270   the new lines and rays also saturate the constraints  saturated by
271   the old lines and rays.
272 
273   In fact, if \f$\vect{y}_1\f$ is the old generator that does not saturate
274   \f$\vect{c}\f$, \f$\vect{y}_2\f$ is the new one such that
275   \f[
276     \vect{y}_2 = \lambda \vect{y}_1 + \mu \vect{z}
277   \f]
278   and \f$\vect{c}_1\f$ is a previous constraint that \f$\vect{y}_1\f$
279   and \f$\vect{z}\f$ saturates, we can see
280   \f[
281     \langle \vect{c}_1, \vect{y}_2 \rangle
282     = \langle \vect{c}_1, (\lambda \vect{y}_1 + \mu \vect{z}) \rangle
283     = \lambda \langle \vect{c}_1, \vect{y}_1 \rangle
284        + \mu \langle \vect{c}_1, \vect{z} \rangle
285        = 0 + \mu \langle \vect{c}_1, \vect{z} \rangle
286        = \mu \langle \vect{c}_1, \vect{z} \rangle
287   \f]
288   and
289   \f[
290     \mu \langle \vect{c}_1, \vect{z} \rangle = 0.
291   \f]
292 
293   Proposition 1: Let \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ be distinct
294   rays of \f$P\f$.
295   Then the following statements are equivalent:
296   a) \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ are adjacent extreme rays
297      (see Section \ref prelims);
298   b) \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ are extreme rays and the
299      rank of the system composed by the constraints saturated by both
300      \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ is equal to
301      \f$d - 2\f$, where \f$d\f$ is the rank of the system of constraints.
302 
303   In fact, let \f$F\f$ be the system of generators that saturate the
304   constraints saturated by both \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$.
305   If b) holds, the set \f$F\f$ is 2-dimensional and \f$\vect{r}_1\f$ and
306   \f$\vect{r}_2\f$ generate this set. So, every generator
307   \f$\vect{x}\f$ of \f$F\f$ can be built as a combination of
308   \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$, i.e.
309   \f[
310     \vect{x} = \lambda \vect{r}_1 + \mu \vect{r}_2.
311   \f]
312   This combination is non-negative because there exists at least a
313   constraint \f$c\f$ saturated by \f$\vect{r}_1\f$ and not
314   \f$\vect{r}_2\f$ (or vice versa) (because they are distinct) for which
315   \f[
316     \langle \vect{c}, \vect{x} \rangle \geq 0
317   \f]
318   and
319   \f[
320     \langle \vect{c}, \vect{x} \rangle
321     = \lambda \langle \vect{c}, \vect{r}_1 \rangle
322                            (or = \mu \langle \vect{c}, \vect{r}_2 \rangle).
323   \f]
324   So, there is no other extreme ray in \f$F\f$ and a) holds.
325   Otherwise, if b) does not hold, the rank of the system generated by
326   the constraints saturated by both \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$
327   is equal to \f$d - k\f$, with \p k \>= 3, the set \f$F\f$ is
328   \p k -dimensional and at least \p k extreme rays are necessary
329   to generate \f$F\f$.
330   So, \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ are not adjacent and
331   a) does not hold.
332 
333   Proposition 2: When we build the new system of generators starting from
334   a system \f$A\f$ of constraints of \f$P\f$, if \f$\vect{c}\f$ is the
335   constraint to add to \f$A\f$ and all lines of \f$P\f$ saturate
336   \f$\vect{c}\f$, the new set of rays is the union of those rays that
337   saturate, of those that satisfy and of a set \f$\overline Q\f$ of
338   rays such that each of them
339   -# lies on the hyper-plane represented by the k-th constraint,
340   -# is a positive combination of two adjacent rays \f$\vect{r}_1\f$ and
341      \f$\vect{r}_2\f$ such that the first one satisfies the constraint and
342      the other does not satisfy it.
343   If the adjacency property is not taken in account, the new set of
344   rays is not irredundant, in general.
345 
346   In fact, if \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ are not adjacent,
347   the rank of the system composed by the constraints saturated by both
348   \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ is different from \f$d - 2\f$
349   (see the previous proposition) or neither \f$\vect{r}_1\f$ nor
350   \f$\vect{r}_2\f$ are extreme rays. Since the new ray \f$\vect{r}\f$
351   is a combination of \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$,
352   it saturates the same constraints saturated by both \f$\vect{r}_1\f$ and
353   \f$\vect{r}_2\f$.
354   If the rank is less than \f$d - 2\f$, the rank of
355   the system composed by \f$\vect{c}\f$ (that is saturated by \f$\vect{r}\f$)
356   and by the constraints of \f$A\f$ saturated by \f$\vect{r}\f$  is less
357   than \f$d - 1\f$. It means that \f$r\f$ is redundant (see
358   Section \ref prelims).
359   If neither \f$\vect{r}_1\f$ nor \f$\vect{r}_2\f$ are extreme rays,
360   they belong to a 2-dimensional face containing exactly two extreme rays
361   of \f$P\f$.
362   These two adjacent rays build a ray equal to \f$\vect{r}\f$ and so
363   \f$\vect{r}\f$ is redundant.
364 
365   \endif
366 */
367 template <typename Source_Linear_System, typename Dest_Linear_System>
368 dimension_type
conversion(Source_Linear_System & source,const dimension_type start,Dest_Linear_System & dest,Bit_Matrix & sat,dimension_type num_lines_or_equalities)369 Polyhedron::conversion(Source_Linear_System& source,
370                        const dimension_type start,
371                        Dest_Linear_System& dest,
372                        Bit_Matrix& sat,
373                        dimension_type num_lines_or_equalities) {
374   typedef typename Dest_Linear_System::row_type dest_row_type;
375   typedef typename Source_Linear_System::row_type source_row_type;
376 
377   // Constraints and generators must have the same dimension,
378   // otherwise the scalar products below will bomb.
379   PPL_ASSERT(source.space_dimension() == dest.space_dimension());
380   const dimension_type source_space_dim = source.space_dimension();
381   const dimension_type source_num_rows = source.num_rows();
382   const dimension_type source_num_columns = source_space_dim
383     + (source.is_necessarily_closed() ? 1U : 2U);
384 
385 
386   dimension_type dest_num_rows = dest.num_rows();
387   // The rows removed from `dest' will be placed in this vector, so they
388   // can be recycled if needed.
389   std::vector<dest_row_type> recyclable_dest_rows;
390 
391   using std::swap;
392 
393   // By construction, the number of columns of `sat' is the same as
394   // the number of rows of `source'; also, the number of rows of `sat'
395   // is the same as the number of rows of `dest'.
396   PPL_ASSERT(source_num_rows == sat.num_columns());
397   PPL_ASSERT(dest_num_rows == sat.num_rows());
398 
399   // If `start > 0', then we are converting the pending constraints.
400   PPL_ASSERT(start == 0 || start == source.first_pending_row());
401 
402   PPL_DIRTY_TEMP_COEFFICIENT(normalized_sp_i);
403   PPL_DIRTY_TEMP_COEFFICIENT(normalized_sp_o);
404 
405   bool dest_sorted = dest.is_sorted();
406   const dimension_type dest_first_pending_row = dest.first_pending_row();
407 
408   // This will contain the row indexes of the redundant rows of `source'.
409   std::vector<dimension_type> redundant_source_rows;
410 
411 #if PPL_QUICK_ADJ_TEST
412   // This will contain the number of ones in each row of `sat'.
413   PPL_DIRTY_TEMP(std::vector<dimension_type>, sat_num_ones);
414   sat_num_ones.resize(dest_num_rows, 0);
415   for (dimension_type i = dest_num_rows; i-- > 0; ) {
416     sat_num_ones[i] = sat[i].count_ones();
417   }
418 #endif // PPL_QUICK_ADJ_TEST
419 
420   // Converting the sub-system of `source' having rows with indexes
421   // from `start' to the last one (i.e., `source_num_rows' - 1).
422   for (dimension_type k = start; k < source_num_rows; ++k) {
423     const source_row_type& source_k = source[k];
424 
425 #ifndef NDEBUG
426 #if PPL_QUICK_ADJ_TEST
427     for (dimension_type i = dest_num_rows; i-- > 0; ) {
428       PPL_ASSERT(sat_num_ones[i] == sat[i].count_ones());
429     }
430 #endif // PPL_QUICK_ADJ_TEST
431 #endif // NDEBUG
432 
433     // `scalar_prod[i]' will contain the scalar product of the
434     // constraint `source_k' and the generator `dest_rows[i]'.  This
435     // product is 0 if and only if the generator saturates the
436     // constraint.
437     PPL_DIRTY_TEMP(std::vector<Coefficient>, scalar_prod);
438     if (dest_num_rows > scalar_prod.size()) {
439       scalar_prod.insert(scalar_prod.end(),
440                          dest_num_rows - scalar_prod.size(),
441                          Coefficient_zero());
442     }
443     // `index_non_zero' will indicate the first generator in `dest_rows'
444     // that does not saturate the constraint `source_k'.
445     dimension_type index_non_zero = 0;
446     for ( ; index_non_zero < dest_num_rows; ++index_non_zero) {
447       WEIGHT_BEGIN();
448       Scalar_Products::assign(scalar_prod[index_non_zero],
449                               source_k,
450                               dest.sys.rows[index_non_zero]);
451       WEIGHT_ADD_MUL(17, source_space_dim);
452       if (scalar_prod[index_non_zero] != 0) {
453         // The generator does not saturate the constraint.
454         break;
455       }
456       // Check if the client has requested abandoning all expensive
457       // computations.  If so, the exception specified by the client
458       // is thrown now.
459       maybe_abandon();
460     }
461     for (dimension_type i = index_non_zero + 1; i < dest_num_rows; ++i) {
462       WEIGHT_BEGIN();
463       Scalar_Products::assign(scalar_prod[i], source_k, dest.sys.rows[i]);
464       WEIGHT_ADD_MUL(25, source_space_dim);
465       // Check if the client has requested abandoning all expensive
466       // computations.  If so, the exception specified by the client
467       // is thrown now.
468       maybe_abandon();
469     }
470 
471     // We first treat the case when `index_non_zero' is less than
472     // `num_lines_or_equalities', i.e., when the generator that
473     // does not saturate the constraint `source_k' is a line.
474     // The other case (described later) is when all the lines
475     // in `dest_rows' (i.e., all the rows having indexes less than
476     // `num_lines_or_equalities') do saturate the constraint.
477 
478     if (index_non_zero < num_lines_or_equalities) {
479       // Since the generator `dest_rows[index_non_zero]' does not saturate
480       // the constraint `source_k', it can no longer be a line
481       // (see saturation rule in Section \ref prelims).
482       // Therefore, we first transform it to a ray.
483       dest.sys.rows[index_non_zero].set_is_ray_or_point_or_inequality();
484       // Of the two possible choices, we select the ray satisfying
485       // the constraint (namely, the ray whose scalar product
486       // with the constraint gives a positive result).
487       if (scalar_prod[index_non_zero] < 0) {
488         // The ray `dest_rows[index_non_zero]' lies on the wrong half-space:
489         // we change it to have the opposite direction.
490         neg_assign(scalar_prod[index_non_zero]);
491         neg_assign(dest.sys.rows[index_non_zero].expr);
492         // The modified row may still not be OK(), so don't assert OK here.
493         // They are all checked at the end of this function.
494       }
495       // Having changed a line to a ray, we set `dest_rows' to be a
496       // non-sorted system, we decrement the number of lines of `dest_rows'
497       // and, if necessary, we move the new ray below all the remaining lines.
498       dest_sorted = false;
499       --num_lines_or_equalities;
500       if (index_non_zero != num_lines_or_equalities) {
501         swap(dest.sys.rows[index_non_zero],
502              dest.sys.rows[num_lines_or_equalities]);
503         swap(scalar_prod[index_non_zero],
504              scalar_prod[num_lines_or_equalities]);
505       }
506       const dest_row_type& dest_nle = dest.sys.rows[num_lines_or_equalities];
507 
508       // Computing the new lineality space.
509       // Since each line must lie on the hyper-plane corresponding to
510       // the constraint `source_k', the scalar product between
511       // the line and the constraint must be 0.
512       // This property already holds for the lines having indexes
513       // between 0 and `index_non_zero' - 1.
514       // We have to consider the remaining lines, having indexes
515       // between `index_non_zero' and `num_lines_or_equalities' - 1.
516       // Each line that does not saturate the constraint has to be
517       // linearly combined with generator `dest_nle' so that the
518       // resulting new line saturates the constraint.
519       // Note that, by Observation 1 above, the resulting new line
520       // will still saturate all the constraints that were saturated by
521       // the old line.
522 
523       Coefficient& scalar_prod_nle = scalar_prod[num_lines_or_equalities];
524       PPL_ASSERT(scalar_prod_nle != 0);
525       for (dimension_type
526              i = index_non_zero; i < num_lines_or_equalities; ++i) {
527         if (scalar_prod[i] != 0) {
528           // The following fragment optimizes the computation of
529           //
530           // <CODE>
531           //   Coefficient scale = scalar_prod[i];
532           //   scale.gcd_assign(scalar_prod_nle);
533           //   Coefficient normalized_sp_i = scalar_prod[i] / scale;
534           //   Coefficient normalized_sp_n = scalar_prod_nle / scale;
535           //   for (dimension_type c = dest_num_columns; c-- > 0; ) {
536           //     dest[i][c] *= normalized_sp_n;
537           //     dest[i][c] -= normalized_sp_i * dest_nle[c];
538           //   }
539           // </CODE>
540           normalize2(scalar_prod[i],
541                      scalar_prod_nle,
542                      normalized_sp_i,
543                      normalized_sp_o);
544           dest_row_type& dest_i = dest.sys.rows[i];
545           neg_assign(normalized_sp_i);
546           dest_i.expr.linear_combine(dest_nle.expr,
547                                      normalized_sp_o, normalized_sp_i);
548           dest_i.strong_normalize();
549           // The modified row may still not be OK(), so don't assert OK here.
550           // They are all checked at the end of this function.
551           scalar_prod[i] = 0;
552           // dest_sorted has already been set to false.
553         }
554       }
555 
556       // Computing the new pointed cone.
557       // Similarly to what we have done during the computation of
558       // the lineality space, we consider all the remaining rays
559       // (having indexes strictly greater than `num_lines_or_equalities')
560       // that do not saturate the constraint `source_k'. These rays
561       // are positively combined with the ray `dest_nle' so that the
562       // resulting new rays saturate the constraint.
563       for (dimension_type
564              i = num_lines_or_equalities + 1; i < dest_num_rows; ++i) {
565         if (scalar_prod[i] != 0) {
566           // The following fragment optimizes the computation of
567           //
568           // <CODE>
569           //   Coefficient scale = scalar_prod[i];
570           //   scale.gcd_assign(scalar_prod_nle);
571           //   Coefficient normalized_sp_i = scalar_prod[i] / scale;
572           //   Coefficient normalized_sp_n = scalar_prod_nle / scale;
573           //   for (dimension_type c = dest_num_columns; c-- > 0; ) {
574           //     dest[i][c] *= normalized_sp_n;
575           //     dest[i][c] -= normalized_sp_i * dest_nle[c];
576           //   }
577           // </CODE>
578           normalize2(scalar_prod[i],
579                      scalar_prod_nle,
580                      normalized_sp_i,
581                      normalized_sp_o);
582           dest_row_type& dest_i = dest.sys.rows[i];
583           WEIGHT_BEGIN();
584           neg_assign(normalized_sp_i);
585           dest_i.expr.linear_combine(dest_nle.expr,
586                                      normalized_sp_o, normalized_sp_i);
587           dest_i.strong_normalize();
588           // The modified row may still not be OK(), so don't assert OK here.
589           // They are all checked at the end of this function.
590           scalar_prod[i] = 0;
591           // `dest_sorted' has already been set to false.
592           WEIGHT_ADD_MUL(41, source_space_dim);
593         }
594         // Check if the client has requested abandoning all expensive
595         // computations.  If so, the exception specified by the client
596         // is thrown now.
597         maybe_abandon();
598       }
599       // Since the `scalar_prod_nle' is positive (by construction), it
600       // does not saturate the constraint `source_k'.  Therefore, if
601       // the constraint is an inequality, we set to 1 the
602       // corresponding element of `sat' ...
603       Bit_Row& sat_nle = sat[num_lines_or_equalities];
604       if (source_k.is_ray_or_point_or_inequality()) {
605         sat_nle.set(k - redundant_source_rows.size());
606 #if PPL_QUICK_ADJ_TEST
607         ++sat_num_ones[num_lines_or_equalities];
608 #endif // PPL_QUICK_ADJ_TEST
609       }
610       else {
611         // ... otherwise, the constraint is an equality which is
612         // violated by the generator `dest_nle': the generator has to be
613         // removed from `dest_rows'.
614         --dest_num_rows;
615         swap(dest.sys.rows[num_lines_or_equalities],
616              dest.sys.rows[dest_num_rows]);
617         recyclable_dest_rows.resize(recyclable_dest_rows.size() + 1);
618         swap(dest.sys.rows.back(), recyclable_dest_rows.back());
619         dest.sys.rows.pop_back();
620         PPL_ASSERT(dest_num_rows == dest.sys.rows.size());
621 
622         swap(scalar_prod_nle, scalar_prod[dest_num_rows]);
623         swap(sat_nle, sat[dest_num_rows]);
624 #if PPL_QUICK_ADJ_TEST
625         swap(sat_num_ones[num_lines_or_equalities],
626              sat_num_ones[dest_num_rows]);
627 #endif // PPL_QUICK_ADJ_TEST
628         // dest_sorted has already been set to false.
629       }
630       // Finished handling the line or equality case:
631       // continue with next `k'.
632       continue;
633     }
634 
635     // Here all the lines in `dest_rows' saturate the constraint `source_k'.
636     PPL_ASSERT(index_non_zero >= num_lines_or_equalities);
637     // First, we reorder the generators in `dest_rows' as follows:
638     // -# all the lines should have indexes between 0 and
639     //    `num_lines_or_equalities' - 1 (this already holds);
640     // -# all the rays that saturate the constraint should have
641     //    indexes between `num_lines_or_equalities' and
642     //    `lines_or_equal_bound' - 1; these rays form the set Q=.
643     // -# all the rays that have a positive scalar product with the
644     //    constraint should have indexes between `lines_or_equal_bound'
645     //    and `sup_bound' - 1; these rays form the set Q+.
646     // -# all the rays that have a negative scalar product with the
647     //    constraint should have indexes between `sup_bound' and
648     //    `dest_num_rows' - 1; these rays form the set Q-.
649     dimension_type lines_or_equal_bound = num_lines_or_equalities;
650     dimension_type inf_bound = dest_num_rows;
651     // While we find saturating generators, we simply increment
652     // `lines_or_equal_bound'.
653     while (inf_bound > lines_or_equal_bound
654            && scalar_prod[lines_or_equal_bound] == 0) {
655       ++lines_or_equal_bound;
656     }
657     dimension_type sup_bound = lines_or_equal_bound;
658     while (inf_bound > sup_bound) {
659       const int sp_sign = sgn(scalar_prod[sup_bound]);
660       if (sp_sign == 0) {
661         // This generator has to be moved in Q=.
662         swap(dest.sys.rows[sup_bound], dest.sys.rows[lines_or_equal_bound]);
663         swap(scalar_prod[sup_bound], scalar_prod[lines_or_equal_bound]);
664         swap(sat[sup_bound], sat[lines_or_equal_bound]);
665 #if PPL_QUICK_ADJ_TEST
666         swap(sat_num_ones[sup_bound], sat_num_ones[lines_or_equal_bound]);
667 #endif // PPL_QUICK_ADJ_TEST
668         ++lines_or_equal_bound;
669         ++sup_bound;
670         dest_sorted = false;
671       }
672       else if (sp_sign < 0) {
673         // This generator has to be moved in Q-.
674         --inf_bound;
675         swap(dest.sys.rows[sup_bound], dest.sys.rows[inf_bound]);
676         swap(sat[sup_bound], sat[inf_bound]);
677         swap(scalar_prod[sup_bound], scalar_prod[inf_bound]);
678 #if PPL_QUICK_ADJ_TEST
679         swap(sat_num_ones[sup_bound], sat_num_ones[inf_bound]);
680 #endif // PPL_QUICK_ADJ_TEST
681         dest_sorted = false;
682       }
683       else {
684         // sp_sign > 0: this generator has to be moved in Q+.
685         ++sup_bound;
686       }
687     }
688 
689     if (sup_bound == dest_num_rows) {
690       // Here the set Q- is empty.
691       // If the constraint is an inequality, then all the generators
692       // in Q= and Q+ satisfy the constraint. The constraint is redundant
693       // and it can be safely removed from the constraint system.
694       // This is why the `source' parameter is not declared `const'.
695       if (source_k.is_ray_or_point_or_inequality()) {
696         redundant_source_rows.push_back(k);
697       }
698       else {
699         // The constraint is an equality, so that all the generators
700         // in Q+ violate it. Since the set Q- is empty, we can simply
701         // remove from `dest_rows' all the generators of Q+.
702         PPL_ASSERT(dest_num_rows >= lines_or_equal_bound);
703         while (dest_num_rows != lines_or_equal_bound) {
704           recyclable_dest_rows.resize(recyclable_dest_rows.size() + 1);
705           swap(dest.sys.rows.back(), recyclable_dest_rows.back());
706           dest.sys.rows.pop_back();
707           --dest_num_rows;
708           }
709         PPL_ASSERT(dest_num_rows == dest.sys.rows.size());
710       }
711       // Finished handling the case when Q- is empty:
712       // continue with next `k'.
713       continue;
714     }
715 
716     // The set Q- is not empty, i.e., at least one generator
717     // violates the constraint `source_k'.
718     if (sup_bound == num_lines_or_equalities) {
719       // The set Q+ is empty, so that all generators that satisfy
720       // the constraint also saturate it.
721       // We can simply remove from `dest_rows' all the generators in Q-.
722       PPL_ASSERT(dest_num_rows >= sup_bound);
723       while (dest_num_rows != sup_bound) {
724         recyclable_dest_rows.resize(recyclable_dest_rows.size() + 1);
725         swap(dest.sys.rows.back(), recyclable_dest_rows.back());
726         dest.sys.rows.pop_back();
727         --dest_num_rows;
728       }
729       PPL_ASSERT(dest_num_rows == dest.sys.rows.size());
730       // Finished handling the case when Q+ is empty:
731       // continue with next `k'.
732       continue;
733     }
734 
735     // The sets Q+ and Q- are both non-empty.
736     // The generators of the new pointed cone are all those satisfying
737     // the constraint `source_k' plus a set of new rays enjoying
738     // the following properties:
739     // -# they lie on the hyper-plane represented by the constraint
740     // -# they are obtained as a positive combination of two
741     //    adjacent rays, the first taken from Q+ and the second
742     //    taken from Q-.
743 
744     const dimension_type bound = dest_num_rows;
745 
746 #if PPL_QUICK_NON_ADJ_TEST
747     // For the quick non-adjacency test, we refer to the definition
748     // of a minimal proper face (see comments in Polyhedron_defs.hh):
749     // an extremal ray saturates at least `n' - `t' - 1 constraints,
750     // where `n' is the dimension of the space and `t' is the dimension
751     // of the lineality space. Since `n == source_num_columns - 1' and
752     // `t == num_lines_or_equalities', we obtain that an extremal ray
753     // saturates at least `source_num_columns - num_lines_or_equalities - 2'
754     // constraints.
755     const dimension_type min_saturators
756       = source_num_columns - num_lines_or_equalities - 2;
757     // NOTE: we are treating the `k'-th constraint.
758     const dimension_type max_saturators = k - redundant_source_rows.size();
759 #endif // PPL_QUICK_NON_ADJ_TEST
760 
761     // In the following loop,
762     // `i' runs through the generators in the set Q+ and
763     // `j' runs through the generators in the set Q-.
764     for (dimension_type i = lines_or_equal_bound; i < sup_bound; ++i) {
765       for (dimension_type j = sup_bound; j < bound; ++j) {
766         // Checking if generators `dest_rows[i]' and `dest_rows[j]' are
767         // adjacent.
768         // If there exist another generator that saturates
769         // all the constraints saturated by both `dest_rows[i]' and
770         // `dest_rows[j]', then they are NOT adjacent.
771         PPL_ASSERT(sat[i].last() == C_Integer<unsigned long>::max
772                    || sat[i].last() < k);
773         PPL_ASSERT(sat[j].last() == C_Integer<unsigned long>::max
774                    || sat[j].last() < k);
775 
776         // Being the union of `sat[i]' and `sat[j]',
777         // `new_satrow' corresponds to a ray that saturates all the
778         // constraints saturated by both `dest_rows[i]' and
779         // `dest_rows[j]'.
780         Bit_Row new_satrow(sat[i], sat[j]);
781 
782         // Even before actually creating the new ray as a
783         // positive combination of `dest_rows[i]' and `dest_rows[j]',
784         // we exploit saturation information to perform:
785         //  - a quick non-adjacency test;
786         //  - a quick adjacency test.
787 
788 #if (PPL_QUICK_NON_ADJ_TEST || PPL_QUICK_ADJ_TEST)
789         // Compute the number of common saturators.
790         dimension_type new_satrow_ones = new_satrow.count_ones();
791 #endif // (PPL_QUICK_NON_ADJ_TEST || PPL_QUICK_ADJ_TEST)
792 
793 #if PPL_QUICK_NON_ADJ_TEST
794         const dimension_type num_common_satur
795           = max_saturators - new_satrow_ones;
796         if (num_common_satur < min_saturators) {
797           // Quick non-adjacency test succeded: consider next `j'.
798           continue;
799         }
800 #endif // PPL_QUICK_NON_ADJ_TEST
801 
802 #if PPL_QUICK_ADJ_TEST
803         // If either `sat[i]' or `sat[j]' has exactly one more zeroes
804         // than `new_satrow', then `dest_rows[i]' and `dest_rows[j]'
805         // are adjacent. Equivalently, adjacency holds if `new_satrow_ones'
806         // is equal to 1 plus the maximum of `sat_num_ones[i]' and
807         // `sat_num_ones[j]'.
808         const dimension_type max_ones_i_j
809           = std::max(sat_num_ones[i], sat_num_ones[j]);
810         if (max_ones_i_j + 1 == new_satrow_ones) {
811           // Quick adjacency test succeded: skip the full test.
812           goto are_adjacent;
813         }
814 #endif // PPL_QUICK_ADJ_TEST
815 
816         // Perform the full (combinatorial) adjacency test.
817         {
818           bool redundant = false;
819           WEIGHT_BEGIN();
820           for (dimension_type l = num_lines_or_equalities; l < bound; ++l) {
821             if (l != i && l != j
822                 && subset_or_equal(sat[l], new_satrow)) {
823               // Found another generator saturating all the constraints
824               // saturated by both `dest_rows[i]' and `dest_rows[j]'.
825               redundant = true;
826               break;
827             }
828           }
829           PPL_ASSERT(bound >= num_lines_or_equalities);
830           WEIGHT_ADD_MUL(15, bound - num_lines_or_equalities);
831           if (redundant) {
832             // Full non-adjacency test succeded: consider next `j'.
833             continue;
834           }
835         }
836 
837 #if PPL_QUICK_ADJ_TEST
838       are_adjacent:
839 #endif // PPL_QUICK_ADJ_TEST
840         // Adding the new ray to `dest_rows' and the corresponding
841         // saturation row to `sat'.
842         dest_row_type new_row;
843         if (recyclable_dest_rows.empty()) {
844           sat.add_recycled_row(new_satrow);
845 #if PPL_QUICK_ADJ_TEST
846           sat_num_ones.push_back(new_satrow_ones);
847 #endif // PPL_QUICK_ADJ_TEST
848         }
849         else {
850           swap(new_row, recyclable_dest_rows.back());
851           recyclable_dest_rows.pop_back();
852           new_row.set_space_dimension_no_ok(source_space_dim);
853           swap(sat[dest_num_rows], new_satrow);
854 #if PPL_QUICK_ADJ_TEST
855           swap(sat_num_ones[dest_num_rows], new_satrow_ones);
856 #endif // PPL_QUICK_ADJ_TEST
857         }
858 
859         // The following fragment optimizes the computation of
860         //
861         // <CODE>
862         //   Coefficient scale = scalar_prod[i];
863         //   scale.gcd_assign(scalar_prod[j]);
864         //   Coefficient normalized_sp_i = scalar_prod[i] / scale;
865         //   Coefficient normalized_sp_j = scalar_prod[j] / scale;
866         //   for (dimension_type c = dest_num_columns; c-- > 0; ) {
867         //     new_row[c] = normalized_sp_i * dest[j][c];
868         //     new_row[c] -= normalized_sp_j * dest[i][c];
869         //   }
870         // </CODE>
871         normalize2(scalar_prod[i],
872                    scalar_prod[j],
873                    normalized_sp_i,
874                    normalized_sp_o);
875         WEIGHT_BEGIN();
876 
877         neg_assign(normalized_sp_o);
878         new_row = dest.sys.rows[j];
879         // TODO: Check if the following assertions hold.
880         PPL_ASSERT(normalized_sp_i != 0);
881         PPL_ASSERT(normalized_sp_o != 0);
882         new_row.expr.linear_combine(dest.sys.rows[i].expr,
883                                     normalized_sp_i, normalized_sp_o);
884 
885         WEIGHT_ADD_MUL(86, source_space_dim);
886         new_row.strong_normalize();
887         // Don't assert new_row.OK() here, because it may fail if
888         // the parameter `dest' contained a row that wasn't ok.
889         // Since we added a new generator to `dest_rows',
890         // we also add a new element to `scalar_prod';
891         // by construction, the new ray lies on the hyper-plane
892         // represented by the constraint `source_k'.
893         // Thus, the added scalar product is 0.
894         PPL_ASSERT(scalar_prod.size() >= dest_num_rows);
895         if (scalar_prod.size() <= dest_num_rows) {
896           scalar_prod.push_back(Coefficient_zero());
897         }
898         else {
899           scalar_prod[dest_num_rows] = Coefficient_zero();
900         }
901         dest.sys.rows.resize(dest.sys.rows.size() + 1);
902         swap(dest.sys.rows.back(), new_row);
903         // Increment the number of generators.
904         ++dest_num_rows;
905       } // End of loop on `j'.
906       // Check if the client has requested abandoning all expensive
907       // computations.  If so, the exception specified by the client
908       // is thrown now.
909       maybe_abandon();
910     } // End of loop on `i'.
911     // Now we substitute the rays in Q- (i.e., the rays violating
912     // the constraint) with the newly added rays.
913     dimension_type j;
914     if (source_k.is_ray_or_point_or_inequality()) {
915       // The constraint is an inequality:
916       // the violating generators are those in Q-.
917       j = sup_bound;
918       // For all the generators in Q+, set to 1 the corresponding
919       // entry for the constraint `source_k' in the saturation matrix.
920 
921       // After the removal of redundant rows in `source', the k-th
922       // row will have index `new_k'.
923       const dimension_type new_k = k - redundant_source_rows.size();
924       for (dimension_type l = lines_or_equal_bound;
925            l < sup_bound; ++l) {
926         sat[l].set(new_k);
927 #if PPL_QUICK_ADJ_TEST
928         ++sat_num_ones[l];
929 #endif // PPL_PPL_QUICK_ADJ_TEST
930       }
931     }
932     else {
933       // The constraint is an equality:
934       // the violating generators are those in the union of Q+ and Q-.
935       j = lines_or_equal_bound;
936     }
937     // Swapping the newly added rays
938     // (index `i' running through `dest_num_rows - 1' down-to `bound')
939     // with the generators violating the constraint
940     // (index `j' running through `j' up-to `bound - 1').
941     dimension_type i = dest_num_rows;
942     while (j < bound && i > bound) {
943       --i;
944       swap(dest.sys.rows[i], dest.sys.rows[j]);
945       swap(scalar_prod[i], scalar_prod[j]);
946       swap(sat[i], sat[j]);
947 #if PPL_QUICK_ADJ_TEST
948       swap(sat_num_ones[i], sat_num_ones[j]);
949 #endif // PPL_QUICK_ADJ_TEST
950       ++j;
951       dest_sorted = false;
952     }
953     // Setting the number of generators in `dest':
954     // - if the number of generators violating the constraint
955     //   is less than or equal to the number of the newly added
956     //   generators, we assign `i' to `dest_num_rows' because
957     //   all generators above this index are significant;
958     // - otherwise, we assign `j' to `dest_num_rows' because
959     //   all generators below index `j-1' violates the constraint.
960     const dimension_type new_num_rows = (j == bound) ? i : j;
961     PPL_ASSERT(dest_num_rows >= new_num_rows);
962     while (dest_num_rows != new_num_rows) {
963       recyclable_dest_rows.resize(recyclable_dest_rows.size() + 1);
964       swap(dest.sys.rows.back(), recyclable_dest_rows.back());
965       dest.sys.rows.pop_back();
966       --dest_num_rows;
967     }
968     PPL_ASSERT(dest_num_rows == dest.sys.rows.size());
969   } // End of loop on `k'.
970 
971   // We may have identified some redundant constraints in `source',
972   // which have been swapped at the end of the system.
973   if (redundant_source_rows.size() > 0) {
974     source.remove_rows(redundant_source_rows);
975     sat.remove_trailing_columns(redundant_source_rows.size());
976   }
977 
978   // If `start == 0', then `source' was sorted and remained so.
979   // If otherwise `start > 0', then the two sub-system made by the
980   // non-pending rows and the pending rows, respectively, were both sorted.
981   // Thus, the overall system is sorted if and only if either
982   // `start == source_num_rows' (i.e., the second sub-system is empty)
983   // or the row ordering holds for the two rows at the boundary between
984   // the two sub-systems.
985   if (start > 0 && start < source.num_rows()) {
986     source.set_sorted(compare(source[start - 1], source[start]) <= 0);
987   }
988   // There are no longer pending constraints in `source'.
989   source.unset_pending_rows();
990 
991   // We may have identified some redundant rays in `dest_rows',
992   // which have been swapped into recyclable_dest_rows.
993   if (!recyclable_dest_rows.empty()) {
994     const dimension_type num_removed_rows = recyclable_dest_rows.size();
995     sat.remove_trailing_rows(num_removed_rows);
996   }
997   if (dest_sorted) {
998     // If the non-pending generators in `dest' are still declared to be
999     // sorted, then we have to also check for the sortedness of the
1000     // pending generators.
1001     for (dimension_type i = dest_first_pending_row;
1002          i < dest_num_rows; ++i) {
1003       if (compare(dest.sys.rows[i - 1], dest.sys.rows[i]) > 0) {
1004         dest_sorted = false;
1005         break;
1006       }
1007     }
1008   }
1009 #ifndef NDEBUG
1010   // The previous code can modify the rows' fields, exploiting the friendness.
1011   // Check that all rows are OK now.
1012   for (dimension_type i = dest.num_rows(); i-- > 0; ) {
1013     PPL_ASSERT(dest.sys.rows[i].OK());
1014   }
1015 #endif
1016 
1017   dest.sys.index_first_pending = dest.num_rows();
1018   dest.set_sorted(dest_sorted);
1019   PPL_ASSERT(dest.sys.OK());
1020 
1021   return num_lines_or_equalities;
1022 }
1023 
1024 } // namespace Parma_Polyhedra_Library
1025 
1026 #endif // !defined(PPL_Polyhedron_conversion_templates_hh)
1027