1 /* Partially_Reduced_Product class implementation:
2    non-inline template functions.
3    Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it>
4    Copyright (C) 2010-2016 BUGSENG srl (http://bugseng.com)
5 
6 This file is part of the Parma Polyhedra Library (PPL).
7 
8 The PPL is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 The PPL is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software Foundation,
20 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA.
21 
22 For the most up-to-date information see the Parma Polyhedra Library
23 site: http://bugseng.com/products/ppl/ . */
24 
25 #ifndef PPL_Partially_Reduced_Product_templates_hh
26 #define PPL_Partially_Reduced_Product_templates_hh 1
27 
28 #include "Grid_Generator_defs.hh"
29 #include "Grid_Generator_System_defs.hh"
30 #include "Grid_Generator_System_inlines.hh"
31 #include <algorithm>
32 #include <deque>
33 
34 namespace Parma_Polyhedra_Library {
35 
36 template <typename D1, typename D2, typename R>
37 void
38 Partially_Reduced_Product<D1, D2, R>
throw_space_dimension_overflow(const char * method,const char * reason)39 ::throw_space_dimension_overflow(const char* method,
40                                  const char* reason) {
41   std::ostringstream s;
42   s << "PPL::Partially_Reduced_Product::" << method << ":" << std::endl
43     << reason << ".";
44   throw std::length_error(s.str());
45 }
46 
47 template <typename D1, typename D2, typename R>
48 Constraint_System
constraints() const49 Partially_Reduced_Product<D1, D2, R>::constraints() const {
50   reduce();
51   Constraint_System cs = d2.constraints();
52   const Constraint_System& cs1 = d1.constraints();
53   for (Constraint_System::const_iterator i = cs1.begin(),
54          cs_end = cs1.end(); i != cs_end; ++i) {
55     cs.insert(*i);
56   }
57   return cs;
58 }
59 
60 template <typename D1, typename D2, typename R>
61 Constraint_System
minimized_constraints() const62 Partially_Reduced_Product<D1, D2, R>::minimized_constraints() const {
63   reduce();
64   Constraint_System cs = d2.constraints();
65   const Constraint_System& cs1 = d1.constraints();
66   for (Constraint_System::const_iterator i = cs1.begin(),
67          cs_end = cs1.end(); i != cs_end; ++i) {
68     cs.insert(*i);
69   }
70   if (cs.has_strict_inequalities()) {
71     NNC_Polyhedron ph(cs);
72     return ph.minimized_constraints();
73   }
74   else {
75     C_Polyhedron ph(cs);
76     return ph.minimized_constraints();
77   }
78 }
79 
80 template <typename D1, typename D2, typename R>
81 Congruence_System
congruences() const82 Partially_Reduced_Product<D1, D2, R>::congruences() const {
83   reduce();
84   Congruence_System cgs = d2.congruences();
85   const Congruence_System& cgs1 = d1.congruences();
86   for (Congruence_System::const_iterator i = cgs1.begin(),
87          cgs_end = cgs1.end(); i != cgs_end; ++i) {
88     cgs.insert(*i);
89   }
90   return cgs;
91 }
92 
93 template <typename D1, typename D2, typename R>
94 Congruence_System
minimized_congruences() const95 Partially_Reduced_Product<D1, D2, R>::minimized_congruences() const {
96   reduce();
97   Congruence_System cgs = d2.congruences();
98   const Congruence_System& cgs1 = d1.congruences();
99   for (Congruence_System::const_iterator i = cgs1.begin(),
100          cgs_end = cgs1.end(); i != cgs_end; ++i) {
101     cgs.insert(*i);
102   }
103   Grid gr(cgs);
104   return gr.minimized_congruences();
105 }
106 
107 template <typename D1, typename D2, typename R>
108 void
109 Partially_Reduced_Product<D1, D2, R>
add_recycled_constraints(Constraint_System & cs)110 ::add_recycled_constraints(Constraint_System& cs) {
111   if (d1.can_recycle_constraint_systems()) {
112     d2.refine_with_constraints(cs);
113     d1.add_recycled_constraints(cs);
114   }
115   else {
116     if (d2.can_recycle_constraint_systems()) {
117       d1.refine_with_constraints(cs);
118       d2.add_recycled_constraints(cs);
119     }
120     else {
121       d1.add_constraints(cs);
122       d2.add_constraints(cs);
123     }
124   }
125   clear_reduced_flag();
126 }
127 
128 template <typename D1, typename D2, typename R>
129 void
130 Partially_Reduced_Product<D1, D2, R>
add_recycled_congruences(Congruence_System & cgs)131 ::add_recycled_congruences(Congruence_System& cgs) {
132   if (d1.can_recycle_congruence_systems()) {
133     d2.refine_with_congruences(cgs);
134     d1.add_recycled_congruences(cgs);
135   }
136   else {
137     if (d2.can_recycle_congruence_systems()) {
138       d1.refine_with_congruences(cgs);
139       d2.add_recycled_congruences(cgs);
140     }
141     else {
142       d1.add_congruences(cgs);
143       d2.add_congruences(cgs);
144     }
145   }
146   clear_reduced_flag();
147 }
148 
149 template <typename D1, typename D2, typename R>
150 Poly_Gen_Relation
151 Partially_Reduced_Product<D1, D2, R>
relation_with(const Generator & g) const152 ::relation_with(const Generator& g) const {
153   reduce();
154   if (Poly_Gen_Relation::nothing() == d1.relation_with(g)
155       || Poly_Gen_Relation::nothing() == d2.relation_with(g)) {
156     return Poly_Gen_Relation::nothing();
157   }
158   else {
159     return Poly_Gen_Relation::subsumes();
160   }
161 }
162 
163 template <typename D1, typename D2, typename R>
164 Poly_Con_Relation
165 Partially_Reduced_Product<D1, D2, R>
relation_with(const Constraint & c) const166 ::relation_with(const Constraint& c) const {
167   reduce();
168   Poly_Con_Relation relation1 = d1.relation_with(c);
169   Poly_Con_Relation relation2 = d2.relation_with(c);
170 
171   Poly_Con_Relation result = Poly_Con_Relation::nothing();
172 
173   if (relation1.implies(Poly_Con_Relation::is_included())) {
174     result = result && Poly_Con_Relation::is_included();
175   }
176   else if (relation2.implies(Poly_Con_Relation::is_included())) {
177     result = result && Poly_Con_Relation::is_included();
178   }
179   if (relation1.implies(Poly_Con_Relation::saturates())) {
180     result = result && Poly_Con_Relation::saturates();
181   }
182   else if (relation2.implies(Poly_Con_Relation::saturates())) {
183     result = result && Poly_Con_Relation::saturates();
184   }
185   if (relation1.implies(Poly_Con_Relation::is_disjoint())) {
186     result = result && Poly_Con_Relation::is_disjoint();
187   }
188   else if (relation2.implies(Poly_Con_Relation::is_disjoint())) {
189     result = result && Poly_Con_Relation::is_disjoint();
190   }
191 
192   return result;
193 }
194 
195 template <typename D1, typename D2, typename R>
196 Poly_Con_Relation
197 Partially_Reduced_Product<D1, D2, R>
relation_with(const Congruence & cg) const198 ::relation_with(const Congruence& cg) const {
199   reduce();
200   Poly_Con_Relation relation1 = d1.relation_with(cg);
201   Poly_Con_Relation relation2 = d2.relation_with(cg);
202 
203   Poly_Con_Relation result = Poly_Con_Relation::nothing();
204 
205   if (relation1.implies(Poly_Con_Relation::is_included())) {
206     result = result && Poly_Con_Relation::is_included();
207   }
208   else if (relation2.implies(Poly_Con_Relation::is_included())) {
209     result = result && Poly_Con_Relation::is_included();
210   }
211   if (relation1.implies(Poly_Con_Relation::saturates())) {
212     result = result && Poly_Con_Relation::saturates();
213   }
214   else if (relation2.implies(Poly_Con_Relation::saturates())) {
215     result = result && Poly_Con_Relation::saturates();
216   }
217   if (relation1.implies(Poly_Con_Relation::is_disjoint())) {
218     result = result && Poly_Con_Relation::is_disjoint();
219   }
220   else if (relation2.implies(Poly_Con_Relation::is_disjoint())) {
221     result = result && Poly_Con_Relation::is_disjoint();
222   }
223 
224   return result;
225 }
226 
227 template <typename D1, typename D2, typename R>
228 bool
229 Partially_Reduced_Product<D1, D2, R>
maximize(const Linear_Expression & expr,Coefficient & sup_n,Coefficient & sup_d,bool & maximum) const230 ::maximize(const Linear_Expression& expr,
231            Coefficient& sup_n,
232            Coefficient& sup_d,
233            bool& maximum) const {
234   reduce();
235 
236   if (is_empty()) {
237     return false;
238   }
239 
240   PPL_DIRTY_TEMP_COEFFICIENT(sup1_n);
241   PPL_DIRTY_TEMP_COEFFICIENT(sup1_d);
242   PPL_DIRTY_TEMP_COEFFICIENT(sup2_n);
243   PPL_DIRTY_TEMP_COEFFICIENT(sup2_d);
244   bool maximum1;
245   bool maximum2;
246   bool r1 = d1.maximize(expr, sup1_n, sup1_d, maximum1);
247   bool r2 = d2.maximize(expr, sup2_n, sup2_d, maximum2);
248   // If neither is bounded from above, return false.
249   if (!r1 && !r2) {
250     return false;
251   }
252   // If only d2 is bounded from above, then use the values for d2.
253   if (!r1) {
254     sup_n = sup2_n;
255     sup_d = sup2_d;
256     maximum = maximum2;
257     return true;
258   }
259   // If only d1 is bounded from above, then use the values for d1.
260   if (!r2) {
261     sup_n = sup1_n;
262     sup_d = sup1_d;
263     maximum = maximum1;
264     return true;
265   }
266   // If both d1 and d2 are bounded from above, then use the minimum values.
267   if (sup2_d * sup1_n >= sup1_d * sup2_n) {
268     sup_n = sup1_n;
269     sup_d = sup1_d;
270     maximum = maximum1;
271   }
272   else {
273     sup_n = sup2_n;
274     sup_d = sup2_d;
275     maximum = maximum2;
276   }
277   return true;
278 }
279 
280 template <typename D1, typename D2, typename R>
281 bool
282 Partially_Reduced_Product<D1, D2, R>
minimize(const Linear_Expression & expr,Coefficient & inf_n,Coefficient & inf_d,bool & minimum) const283 ::minimize(const Linear_Expression& expr,
284            Coefficient& inf_n,
285            Coefficient& inf_d,
286            bool& minimum) const {
287   reduce();
288 
289   if (is_empty()) {
290     return false;
291   }
292   PPL_ASSERT(reduced);
293 
294   PPL_DIRTY_TEMP_COEFFICIENT(inf1_n);
295   PPL_DIRTY_TEMP_COEFFICIENT(inf1_d);
296   PPL_DIRTY_TEMP_COEFFICIENT(inf2_n);
297   PPL_DIRTY_TEMP_COEFFICIENT(inf2_d);
298   bool minimum1;
299   bool minimum2;
300   bool r1 = d1.minimize(expr, inf1_n, inf1_d, minimum1);
301   bool r2 = d2.minimize(expr, inf2_n, inf2_d, minimum2);
302   // If neither is bounded from below, return false.
303   if (!r1 && !r2) {
304     return false;
305   }
306   // If only d2 is bounded from below, then use the values for d2.
307   if (!r1) {
308     inf_n = inf2_n;
309     inf_d = inf2_d;
310     minimum = minimum2;
311     return true;
312   }
313   // If only d1 is bounded from below, then use the values for d1.
314   if (!r2) {
315     inf_n = inf1_n;
316     inf_d = inf1_d;
317     minimum = minimum1;
318     return true;
319   }
320   // If both d1 and d2 are bounded from below, then use the minimum values.
321   if (inf2_d * inf1_n <= inf1_d * inf2_n) {
322     inf_n = inf1_n;
323     inf_d = inf1_d;
324     minimum = minimum1;
325   }
326   else {
327     inf_n = inf2_n;
328     inf_d = inf2_d;
329     minimum = minimum2;
330   }
331   return true;
332 }
333 
334 template <typename D1, typename D2, typename R>
335 bool
336 Partially_Reduced_Product<D1, D2, R>
maximize(const Linear_Expression & expr,Coefficient & sup_n,Coefficient & sup_d,bool & maximum,Generator & g) const337 ::maximize(const Linear_Expression& expr,
338            Coefficient& sup_n,
339            Coefficient& sup_d,
340            bool& maximum,
341            Generator& g) const {
342   reduce();
343 
344   if (is_empty()) {
345     return false;
346   }
347   PPL_ASSERT(reduced);
348 
349   PPL_DIRTY_TEMP_COEFFICIENT(sup1_n);
350   PPL_DIRTY_TEMP_COEFFICIENT(sup1_d);
351   PPL_DIRTY_TEMP_COEFFICIENT(sup2_n);
352   PPL_DIRTY_TEMP_COEFFICIENT(sup2_d);
353   bool maximum1;
354   bool maximum2;
355   Generator g1(point());
356   Generator g2(point());
357   bool r1 = d1.maximize(expr, sup1_n, sup1_d, maximum1, g1);
358   bool r2 = d2.maximize(expr, sup2_n, sup2_d, maximum2, g2);
359   // If neither is bounded from above, return false.
360   if (!r1 && !r2) {
361     return false;
362   }
363   // If only d2 is bounded from above, then use the values for d2.
364   if (!r1) {
365     sup_n = sup2_n;
366     sup_d = sup2_d;
367     maximum = maximum2;
368     g = g2;
369     return true;
370   }
371   // If only d1 is bounded from above, then use the values for d1.
372   if (!r2) {
373     sup_n = sup1_n;
374     sup_d = sup1_d;
375     maximum = maximum1;
376     g = g1;
377     return true;
378   }
379   // If both d1 and d2 are bounded from above, then use the minimum values.
380   if (sup2_d * sup1_n >= sup1_d * sup2_n) {
381     sup_n = sup1_n;
382     sup_d = sup1_d;
383     maximum = maximum1;
384     g = g1;
385   }
386   else {
387     sup_n = sup2_n;
388     sup_d = sup2_d;
389     maximum = maximum2;
390     g = g2;
391   }
392   return true;
393 }
394 
395 template <typename D1, typename D2, typename R>
396 bool
397 Partially_Reduced_Product<D1, D2, R>
minimize(const Linear_Expression & expr,Coefficient & inf_n,Coefficient & inf_d,bool & minimum,Generator & g) const398 ::minimize(const Linear_Expression& expr,
399            Coefficient& inf_n,
400            Coefficient& inf_d,
401            bool& minimum,
402            Generator& g) const {
403   reduce();
404 
405   if (is_empty()) {
406     return false;
407   }
408   PPL_ASSERT(reduced);
409 
410   PPL_DIRTY_TEMP_COEFFICIENT(inf1_n);
411   PPL_DIRTY_TEMP_COEFFICIENT(inf1_d);
412   PPL_DIRTY_TEMP_COEFFICIENT(inf2_n);
413   PPL_DIRTY_TEMP_COEFFICIENT(inf2_d);
414   bool minimum1;
415   bool minimum2;
416   Generator g1(point());
417   Generator g2(point());
418   bool r1 = d1.minimize(expr, inf1_n, inf1_d, minimum1, g1);
419   bool r2 = d2.minimize(expr, inf2_n, inf2_d, minimum2, g2);
420   // If neither is bounded from below, return false.
421   if (!r1 && !r2) {
422     return false;
423   }
424   // If only d2 is bounded from below, then use the values for d2.
425   if (!r1) {
426     inf_n = inf2_n;
427     inf_d = inf2_d;
428     minimum = minimum2;
429     g = g2;
430     return true;
431   }
432   // If only d1 is bounded from below, then use the values for d1.
433   if (!r2) {
434     inf_n = inf1_n;
435     inf_d = inf1_d;
436     minimum = minimum1;
437     g = g1;
438     return true;
439   }
440   // If both d1 and d2 are bounded from below, then use the minimum values.
441   if (inf2_d * inf1_n <= inf1_d * inf2_n) {
442     inf_n = inf1_n;
443     inf_d = inf1_d;
444     minimum = minimum1;
445     g = g1;
446   }
447   else {
448     inf_n = inf2_n;
449     inf_d = inf2_d;
450     minimum = minimum2;
451     g = g2;
452   }
453   return true;
454 }
455 
456 template <typename D1, typename D2, typename R>
457 inline bool
OK() const458 Partially_Reduced_Product<D1, D2, R>::OK() const {
459   if (reduced) {
460     Partially_Reduced_Product<D1, D2, R> dp1 = *this;
461     Partially_Reduced_Product<D1, D2, R> dp2 = *this;
462     /* Force dp1 reduction */
463     dp1.clear_reduced_flag();
464     dp1.reduce();
465     if (dp1 != dp2) {
466       return false;
467     }
468   }
469   return d1.OK() && d2.OK();
470 }
471 
472 template <typename D1, typename D2, typename R>
473 bool
ascii_load(std::istream & s)474 Partially_Reduced_Product<D1, D2, R>::ascii_load(std::istream& s) {
475   const char yes = '+';
476   const char no = '-';
477   std::string str;
478   if (!(s >> str) || str != "Partially_Reduced_Product") {
479     return false;
480   }
481   if (!(s >> str)
482       || (str[0] != yes && str[0] != no)
483       || str.substr(1) != "reduced") {
484     return false;
485   }
486   reduced = (str[0] == yes);
487   if (!(s >> str) || str != "Domain") {
488     return false;
489   }
490   if (!(s >> str) || str != "1:") {
491     return false;
492   }
493   if (!d1.ascii_load(s)) {
494     return false;
495   }
496   if (!(s >> str) || str != "Domain") {
497     return false;
498   }
499   if (!(s >> str) || str != "2:") {
500     return false;
501   }
502   return d2.ascii_load(s);
503 }
504 
505 template <typename D1, typename D2>
product_reduce(D1 & d1,D2 & d2)506 void Smash_Reduction<D1, D2>::product_reduce(D1& d1, D2& d2) {
507   using std::swap;
508   if (d2.is_empty()) {
509     if (!d1.is_empty()) {
510       D1 new_d1(d1.space_dimension(), EMPTY);
511       swap(d1, new_d1);
512     }
513   }
514   else if (d1.is_empty()) {
515     D2 new_d2(d2.space_dimension(), EMPTY);
516     swap(d2, new_d2);
517   }
518 }
519 
520 template <typename D1, typename D2>
product_reduce(D1 & d1,D2 & d2)521 void Constraints_Reduction<D1, D2>::product_reduce(D1& d1, D2& d2) {
522   if (d1.is_empty() || d2.is_empty()) {
523     // If one of the components is empty, do the smash reduction and return.
524     Parma_Polyhedra_Library::Smash_Reduction<D1, D2> sr;
525     sr.product_reduce(d1, d2);
526     return;
527   }
528   else {
529     using std::swap;
530     dimension_type space_dim = d1.space_dimension();
531     d1.refine_with_constraints(d2.minimized_constraints());
532     if (d1.is_empty()) {
533       D2 new_d2(space_dim, EMPTY);
534       swap(d2, new_d2);
535       return;
536     }
537     d2.refine_with_constraints(d1.minimized_constraints());
538     if (d2.is_empty()) {
539       D1 new_d1(space_dim, EMPTY);
540       swap(d1, new_d1);
541     }
542   }
543 }
544 
545 /* Auxiliary procedure for the Congruences_Reduction() method.
546    If more than one hyperplane defined by congruence cg intersect
547    d2, then d1 and d2 are unchanged; if exactly one intersects d2, then
548    the corresponding equality is added to d1 and d2;
549    otherwise d1 and d2 are set empty. */
550 template <typename D1, typename D2>
shrink_to_congruence_no_check(D1 & d1,D2 & d2,const Congruence & cg)551 bool shrink_to_congruence_no_check(D1& d1, D2& d2, const Congruence& cg) {
552   // It is assumed that cg is a proper congruence.
553   PPL_ASSERT(cg.modulus() != 0);
554   // It is assumed that cg is satisfied by all points in d1.
555   PPL_ASSERT(d1.relation_with(cg) == Poly_Con_Relation::is_included());
556 
557   Linear_Expression e(cg.expression());
558 
559   // Find the maximum and minimum bounds for the domain element d with the
560   // linear expression e.
561   PPL_DIRTY_TEMP_COEFFICIENT(max_numer);
562   PPL_DIRTY_TEMP_COEFFICIENT(max_denom);
563   bool max_included;
564   PPL_DIRTY_TEMP_COEFFICIENT(min_numer);
565   PPL_DIRTY_TEMP_COEFFICIENT(min_denom);
566   if (d2.maximize(e, max_numer, max_denom, max_included)) {
567     bool min_included;
568     if (d2.minimize(e, min_numer, min_denom, min_included)) {
569       // Adjust values to allow for the denominators max_denom and min_denom.
570       max_numer *= min_denom;
571       min_numer *= max_denom;
572       PPL_DIRTY_TEMP_COEFFICIENT(denom);
573       PPL_DIRTY_TEMP_COEFFICIENT(mod);
574       denom = max_denom * min_denom;
575       mod = cg.modulus() * denom;
576       // If the difference between the maximum and minimum bounds is more than
577       // twice the modulus, then there will be two neighboring hyperplanes
578       // defined by cg that are intersected by the domain element d;
579       // there is no possible reduction in this case.
580       PPL_DIRTY_TEMP_COEFFICIENT(mod2);
581       mod2 = 2 * mod;
582       if (max_numer - min_numer < mod2
583           || (max_numer - min_numer == mod2 && (!max_included || !min_included)))
584         {
585           PPL_DIRTY_TEMP_COEFFICIENT(shrink_amount);
586           PPL_DIRTY_TEMP_COEFFICIENT(max_decreased);
587           PPL_DIRTY_TEMP_COEFFICIENT(min_increased);
588           // Find the amount by which the maximum value may be decreased.
589           shrink_amount = max_numer % mod;
590           if (!max_included && shrink_amount == 0) {
591             shrink_amount = mod;
592           }
593           if (shrink_amount < 0) {
594             shrink_amount += mod;
595           }
596           max_decreased = max_numer - shrink_amount;
597           // Find the amount by which the minimum value may be increased.
598           shrink_amount = min_numer % mod;
599           if (!min_included && shrink_amount == 0) {
600             shrink_amount = - mod;
601           }
602           if (shrink_amount > 0) {
603             shrink_amount -= mod;
604           }
605           min_increased = min_numer - shrink_amount;
606           if (max_decreased == min_increased) {
607             // The domain element d2 intersects exactly one hyperplane
608             // defined by cg, so add the equality to d1 and d2.
609             Constraint new_c(denom * e == min_increased);
610             d1.refine_with_constraint(new_c);
611             d2.refine_with_constraint(new_c);
612             return true;
613           }
614           else {
615             if (max_decreased < min_increased) {
616               using std::swap;
617               // In this case, d intersects no hyperplanes defined by cg,
618               // so set d to empty and return false.
619               D1 new_d1(d1.space_dimension(), EMPTY);
620               swap(d1, new_d1);
621               D2 new_d2(d2.space_dimension(), EMPTY);
622               swap(d2, new_d2);
623               return false;
624             }
625           }
626         }
627     }
628   }
629   return true;
630 }
631 
632 template <typename D1, typename D2>
633 void
product_reduce(D1 & d1,D2 & d2)634 Congruences_Reduction<D1, D2>::product_reduce(D1& d1, D2& d2) {
635   if (d1.is_empty() || d2.is_empty()) {
636     // If one of the components is empty, do the smash reduction and return.
637     Parma_Polyhedra_Library::Smash_Reduction<D1, D2> sr;
638     sr.product_reduce(d1, d2);
639     return;
640   }
641   // Use the congruences representing d1 to shrink both components.
642   const Congruence_System cgs1 = d1.minimized_congruences();
643   for (Congruence_System::const_iterator i = cgs1.begin(),
644          cgs_end = cgs1.end(); i != cgs_end; ++i) {
645     const Congruence& cg1 = *i;
646     if (cg1.is_equality()) {
647       d2.refine_with_congruence(cg1);
648     }
649     else {
650       if (!Parma_Polyhedra_Library::
651           shrink_to_congruence_no_check(d1, d2, cg1)) {
652         // The product is empty.
653         return;
654       }
655     }
656   }
657   // Use the congruences representing d2 to shrink both components.
658   const Congruence_System cgs2 = d2.minimized_congruences();
659   for (Congruence_System::const_iterator i = cgs2.begin(),
660          cgs_end = cgs2.end(); i != cgs_end; ++i) {
661     const Congruence& cg2 = *i;
662     if (cg2.is_equality()) {
663       d1.refine_with_congruence(cg2);
664     }
665     else {
666       if (!Parma_Polyhedra_Library::
667           shrink_to_congruence_no_check(d2, d1, cg2)) {
668         // The product is empty.
669         return;
670       }
671     }
672   }
673 }
674 
675 template <typename D1, typename D2>
676 void
product_reduce(D1 & d1,D2 & d2)677 Shape_Preserving_Reduction<D1, D2>::product_reduce(D1& d1, D2& d2) {
678   // First do the congruences reduction.
679   Parma_Polyhedra_Library::Congruences_Reduction<D1, D2> cgr;
680   cgr.product_reduce(d1, d2);
681   if (d1.is_empty()) {
682     return;
683   }
684 
685   PPL_DIRTY_TEMP_COEFFICIENT(freq_n);
686   PPL_DIRTY_TEMP_COEFFICIENT(freq_d);
687   PPL_DIRTY_TEMP_COEFFICIENT(val_n);
688   PPL_DIRTY_TEMP_COEFFICIENT(val_d);
689 
690   // Use the constraints representing d2.
691   Constraint_System cs = d2.minimized_constraints();
692   Constraint_System refining_cs;
693   for (Constraint_System::const_iterator i = cs.begin(),
694          cs_end = cs.end(); i != cs_end; ++i) {
695     const Constraint& c = *i;
696     if (c.is_equality()) {
697       continue;
698     }
699     // Check the frequency and value of the linear expression for
700     // the constraint `c'.
701     Linear_Expression le(c.expression());
702     if (!d1.frequency(le, freq_n, freq_d, val_n, val_d)) {
703       // Nothing to do.
704       continue;
705     }
706     if (val_n == 0) {
707       // Nothing to do.
708       continue;
709     }
710     // Adjust the value of the inhomogeneous term to satisfy
711     // the implied congruence.
712     if (val_n < 0) {
713       val_n = val_n*freq_d + val_d*freq_n;
714       val_d *= freq_d;
715     }
716     le *= val_d;
717     le -= val_n;
718     refining_cs.insert(le >= 0);
719   }
720   d2.refine_with_constraints(refining_cs);
721 
722   // Use the constraints representing d1.
723   cs = d1.minimized_constraints();
724   refining_cs.clear();
725   for (Constraint_System::const_iterator i = cs.begin(),
726          cs_end = cs.end(); i != cs_end; ++i) {
727     const Constraint& c = *i;
728     if (c.is_equality()) {
729       // Equalities already shared.
730       continue;
731     }
732     // Check the frequency and value of the linear expression for
733     // the constraint `c'.
734     Linear_Expression le(c.expression());
735     if (!d2.frequency(le, freq_n, freq_d, val_n, val_d)) {
736       // Nothing to do.
737       continue;
738     }
739     if (val_n == 0) {
740       // Nothing to do.
741       continue;
742     }
743     // Adjust the value of the inhomogeneous term to satisfy
744     // the implied congruence.
745     if (val_n < 0) {
746       val_n = val_n*freq_d + val_d*freq_n;
747       val_d *= freq_d;
748     }
749     le *= val_d;
750     le -= val_n;
751     refining_cs.insert(le >= 0);
752   }
753   d1.refine_with_constraints(refining_cs);
754 
755   // The reduction may have introduced additional equalities
756   // so these must be shared with the other component.
757   Parma_Polyhedra_Library::Constraints_Reduction<D1, D2> cr;
758   cr.product_reduce(d1, d2);
759 }
760 
761 } // namespace Parma_Polyhedra_Library
762 
763 #endif // !defined(PPL_Partially_Reduced_Product_templates_hh)
764