/* This is the header file of the Parma Polyhedra Library.
   Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it>
   Copyright (C) 2010-2016 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://bugseng.com/products/ppl/ . */

#ifndef PPL_ppl_hh
#define PPL_ppl_hh 1

#ifdef NDEBUG
# define PPL_SAVE_NDEBUG NDEBUG
# undef NDEBUG
#endif

#ifdef __STDC_LIMIT_MACROS
# define PPL_SAVE_STDC_LIMIT_MACROS __STDC_LIMIT_MACROS
#endif

/* Automatically generated from PPL source file ../ppl-config.h line 1. */
/* config.h.  Generated from config.h.in by configure.  */
/* config.h.in.  Generated from configure.ac by autoheader.  */


/* BEGIN ppl-config.h */

#ifndef PPL_ppl_config_h
#define PPL_ppl_config_h 1

/* Unique (nonzero) code for the IEEE 754 Single Precision
   floating point format.  */
#define PPL_FLOAT_IEEE754_SINGLE 1

/* Unique (nonzero) code for the IEEE 754 Double Precision
   floating point format.  */
#define PPL_FLOAT_IEEE754_DOUBLE 2

/* Unique (nonzero) code for the IEEE 754 Quad Precision
   floating point format.  */
#define PPL_FLOAT_IEEE754_QUAD 3

/* Unique (nonzero) code for the Intel Double-Extended
   floating point format.  */
#define PPL_FLOAT_INTEL_DOUBLE_EXTENDED 4


/* Define if building universal (internal helper macro) */
/* #undef AC_APPLE_UNIVERSAL_BUILD */

/* Defined if the C++compiler supports C++11 features. */
#define PPL_HAVE_CXX11 /**/

/* Define to 1 if you have the declaration of 'ffs', and to 0 if you don't. */
#define PPL_HAVE_DECL_FFS 1

/* Define to 1 if you have the declaration of 'fma', and to 0 if you don't. */
#define PPL_HAVE_DECL_FMA 1

/* Define to 1 if you have the declaration of 'fmaf', and to 0 if you don't.
   */
#define PPL_HAVE_DECL_FMAF 1

/* Define to 1 if you have the declaration of 'fmal', and to 0 if you don't.
   */
#define PPL_HAVE_DECL_FMAL 1

/* Define to 1 if you have the declaration of 'getenv', and to 0 if you don't.
   */
#define PPL_HAVE_DECL_GETENV 1

/* Define to 1 if you have the declaration of 'getrusage', and to 0 if you
   don't. */
#define PPL_HAVE_DECL_GETRUSAGE 1

/* Define to 1 if you have the declaration of 'rintf', and to 0 if you don't.
   */
#define PPL_HAVE_DECL_RINTF 1

/* Define to 1 if you have the declaration of 'rintl', and to 0 if you don't.
   */
#define PPL_HAVE_DECL_RINTL 1

/* Define to 1 if you have the declaration of 'RLIMIT_AS', and to 0 if you
   don't. */
#define PPL_HAVE_DECL_RLIMIT_AS 1

/* Define to 1 if you have the declaration of 'RLIMIT_DATA', and to 0 if you
   don't. */
#define PPL_HAVE_DECL_RLIMIT_DATA 1

/* Define to 1 if you have the declaration of 'RLIMIT_RSS', and to 0 if you
   don't. */
#define PPL_HAVE_DECL_RLIMIT_RSS 1

/* Define to 1 if you have the declaration of 'RLIMIT_VMEM', and to 0 if you
   don't. */
#define PPL_HAVE_DECL_RLIMIT_VMEM 0

/* Define to 1 if you have the declaration of 'setitimer', and to 0 if you
   don't. */
#define PPL_HAVE_DECL_SETITIMER 1

/* Define to 1 if you have the declaration of 'setrlimit', and to 0 if you
   don't. */
#define PPL_HAVE_DECL_SETRLIMIT 1

/* Define to 1 if you have the declaration of 'sigaction', and to 0 if you
   don't. */
#define PPL_HAVE_DECL_SIGACTION 1

/* Define to 1 if you have the declaration of 'strtod', and to 0 if you don't.
   */
#define PPL_HAVE_DECL_STRTOD 1

/* Define to 1 if you have the declaration of 'strtof', and to 0 if you don't.
   */
#define PPL_HAVE_DECL_STRTOF 1

/* Define to 1 if you have the declaration of 'strtold', and to 0 if you
   don't. */
#define PPL_HAVE_DECL_STRTOLD 1

/* Define to 1 if you have the declaration of 'strtoll', and to 0 if you
   don't. */
#define PPL_HAVE_DECL_STRTOLL 1

/* Define to 1 if you have the declaration of 'strtoull', and to 0 if you
   don't. */
#define PPL_HAVE_DECL_STRTOULL 1

/* Define to 1 if you have the <dlfcn.h> header file. */
#define PPL_HAVE_DLFCN_H 1

/* Define to 1 if you have the <fenv.h> header file. */
#define PPL_HAVE_FENV_H 1

/* Define to 1 if you have the <getopt.h> header file. */
#define PPL_HAVE_GETOPT_H 1

/* Define to 1 if you have the <glpk/glpk.h> header file. */
/* #undef PPL_HAVE_GLPK_GLPK_H */

/* Define to 1 if you have the <glpk.h> header file. */
#define PPL_HAVE_GLPK_H 1

/* Define to 1 if you have the <ieeefp.h> header file. */
/* #undef PPL_HAVE_IEEEFP_H */

/* Define to 1 if you have the <inttypes.h> header file. */
#define PPL_HAVE_INTTYPES_H 1

/* Define to 1 if the system has the type 'int_fast16_t'. */
#define PPL_HAVE_INT_FAST16_T 1

/* Define to 1 if the system has the type 'int_fast32_t'. */
#define PPL_HAVE_INT_FAST32_T 1

/* Define to 1 if the system has the type 'int_fast64_t'. */
#define PPL_HAVE_INT_FAST64_T 1

/* Define to 1 if the system has the type 'siginfo_t'. */
#define PPL_HAVE_SIGINFO_T 1

/* Define to 1 if you have the <signal.h> header file. */
#define PPL_HAVE_SIGNAL_H 1

/* Define to 1 if you have the <stdint.h> header file. */
#define PPL_HAVE_STDINT_H 1

/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1

/* Define to 1 if you have the <stdlib.h> header file. */
#define PPL_HAVE_STDLIB_H 1

/* Define to 1 if you have the <strings.h> header file. */
#define PPL_HAVE_STRINGS_H 1

/* Define to 1 if you have the <string.h> header file. */
#define PPL_HAVE_STRING_H 1

/* Define to 1 if you have the <sys/resource.h> header file. */
#define PPL_HAVE_SYS_RESOURCE_H 1

/* Define to 1 if you have the <sys/stat.h> header file. */
#define PPL_HAVE_SYS_STAT_H 1

/* Define to 1 if you have the <sys/time.h> header file. */
#define PPL_HAVE_SYS_TIME_H 1

/* Define to 1 if you have the <sys/types.h> header file. */
#define PPL_HAVE_SYS_TYPES_H 1

/* Define to 1 if the system has the type 'timeval'. */
#define PPL_HAVE_TIMEVAL 1

/* Define to 1 if typeof works with your compiler. */
#define PPL_HAVE_TYPEOF 1

/* Define to 1 if the system has the type 'uintptr_t'. */
#define PPL_HAVE_UINTPTR_T 1

/* Define to 1 if the system has the type 'uint_fast16_t'. */
#define PPL_HAVE_UINT_FAST16_T 1

/* Define to 1 if the system has the type 'uint_fast32_t'. */
#define PPL_HAVE_UINT_FAST32_T 1

/* Define to 1 if the system has the type 'uint_fast64_t'. */
#define PPL_HAVE_UINT_FAST64_T 1

/* Define to 1 if you have the <unistd.h> header file. */
#define PPL_HAVE_UNISTD_H 1

/* Define to 1 if '_mp_alloc' is a member of '__mpz_struct'. */
#define PPL_HAVE___MPZ_STRUCT__MP_ALLOC 1

/* Define to 1 if '_mp_d' is a member of '__mpz_struct'. */
#define PPL_HAVE___MPZ_STRUCT__MP_D 1

/* Define to 1 if '_mp_size' is a member of '__mpz_struct'. */
#define PPL_HAVE___MPZ_STRUCT__MP_SIZE 1

/* Define to the sub-directory where libtool stores uninstalled libraries. */
#define LT_OBJDIR ".libs/"

/* Define to the address where bug reports for this package should be sent. */
#define PPL_PACKAGE_BUGREPORT "ppl-devel@cs.unipr.it"

/* Define to the full name of this package. */
#define PPL_PACKAGE_NAME "the Parma Polyhedra Library"

/* Define to the full name and version of this package. */
#define PPL_PACKAGE_STRING "the Parma Polyhedra Library 1.2"

/* Define to the one symbol short name of this package. */
#define PPL_PACKAGE_TARNAME "ppl"

/* Define to the home page for this package. */
#define PACKAGE_URL ""

/* Define to the version of this package. */
#define PPL_PACKAGE_VERSION "1.2"

/* ABI-breaking extra assertions are enabled when this is defined. */
/* #undef PPL_ABI_BREAKING_EXTRA_DEBUG */

/* Not zero if the FPU can be controlled. */
#define PPL_CAN_CONTROL_FPU 1

/* Defined if the integral type to be used for coefficients is a checked one.
   */
/* #undef PPL_CHECKED_INTEGERS */

/* The number of bits of coefficients; 0 if unbounded. */
#define PPL_COEFFICIENT_BITS 0

/* The integral type used to represent coefficients. */
#define PPL_COEFFICIENT_TYPE mpz_class

/* This contains the options with which `configure' was invoked. */
#define PPL_CONFIGURE_OPTIONS " '--prefix=/usr' '--enable-interfaces=c,cxx,swi_prolog' 'CFLAGS=-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions         -Wp,-D_FORTIFY_SOURCE=3 -Wformat -Werror=format-security         -fstack-clash-protection -fcf-protection         -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -g -ffile-prefix-map=/build/ppl/src=/usr/src/debug/ppl -flto=auto' 'LDFLAGS=-Wl,-O1 -Wl,--sort-common -Wl,--as-needed -Wl,-z,relro -Wl,-z,now          -Wl,-z,pack-relative-relocs -flto=auto' 'CPPFLAGS=-I/usr/lib/swipl/include' 'CXXFLAGS=-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions         -Wp,-D_FORTIFY_SOURCE=3 -Wformat -Werror=format-security         -fstack-clash-protection -fcf-protection         -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -Wp,-D_GLIBCXX_ASSERTIONS -g -ffile-prefix-map=/build/ppl/src=/usr/src/debug/ppl -flto=auto'"

/* The unique code of the binary format of C++ doubles, if supported;
   undefined otherwise. */
#define PPL_CXX_DOUBLE_BINARY_FORMAT PPL_FLOAT_IEEE754_DOUBLE

/* The binary format of C++ floats, if supported; undefined otherwise. */
#define PPL_CXX_FLOAT_BINARY_FORMAT PPL_FLOAT_IEEE754_SINGLE

/* The unique code of the binary format of C++ long doubles, if supported;
   undefined otherwise. */
#define PPL_CXX_LONG_DOUBLE_BINARY_FORMAT PPL_FLOAT_INTEL_DOUBLE_EXTENDED

/* Not zero if the the plain char type is signed. */
#define PPL_CXX_PLAIN_CHAR_IS_SIGNED 1

/* Not zero if the C++ compiler provides long double numbers that have bigger
   range or precision than double. */
#define PPL_CXX_PROVIDES_PROPER_LONG_DOUBLE 1

/* Not zero if the C++ compiler supports __attribute__ ((weak)). */
#define PPL_CXX_SUPPORTS_ATTRIBUTE_WEAK 1

/* Not zero if the the IEEE inexact flag is supported in C++. */
#define PPL_CXX_SUPPORTS_IEEE_INEXACT_FLAG 1

/* Not zero if it is possible to limit memory using setrlimit(). */
#define PPL_CXX_SUPPORTS_LIMITING_MEMORY 0

/* Not zero if the C++ compiler supports zero_length arrays. */
#define PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS 1

/* Defined if floating point arithmetic may use the 387 unit. */
#define PPL_FPMATH_MAY_USE_387 1

/* Defined if floating point arithmetic may use the SSE instruction set. */
#define PPL_FPMATH_MAY_USE_SSE 1

/* Defined if GLPK provides glp_term_hook(). */
#define PPL_GLPK_HAS_GLP_TERM_HOOK 1

/* Defined if GLPK provides glp_term_out(). */
#define PPL_GLPK_HAS_GLP_TERM_OUT 1

/* Defined if GLPK provides lib_set_print_hook(). */
/* #undef PPL_GLPK_HAS_LIB_SET_PRINT_HOOK */

/* Defined if GLPK provides _glp_lib_print_hook(). */
/* #undef PPL_GLPK_HAS__GLP_LIB_PRINT_HOOK */

/* Defined if the integral type to be used for coefficients is GMP's one. */
#define PPL_GMP_INTEGERS 1

/* Not zero if GMP has been compiled with support for exceptions. */
#define PPL_GMP_SUPPORTS_EXCEPTIONS 1

/* Defined if the integral type to be used for coefficients is a native one.
   */
/* #undef PPL_NATIVE_INTEGERS */

/* Assertions are disabled when this is defined. */
#define PPL_NDEBUG 1

/* Not zero if doubles are supported. */
#define PPL_SUPPORTED_DOUBLE 1

/* Not zero if floats are supported. */
#define PPL_SUPPORTED_FLOAT 1

/* Not zero if long doubles are supported. */
#define PPL_SUPPORTED_LONG_DOUBLE 1

/* The size of 'char', as computed by sizeof. */
#define PPL_SIZEOF_CHAR 1

/* The size of 'double', as computed by sizeof. */
#define PPL_SIZEOF_DOUBLE 8

/* The size of 'float', as computed by sizeof. */
#define PPL_SIZEOF_FLOAT 4

/* The size of 'fp', as computed by sizeof. */
#define PPL_SIZEOF_FP 8

/* The size of 'int', as computed by sizeof. */
#define PPL_SIZEOF_INT 4

/* The size of 'int*', as computed by sizeof. */
#define PPL_SIZEOF_INTP 8

/* The size of 'long', as computed by sizeof. */
#define PPL_SIZEOF_LONG 8

/* The size of 'long double', as computed by sizeof. */
#define PPL_SIZEOF_LONG_DOUBLE 16

/* The size of 'long long', as computed by sizeof. */
#define PPL_SIZEOF_LONG_LONG 8

/* The size of 'mp_limb_t', as computed by sizeof. */
#define PPL_SIZEOF_MP_LIMB_T 8

/* The size of 'short', as computed by sizeof. */
#define PPL_SIZEOF_SHORT 2

/* The size of 'size_t', as computed by sizeof. */
#define PPL_SIZEOF_SIZE_T 8

/* Define to 1 if all of the C89 standard headers exist (not just the ones
   required in a freestanding environment). This macro is provided for
   backward compatibility; new code need not use it. */
#define PPL_STDC_HEADERS 1

/* Define PPL_WORDS_BIGENDIAN to 1 if your processor stores words with the most
   significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
#  define PPL_WORDS_BIGENDIAN 1
# endif
#else
# ifndef PPL_WORDS_BIGENDIAN
/* #  undef PPL_WORDS_BIGENDIAN */
# endif
#endif

/* When defined and libstdc++ is used, it is used in debug mode. */
/* #undef _GLIBCXX_DEBUG */

/* When defined and libstdc++ is used, it is used in pedantic debug mode. */
/* #undef _GLIBCXX_DEBUG_PEDANTIC */

/* Define to empty if 'const' does not conform to ANSI C. */
/* #undef const */

/* Define to '__inline__' or '__inline' if that's what the C compiler
   calls it, or to nothing if 'inline' is not supported under any name.  */
#ifndef __cplusplus
/* #undef inline */
#endif

/* Define to __typeof__ if your compiler spells it that way. */
/* #undef typeof */

/* Define to the type of an unsigned integer type wide enough to hold a
   pointer, if such a type exists, and if the system does not define it. */
/* #undef uintptr_t */


#if defined(PPL_NDEBUG) && !defined(NDEBUG)
# define NDEBUG PPL_NDEBUG
#endif

/* In order for the definition of `int64_t' to be seen by Comeau C/C++,
   we must make sure <stdint.h> is included before <sys/types.hh> is
   (even indirectly) included.  Moreover we need to define
   __STDC_LIMIT_MACROS before the first inclusion of <stdint.h>
   in order to have the macros defined also in C++.  */

#ifdef PPL_HAVE_STDINT_H
# ifndef __STDC_LIMIT_MACROS
#  define __STDC_LIMIT_MACROS 1
# endif
# include <stdint.h>
#endif

#ifdef PPL_HAVE_INTTYPES_H
# include <inttypes.h>
#endif

#define PPL_U(x) x

#endif /* !defined(PPL_ppl_config_h) */

/* END ppl-config.h */

/* Automatically generated from PPL source file ../src/version.hh line 1. */
/* Declaration of macros and functions providing version  -*- C++ -*-
   and licensing information.
*/


//! The major number of the PPL version.
/*! \ingroup PPL_CXX_interface */
#define PPL_VERSION_MAJOR 1

//! The minor number of the PPL version.
/*! \ingroup PPL_CXX_interface */
#define PPL_VERSION_MINOR 2

//! The revision number of the PPL version.
/*! \ingroup PPL_CXX_interface */
#define PPL_VERSION_REVISION 0

/*! \brief
  The beta number of the PPL version.  This is zero for official
  releases and nonzero for development snapshots.
  \ingroup PPL_CXX_interface
*/
#define PPL_VERSION_BETA 0

//! A string containing the PPL version.
/*! \ingroup PPL_CXX_interface
  Let <CODE>M</CODE> and <CODE>m</CODE> denote the numbers associated
  to PPL_VERSION_MAJOR and PPL_VERSION_MINOR, respectively.  The
  format of PPL_VERSION is <CODE>M "." m</CODE> if both
  PPL_VERSION_REVISION (<CODE>r</CODE>) and PPL_VERSION_BETA
  (<CODE>b</CODE>)are zero, <CODE>M "." m "pre" b</CODE> if
  PPL_VERSION_REVISION is zero and PPL_VERSION_BETA is not zero,
  <CODE>M "." m "." r</CODE> if PPL_VERSION_REVISION is not zero and
  PPL_VERSION_BETA is zero, <CODE>M "." m "." r "pre" b</CODE> if
  neither PPL_VERSION_REVISION nor PPL_VERSION_BETA are zero.
*/
#define PPL_VERSION "1.2"

namespace Parma_Polyhedra_Library {

//! \name Library Version Control Functions
//@{

//! Returns the major number of the PPL version.
unsigned
version_major();

//! Returns the minor number of the PPL version.
unsigned
version_minor();

//! Returns the revision number of the PPL version.
unsigned
version_revision();

//! Returns the beta number of the PPL version.
unsigned
version_beta();

//! Returns a character string containing the PPL version.
const char* version();

//! Returns a character string containing the PPL banner.
/*!
  The banner provides information about the PPL version, the licensing,
  the lack of any warranty whatsoever, the C++ compiler used to build
  the library, where to report bugs and where to look for further
  information.
*/
const char* banner();

//@} // Library Version Control Functions

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/namespaces.hh line 1. */
/* Documentation for used namespaces.
*/


//! The entire library is confined to this namespace.
namespace Parma_Polyhedra_Library {

//! All input/output operators are confined to this namespace.
/*! \ingroup PPL_CXX_interface
  This is done so that the library's input/output operators
  do not interfere with those the user might want to define.
  In fact, it is highly unlikely that any predefined I/O
  operator will suit the needs of a client application.
  On the other hand, those applications for which the PPL
  I/O operator are enough can easily obtain access to them.
  For example, a directive like
  \code
    using namespace Parma_Polyhedra_Library::IO_Operators;
  \endcode
  would suffice for most uses.
  In more complex situations, such as
  \code
    const Constraint_System& cs = ...;
    copy(cs.begin(), cs.end(),
         ostream_iterator<Constraint>(cout, "\n"));
  \endcode
  the Parma_Polyhedra_Library namespace must be suitably extended.
  This can be done as follows:
  \code
    namespace Parma_Polyhedra_Library {
      // Import all the output operators into the main PPL namespace.
      using IO_Operators::operator<<;
    }
  \endcode
*/
namespace IO_Operators {
} // namespace IO_Operators

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Types and functions implementing checked numbers.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace Checked {
} // namespace Checked

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! %Implementation related data and functions.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace Implementation {
} // namespace Implementation

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Data and functions related to language interfaces.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace Interfaces {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Data and functions related to the C language interface.
/*! \ingroup PPL_C_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace C {

} // namespace C

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Data and functions related to the Java language interface.
/*! \ingroup PPL_Java_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace Java {

} // namespace Java

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Data and functions related to the OCaml language interface.
/*! \ingroup PPL_OCaml_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace OCaml {

} // namespace OCaml

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Data and functions related to the Prolog language interfaces.
/*! \ingroup PPL_Prolog_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace Prolog {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Data and functions related to the Ciao Prolog language interface.
/*! \ingroup PPL_Prolog_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace Ciao {

} // namespace Ciao

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Data and functions related to the GNU Prolog language interface.
/*! \ingroup PPL_Prolog_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace GNU {

} // namespace GNU

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Data and functions related to the SICStus language interface.
/*! \ingroup PPL_Prolog_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace SICStus {

} // namespace SICStus

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Data and functions related to the SWI-Prolog language interface.
/*! \ingroup PPL_Prolog_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace SWI {

} // namespace SWI

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Data and functions related to the XSB language interface.
/*! \ingroup PPL_Prolog_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace XSB {

} // namespace XSB

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Data and functions related to the YAP language interface.
/*! \ingroup PPL_Prolog_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
namespace YAP {

} // namespace YAP

} // namespace Prolog

} // namespace Interfaces

} // namespace Parma_Polyhedra_Library


//! The standard C++ namespace.
/*! \ingroup PPL_CXX_interface
  The Parma Polyhedra Library conforms to the C++ standard and,
  in particular, as far as reserved names are concerned (17.4.3.1,
  [lib.reserved.names]).  The PPL, however, defines several
  template specializations for the standard library class template
  <CODE>numeric_limits</CODE> (18.2.1, [lib.limits]).

  \note
  The PPL provides the specializations of the class template
  <CODE>numeric_limits</CODE> not only for PPL-specific numeric types,
  but also for the GMP types <CODE>mpz_class</CODE> and
  <CODE>mpq_class</CODE>. These specializations will be removed
  as soon as they will be provided by the C++ interface of GMP.
*/
namespace std {
} // namespace std


/* Automatically generated from PPL source file ../src/Interval_Info_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename Policy>
class Interval_Info_Null;

template <typename T, typename Policy>
class Interval_Info_Bitset;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/checked_numeric_limits.hh line 1. */
/* Specializations of std::numeric_limits for "checked" types.
*/


/* Automatically generated from PPL source file ../src/Checked_Number_defs.hh line 1. */
/* Checked_Number class declaration.
*/


/* Automatically generated from PPL source file ../src/Checked_Number_types.hh line 1. */


/* Automatically generated from PPL source file ../src/Coefficient_traits_template.hh line 1. */
/* Coefficient_traits_template class declaration.
*/


namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Coefficient traits.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Coefficient>
struct Coefficient_traits_template {
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Checked_Number_types.hh line 17. */

namespace Parma_Polyhedra_Library {

struct Extended_Number_Policy;

template <typename T, typename Policy>
class Checked_Number;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/checked_defs.hh line 1. */
/* Abstract checked arithmetic function container.
*/


/* Automatically generated from PPL source file ../src/mp_std_bits_defs.hh line 1. */
/* Declarations of specializations of std:: objects for
   multi-precision types.
*/


#include <gmpxx.h>
#include <limits>

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void swap(mpz_class& x, mpz_class& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void swap(mpq_class& x, mpq_class& y);

#if __GNU_MP_VERSION < 5 \
  || (__GNU_MP_VERSION == 5 && __GNU_MP_VERSION_MINOR < 1)

namespace std {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Specialization of std::numeric_limits.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <>
class numeric_limits<mpz_class> {
private:
  typedef mpz_class Type;

public:
  static const bool is_specialized = true;
  static const int digits = 0;
  static const int digits10 = 0;
  static const bool is_signed = true;
  static const bool is_integer = true;
  static const bool is_exact = true;
  static const int radix = 2;
  static const int min_exponent = 0;
  static const int min_exponent10 = 0;
  static const int max_exponent = 0;
  static const int max_exponent10 = 0;
  static const bool has_infinity = false;
  static const bool has_quiet_NaN =  false;
  static const bool has_signaling_NaN = false;
  static const float_denorm_style has_denorm = denorm_absent;
  static const bool has_denorm_loss = false;
  static const bool is_iec559 = false;
  static const bool is_bounded = false;
  static const bool is_modulo = false;
  static const bool traps = false;
  static const bool tinyness_before = false;
  static const float_round_style round_style = round_toward_zero;

  static Type min() {
    return static_cast<Type>(0);
  }

  static Type max() {
    return static_cast<Type>(0);
  }

  static Type epsilon() {
    return static_cast<Type>(0);
  }

  static Type round_error() {
    return static_cast<Type>(0);
  }

  static Type infinity() {
    return static_cast<Type>(0);
  }

  static Type quiet_NaN() {
    return static_cast<Type>(0);
  }

  static Type denorm_min() {
    return static_cast<Type>(1);
  }
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Specialization of std::numeric_limits.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <>
class numeric_limits<mpq_class> {
private:
  typedef mpq_class Type;

public:
  static const bool is_specialized = true;
  static const int digits = 0;
  static const int digits10 = 0;
  static const bool is_signed = true;
  static const bool is_integer = false;
  static const bool is_exact = true;
  static const int radix = 2;
  static const int min_exponent = 0;
  static const int min_exponent10 = 0;
  static const int max_exponent = 0;
  static const int max_exponent10 = 0;
  static const bool has_infinity = false;
  static const bool has_quiet_NaN =  false;
  static const bool has_signaling_NaN = false;
  static const float_denorm_style has_denorm = denorm_absent;
  static const bool has_denorm_loss = false;
  static const bool is_iec559 = false;
  static const bool is_bounded = false;
  static const bool is_modulo = false;
  static const bool traps = false;
  static const bool tinyness_before = false;
  static const float_round_style round_style = round_toward_zero;

  static Type min() {
    return static_cast<Type>(0);
  }

  static Type max() {
    return static_cast<Type>(0);
  }

  static Type epsilon() {
    return static_cast<Type>(0);
  }

  static Type round_error() {
    return static_cast<Type>(0);
  }

  static Type infinity() {
    return static_cast<Type>(0);
  }

  static Type quiet_NaN() {
    return static_cast<Type>(0);
  }

  static Type denorm_min() {
    return static_cast<Type>(0);
  }
};

} // namespace std

#endif // __GNU_MP_VERSION < 5
       // || (__GNU_MP_VERSION == 5 && __GNU_MP_VERSION_MINOR < 1)

/* Automatically generated from PPL source file ../src/mp_std_bits_inlines.hh line 1. */
/* Definitions of specializations of std:: functions and methods for
   multi-precision types.
*/


inline void
swap(mpz_class& x, mpz_class& y) {
  mpz_swap(x.get_mpz_t(), y.get_mpz_t());
}

inline void
swap(mpq_class& x, mpq_class& y) {
  mpq_swap(x.get_mpq_t(), y.get_mpq_t());
}

/* Automatically generated from PPL source file ../src/mp_std_bits_defs.hh line 174. */

/* Automatically generated from PPL source file ../src/Temp_defs.hh line 1. */
/* Temp_* classes declarations.
*/


/* Automatically generated from PPL source file ../src/meta_programming.hh line 1. */
/* Metaprogramming utilities.
*/


#include <gmpxx.h>

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Declares a per-class constant of type <CODE>bool</CODE>, called \p name
  and with value \p value.

  \ingroup PPL_CXX_interface
  Differently from static constants, \p name needs not (and cannot) be
  defined (for static constants, the need for a further definition is
  mandated by Section 9.4.2/4 of the C++ standard).
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
#define const_bool_nodef(name, value)           \
  enum const_bool_value_ ## name { PPL_U(name) = (value) }

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Declares a per-class constant of type <CODE>int</CODE>, called \p name
  and with value \p value.

  \ingroup PPL_CXX_interface
  Differently from static constants, \p name needs not (and cannot) be
  defined (for static constants, the need for a further definition is
  mandated by Section 9.4.2/4 of the C++ standard).
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
#define const_int_nodef(name, value) \
  enum anonymous_enum_ ## name { PPL_U(name) = (value) }

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Declares a per-class constant of type \p type, called \p name
  and with value \p value.  The value of the constant is accessible
  by means of the syntax <CODE>name()</CODE>.

  \ingroup PPL_CXX_interface
  Differently from static constants, \p name needs not (and cannot) be
  defined (for static constants, the need for a further definition is
  mandated by Section 9.4.2/4 of the C++ standard).
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
#define const_value_nodef(type, name, value)    \
  static type PPL_U(name)() {                   \
    return (value);                             \
  }

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Declares a per-class constant of type \p type, called \p name
  and with value \p value.  A constant reference to the constant
  is accessible by means of the syntax <CODE>name()</CODE>.

  \ingroup PPL_CXX_interface
  Differently from static constants, \p name needs not (and cannot) be
  defined (for static constants, the need for a further definition is
  mandated by Section 9.4.2/4 of the C++ standard).
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
#define const_ref_nodef(type, name, value)                              \
  static const type& PPL_U(name)() {                                    \
    static type PPL_U(name) = (value);                                       \
    return (name);                                                      \
  }

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  A class that is only defined if \p b evaluates to <CODE>true</CODE>.

  \ingroup PPL_CXX_interface
  This is the non-specialized case, so the class is declared but not defined.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <bool b>
struct Compile_Time_Check;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  A class that is only defined if \p b evaluates to <CODE>true</CODE>.

  \ingroup PPL_CXX_interface
  This is the specialized case with \p b equal to <CODE>true</CODE>,
  so the class is declared and (trivially) defined.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <>
struct Compile_Time_Check<true> {
};

#define PPL_COMPILE_TIME_CHECK_NAME(suffix) compile_time_check_ ## suffix
#define PPL_COMPILE_TIME_CHECK_AUX(e, suffix)                           \
  enum anonymous_enum_compile_time_check_ ## suffix {                   \
    /* If e evaluates to false, then the sizeof cannot be compiled. */  \
    PPL_COMPILE_TIME_CHECK_NAME(suffix)                                 \
      = sizeof(Parma_Polyhedra_Library::Compile_Time_Check<(e)>)        \
  }

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Produces a compilation error if the compile-time constant \p e does
  not evaluate to <CODE>true</CODE>
  \ingroup PPL_CXX_interface
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
#define PPL_COMPILE_TIME_CHECK(e, msg) PPL_COMPILE_TIME_CHECK_AUX(e, __LINE__)

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  A class holding a constant called <CODE>value</CODE> that evaluates
  to \p b.
  \ingroup PPL_CXX_interface
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <bool b>
struct Bool {
  enum const_bool_value {
    value = b
  };
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  A class holding a constant called <CODE>value</CODE> that evaluates
  to <CODE>true</CODE>.
  \ingroup PPL_CXX_interface
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct True : public Bool<true> {
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  A class holding a constant called <CODE>value</CODE> that evaluates
  to <CODE>false</CODE>.
  \ingroup PPL_CXX_interface
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct False : public Bool<false> {
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  A class holding a constant called <CODE>value</CODE> that evaluates
  to <CODE>true</CODE> if and only if \p T1 is the same type as \p T2.

  \ingroup PPL_CXX_interface
  This is the non-specialized case, in which \p T1 and \p T2 can be different.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T1, typename T2>
struct Is_Same : public False {
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  A class holding a constant called <CODE>value</CODE> that evaluates
  to <CODE>true</CODE> if and only if \p T1 is the same type as \p T2.

  \ingroup PPL_CXX_interface
  This is the specialization in which \p T1 and \p T2 are equal.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
struct Is_Same<T, T> : public True {
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  A class holding a constant called <CODE>value</CODE> that evaluates
  to <CODE>true</CODE> if and only if \p Base is the same type as \p Derived
  or \p Derived is a class derived from \p Base.

  \ingroup PPL_CXX_interface
  \note
  Care must be taken to use this predicate with template classes.
  Suppose we have
  \code
  template <typename T> struct B;
  template <typename T> struct D : public B<T>;
  \endcode
  Of course, we cannot test if, for some type variable <CODE>U</CODE>, we have
  <CODE>Is_Same_Or_Derived<B<U>, Type>:: const_bool_value:: value == true</CODE>.
  But we can do as follows:
  \code
  struct B_Base {
  };

  template <typename T> struct B : public B_Base;
  \endcode
  This enables us to inquire
  <CODE>Is_Same_Or_Derived<B_Base, Type>:: const_bool_value:: value</CODE>.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Base, typename Derived>
struct Is_Same_Or_Derived {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! A class that is constructible from just anything.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  struct Any {
    //! The universal constructor.
    template <typename T>
    Any(const T&);
  };

  //! Overloading with \p Base.
  static char func(const Base&);

  //! Overloading with \p Any.
  static double func(Any);

  //! A function obtaining a const reference to a \p Derived object.
  static const Derived& derived_object();

  PPL_COMPILE_TIME_CHECK(sizeof(char) != sizeof(double),
                         "architecture with sizeof(char) == sizeof(double)"
                         " (!?)");

  enum const_bool_value {
    /*!
      Assuming <CODE>sizeof(char) != sizeof(double)</CODE>, the C++
      overload resolution mechanism guarantees that <CODE>value</CODE>
      evaluates to <CODE>true</CODE> if and only if <CODE>Base</CODE>
      is the same type as <CODE>Derived</CODE> or <CODE>Derived</CODE>
      is a class derived from <CODE>Base</CODE>.
    */
    value = (sizeof(func(derived_object())) == sizeof(char))
  };
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  A class that provides a type member called <CODE>type</CODE> equivalent
  to \p T if and only if \p b is <CODE>true</CODE>.

  \ingroup PPL_CXX_interface
  This is the non-specialized case, in which the <CODE>type</CODE> member
  is not present.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <bool b, typename T = void>
struct Enable_If {
};

template <typename Type, Type, typename T = void>
struct Enable_If_Is {
  typedef T type;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  A class that provides a type member called <CODE>type</CODE> equivalent
  to \p T if and only if \p b is <CODE>true</CODE>.

  \ingroup PPL_CXX_interface
  This is the specialization in which the <CODE>type</CODE> member
  is present.

  \note
  Let <CODE>T</CODE>, <CODE>T1</CODE> and <CODE>T2</CODE> be any type
  expressions and suppose we have some template function
  <CODE>T f(T1, T2)</CODE>.  If we want to declare a specialization
  that is enabled only if some compile-time checkable condition holds,
  we simply declare the specialization by
  \code
  template ...
  typename Enable_If<condition, T>::type
  foo(T1 x, T2 y);
  \endcode
  For all the instantiations of the template parameters that cause
  <CODE>condition</CODE> to evaluate to <CODE>false</CODE>,
  the <CODE>Enable_If<condition, T>::type</CODE> member will not be defined.
  Hence, for that instantiations, the specialization will not be eligible.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
struct Enable_If<true, T> {
  typedef T type;
};

template <typename T>
struct Is_Native : public False {
};

template <> struct Is_Native<char> : public True { };
template <> struct Is_Native<signed char> : public True { };
template <> struct Is_Native<signed short> : public True { };
template <> struct Is_Native<signed int> : public True { };
template <> struct Is_Native<signed long> : public True { };
template <> struct Is_Native<signed long long> : public True { };
template <> struct Is_Native<unsigned char> : public True { };
template <> struct Is_Native<unsigned short> : public True { };
template <> struct Is_Native<unsigned int> : public True { };
template <> struct Is_Native<unsigned long> : public True { };
template <> struct Is_Native<unsigned long long> : public True { };

#if PPL_SUPPORTED_FLOAT
template <> struct Is_Native<float> : public True { };
#endif
#if PPL_SUPPORTED_DOUBLE
template <> struct Is_Native<double> : public True { };
#endif
#if PPL_SUPPORTED_LONG_DOUBLE
template <> struct Is_Native<long double> : public True { };
#endif

template <> struct Is_Native<mpz_class> : public True { };

template <> struct Is_Native<mpq_class> : public True { };

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Slow_Copy.hh line 1. */
/* Basic Slow_Copy classes declarations.
*/


/* Automatically generated from PPL source file ../src/Slow_Copy.hh line 28. */
#include <gmpxx.h>

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface
  Copies are not slow by default.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
struct Slow_Copy : public False {
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface
  Copies are slow for mpz_class objects.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <>
struct Slow_Copy<mpz_class> : public True {
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface
  Copies are slow for mpq_class objects.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <>
struct Slow_Copy<mpq_class> : public True {
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Temp_defs.hh line 29. */

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A pool of temporary items of type \p T.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Temp_Item {
public:
  //! Obtains a reference to a temporary item.
  static Temp_Item& obtain();

  //! Releases the temporary item \p p.
  static void release(Temp_Item& p);

  //! Returns a reference to the encapsulated item.
  T& item();

private:
  //! The encapsulated item.
  T item_;

  //! Pointer to the next item in the free list.
  Temp_Item* next;

  class Free_List {
  public:
    Free_List();
    ~Free_List();
    Temp_Item* head_ptr;
  private:
    Free_List(const Free_List&); // Not implemented.
    Free_List& operator=(const Free_List&); // Not implemented.
  }; // class Free_List

  friend class Free_List;

  //! Head of the free list.
  static Temp_Item*& free_list_ref();

  //! Default constructor.
  Temp_Item();

  //! Copy constructor: private and intentionally not implemented.
  Temp_Item(const Temp_Item&);

  //! Assignment operator: private and intentionally not implemented.
  Temp_Item& operator=(const Temp_Item&);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! An holder for a reference to a temporary object.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Temp_Reference_Holder {
public:
  //! Constructs an holder holding a dirty temp.
  Temp_Reference_Holder();

  //! Destructor.
  ~Temp_Reference_Holder();

  //! Returns a reference to the held item.
  T& item();

private:
  //! Copy constructor: private and intentionally not implemented.
  Temp_Reference_Holder(const Temp_Reference_Holder&);

  //! Assignment operator: private and intentionally not implemented.
  Temp_Reference_Holder& operator=(const Temp_Reference_Holder&);

  //! The held item, encapsulated.
  Temp_Item<T>& held;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! An (fake) holder for the value of a temporary object.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Temp_Value_Holder {
public:
  //! Constructs a fake holder.
  Temp_Value_Holder();

  //! Returns the value of the held item.
  T& item();

private:
  //! Copy constructor: private and intentionally not implemented.
  Temp_Value_Holder(const Temp_Value_Holder&);

  //! Assignment operator: private and intentionally not implemented.
  Temp_Value_Holder& operator=(const Temp_Value_Holder&);

  //! The held item.
  T item_;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A structure for the efficient handling of temporaries.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T, typename Enable = void>
class Dirty_Temp;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Specialization for the handling of temporaries with a free list.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Dirty_Temp<T, typename Enable_If<Slow_Copy<T>::value>::type>
  : public Temp_Reference_Holder<T> {
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Specialization for the handling of temporaries with local variables.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Dirty_Temp<T, typename Enable_If<!Slow_Copy<T>::value>::type>
  : public Temp_Value_Holder<T> {
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Temp_inlines.hh line 1. */
/* Temp_* classes implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Temp_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename T>
inline
Temp_Item<T>::Temp_Item()
  : item_() {
}

template <typename T>
inline T&
Temp_Item<T>::item() {
    return item_;
}

template <typename T>
inline
Temp_Item<T>::Free_List::Free_List()
  : head_ptr(0) {
}

template <typename T>
inline Temp_Item<T>*&
Temp_Item<T>::free_list_ref() {
  static Free_List free_list;
  return free_list.head_ptr;
}

template <typename T>
inline Temp_Item<T>&
Temp_Item<T>::obtain() {
  Temp_Item* const p = free_list_ref();
  if (p != 0) {
    free_list_ref() = p->next;
    return *p;
  }
  else {
    return *new Temp_Item();
  }
}

template <typename T>
inline void
Temp_Item<T>::release(Temp_Item& p) {
  p.next = free_list_ref();
  free_list_ref() = &p;
}

template <typename T>
inline
Temp_Reference_Holder<T>::Temp_Reference_Holder()
  : held(Temp_Item<T>::obtain()) {
}

template <typename T>
inline
Temp_Reference_Holder<T>::~Temp_Reference_Holder() {
  Temp_Item<T>::release(held);
}

template <typename T>
inline T&
Temp_Reference_Holder<T>::item() {
  return held.item();
}

template <typename T>
inline
Temp_Value_Holder<T>::Temp_Value_Holder() {
}

template <typename T>
inline T&
Temp_Value_Holder<T>::item() {
  return item_;
}

} // namespace Parma_Polyhedra_Library

#define PPL_DIRTY_TEMP(T, id)                                           \
  Parma_Polyhedra_Library::Dirty_Temp<PPL_U(T)> holder_ ## id;          \
  PPL_U(T)& PPL_U(id) = holder_ ## id.item()

/* Automatically generated from PPL source file ../src/Temp_templates.hh line 1. */
/* Temp_* classes implementation: non-inline template members.
*/


namespace Parma_Polyhedra_Library {

template <typename T>
Temp_Item<T>::Free_List::~Free_List() {
  while (head_ptr != 0) {
    Temp_Item* const p = head_ptr;
    head_ptr = head_ptr->next;
    delete p;
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Temp_defs.hh line 154. */

/* Automatically generated from PPL source file ../src/Rounding_Dir_defs.hh line 1. */
/* Declaration of Rounding_Dir and related functions.
*/


/* Automatically generated from PPL source file ../src/Result_defs.hh line 1. */
/* Result enum and supporting function declarations.
*/


namespace Parma_Polyhedra_Library {

enum Result_Class {
  //! \hideinitializer Representable number result class.
  VC_NORMAL = 0U << 4,

  //! \hideinitializer Negative infinity result class.
  VC_MINUS_INFINITY = 1U << 4,

  //! \hideinitializer Positive infinity result class.
  VC_PLUS_INFINITY = 2U << 4,

  //! \hideinitializer Not a number result class.
  VC_NAN = 3U << 4,

  VC_MASK = VC_NAN
};

// This must be kept in sync with Relation_Symbol
enum Result_Relation {
  //! \hideinitializer No values satisfies the relation.
  VR_EMPTY = 0U,

  //! \hideinitializer Equal. This need to be accompanied by a value.
  VR_EQ = 1U,

  //! \hideinitializer Less than. This need to be accompanied by a value.
  VR_LT = 2U,

  //! \hideinitializer Greater than. This need to be accompanied by a value.
  VR_GT = 4U,

  //! \hideinitializer Not equal. This need to be accompanied by a value.
  VR_NE = VR_LT | VR_GT,

  //! \hideinitializer Less or equal. This need to be accompanied by a value.
  VR_LE = VR_EQ | VR_LT,

  //! \hideinitializer Greater or equal. This need to be accompanied by a value.
  VR_GE = VR_EQ | VR_GT,

  //! \hideinitializer All values satisfy the relation.
  VR_LGE = VR_LT | VR_EQ | VR_GT,

  VR_MASK = VR_LGE
};

//! Possible outcomes of a checked arithmetic computation.
/*! \ingroup PPL_CXX_interface */
enum Result {
  //! \hideinitializer The exact result is not comparable.
  V_EMPTY = VR_EMPTY,

  //! \hideinitializer The computed result is exact.
  V_EQ = static_cast<unsigned>(VR_EQ),

  //! \hideinitializer The computed result is inexact and rounded up.
  V_LT = static_cast<unsigned>(VR_LT),

  //! \hideinitializer The computed result is inexact and rounded down.
  V_GT = static_cast<unsigned>(VR_GT),

  //! \hideinitializer The computed result is inexact.
  V_NE = VR_NE,

  //! \hideinitializer The computed result may be inexact and rounded up.
  V_LE = VR_LE,

  //! \hideinitializer The computed result may be inexact and rounded down.
  V_GE = VR_GE,

  //! \hideinitializer The computed result may be inexact.
  V_LGE = VR_LGE,

  //! \hideinitializer The exact result is a number out of finite bounds.
  V_OVERFLOW = 1U << 6,

  //! \hideinitializer A negative integer overflow occurred (rounding up).
  V_LT_INF = V_LT | V_OVERFLOW,

  //! \hideinitializer A positive integer overflow occurred (rounding down).
  V_GT_SUP = V_GT | V_OVERFLOW,

  //! \hideinitializer A positive integer overflow occurred (rounding up).
  V_LT_PLUS_INFINITY = V_LT | static_cast<unsigned>(VC_PLUS_INFINITY),

  //! \hideinitializer A negative integer overflow occurred (rounding down).
  V_GT_MINUS_INFINITY = V_GT | static_cast<unsigned>(VC_MINUS_INFINITY),

  //! \hideinitializer Negative infinity result.
  V_EQ_MINUS_INFINITY = V_EQ | static_cast<unsigned>(VC_MINUS_INFINITY),

  //! \hideinitializer Positive infinity result.
  V_EQ_PLUS_INFINITY = V_EQ | static_cast<unsigned>(VC_PLUS_INFINITY),

  //! \hideinitializer Not a number result.
  V_NAN = static_cast<unsigned>(VC_NAN),

  //! \hideinitializer Converting from unknown string.
  V_CVT_STR_UNK = V_NAN | (1U << 8),

  //! \hideinitializer Dividing by zero.
  V_DIV_ZERO = V_NAN | (2U << 8),

  //! \hideinitializer Adding two infinities having opposite signs.
  V_INF_ADD_INF = V_NAN | (3U << 8),

  //! \hideinitializer Dividing two infinities.
  V_INF_DIV_INF = V_NAN | (4U << 8),

  //! \hideinitializer Taking the modulus of an infinity.
  V_INF_MOD = V_NAN | (5U << 8),

  //! \hideinitializer Multiplying an infinity by zero.
  V_INF_MUL_ZERO = V_NAN | (6U << 8),

  //! \hideinitializer Subtracting two infinities having the same sign.
  V_INF_SUB_INF = V_NAN | (7U << 8),

  //! \hideinitializer Computing a remainder modulo zero.
  V_MOD_ZERO = V_NAN | (8U << 8),

  //! \hideinitializer Taking the square root of a negative number.
  V_SQRT_NEG = V_NAN | (9U << 8),

  //! \hideinitializer Unknown result due to intermediate negative overflow.
  V_UNKNOWN_NEG_OVERFLOW = V_NAN | (10U << 8),

  //! \hideinitializer Unknown result due to intermediate positive overflow.
  V_UNKNOWN_POS_OVERFLOW = V_NAN | (11U << 8),

  //! \hideinitializer The computed result is not representable.
  V_UNREPRESENTABLE = 1U << 7

};

//! \name Functions Inspecting and/or Combining Result Values
//@{

/*! \ingroup PPL_CXX_interface */
Result operator&(Result x, Result y);

/*! \ingroup PPL_CXX_interface */
Result operator|(Result x, Result y);

/*! \ingroup PPL_CXX_interface */
Result operator-(Result x, Result y);

/*! \ingroup PPL_CXX_interface \brief
  Extracts the value class part of \p r (representable number,
  unrepresentable minus/plus infinity or nan).
*/
Result_Class result_class(Result r);

/*! \ingroup PPL_CXX_interface \brief
  Extracts the relation part of \p r.
*/
Result_Relation result_relation(Result r);

/*! \ingroup PPL_CXX_interface */
Result result_relation_class(Result r);

//@} // Functions Inspecting and/or Combining Result Values

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Result_inlines.hh line 1. */
/* Result supporting functions implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/assertions.hh line 1. */
/* Implementation of PPL assert-like macros.
*/


// The PPL_UNREACHABLE_MSG macro flags a program point as unreachable.
// Argument `msg__' is added to output when assertions are turned on.
#if defined(NDEBUG)
#define PPL_UNREACHABLE_MSG(msg__) Parma_Polyhedra_Library::ppl_unreachable()
#else
#define PPL_UNREACHABLE_MSG(msg__) Parma_Polyhedra_Library:: \
  ppl_unreachable_msg(msg__, __FILE__, __LINE__, __func__)
#endif

// The PPL_UNREACHABLE macro flags a program point as unreachable.
#define PPL_UNREACHABLE PPL_UNREACHABLE_MSG("unreachable")

// The PPL_ASSERTION_FAILED macro is used to output a message after
// an assertion failure and then cause program termination.
// (It is meant to be used only when assertions are turned on.)
#define PPL_ASSERTION_FAILED(msg__) Parma_Polyhedra_Library:: \
  ppl_assertion_failed(msg__, __FILE__, __LINE__, __func__)

// Helper macro PPL_ASSERT_IMPL_: do not use it directly.
#if defined(NDEBUG)
#define PPL_ASSERT_IMPL_(cond__) ((void) 0)
#else
#define PPL_STRING_(s) #s
#define PPL_ASSERT_IMPL_(cond__) \
  ((cond__) ? (void) 0 : PPL_ASSERTION_FAILED(PPL_STRING_(cond__)))
#endif


// Non zero to detect use of PPL_ASSERT instead of PPL_ASSERT_HEAVY
// Note: flag does not affect code built with NDEBUG defined.
#define PPL_DEBUG_PPL_ASSERT 1

// The PPL_ASSERT macro states that Boolean condition cond__ should hold.
// This is meant to replace uses of C assert().
#if defined(NDEBUG) || (!PPL_DEBUG_PPL_ASSERT)
#define PPL_ASSERT(cond__) PPL_ASSERT_IMPL_(cond__)
#else
// Note: here we have assertions enabled and PPL_DEBUG_PPL_ASSERT is 1.
// Check if the call to PPL_ASSERT should be replaced by PPL_ASSERT_HEAVY
// (i.e., if the former may interfere with computational weights).
#define PPL_ASSERT(cond__)                                        \
  do {                                                            \
    typedef Parma_Polyhedra_Library::Weightwatch_Traits W_Traits; \
    W_Traits::Threshold old_weight__ = W_Traits::weight;          \
    PPL_ASSERT_IMPL_(cond__);                                     \
    PPL_ASSERT_IMPL_(old_weight__ == W_Traits::weight             \
                     && ("PPL_ASSERT_HEAVY has to be used here" != 0)); \
  } while (false)
#endif // !defined(NDEBUG) && PPL_DEBUG_PPL_ASSERT


// Macro PPL_ASSERT_HEAVY is meant to be used when the evaluation of
// the assertion may change computational weights (via WEIGHT_ADD).
#if defined(NDEBUG)
#define PPL_ASSERT_HEAVY(cond__) PPL_ASSERT_IMPL_(cond__)
#else
#define PPL_ASSERT_HEAVY(cond__)                                \
  do {                                                          \
    Parma_Polyhedra_Library::In_Assert guard;                   \
    PPL_ASSERT_IMPL_(cond__);                                   \
  } while (false)
#endif // !defined(NDEBUG)


// Macro PPL_EXPECT (resp., PPL_EXPECT_HEAVY) should be used rather than
// PPL_ASSERT (resp., PPL_ASSERT_HEAVY) when the condition is assumed to
// hold but it is not under library control (typically, it depends on
// user provided input).
#define PPL_EXPECT(cond__) PPL_ASSERT(cond__)
#define PPL_EXPECT_HEAVY(cond__) PPL_ASSERT_HEAVY(cond__)


namespace Parma_Polyhedra_Library {

#if PPL_CXX_SUPPORTS_ATTRIBUTE_WEAK
#define PPL_WEAK_NORETURN __attribute__((weak, noreturn))
#else
#define PPL_WEAK_NORETURN __attribute__((noreturn))
#endif

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Helper function causing program termination by calling \c abort.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void ppl_unreachable() PPL_WEAK_NORETURN;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Helper function printing message on \c std::cerr and causing program
  termination by calling \c abort.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void ppl_unreachable_msg(const char* msg,
                         const char* file, unsigned int line,
                         const char* function) PPL_WEAK_NORETURN;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Helper function printing an assertion failure message on \c std::cerr
  and causing program termination by calling \c abort.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void ppl_assertion_failed(const char* assertion_text,
                          const char* file, unsigned int line,
                          const char* function) PPL_WEAK_NORETURN;


#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Returns \c true if and only if \p x_copy contains \p y_copy.

  \note
  This is a helper function for debugging purposes, to be used in assertions.
  The two arguments are meant to be passed by value, i.e., <em>copied</em>,
  so that their representations will not be affected by the containment check.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
bool copy_contains(T x_copy, T y_copy) {
  return x_copy.contains(y_copy);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Result_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

/*! \ingroup PPL_CXX_interface */
inline Result
operator&(Result x, Result y) {
  const unsigned res = static_cast<unsigned>(x) & static_cast<unsigned>(y);
  return static_cast<Result>(res);
}

/*! \ingroup PPL_CXX_interface */
inline Result
operator|(Result x, Result y) {
  const unsigned res = static_cast<unsigned>(x) | static_cast<unsigned>(y);
  return static_cast<Result>(res);
}

/*! \ingroup PPL_CXX_interface */
inline Result
operator-(Result x, Result y) {
  const Result y_neg = static_cast<Result>(~static_cast<unsigned>(y));
  return x & y_neg;
}

/*! \ingroup PPL_CXX_interface */
inline Result_Class
result_class(Result r) {
  const Result rc = r & static_cast<Result>(VC_MASK);
  return static_cast<Result_Class>(rc);
}

/*! \ingroup PPL_CXX_interface */
inline Result_Relation
result_relation(Result r) {
  const Result rc = r & static_cast<Result>(VR_MASK);
  return static_cast<Result_Relation>(rc);
}

/*! \ingroup PPL_CXX_interface */
inline Result
result_relation_class(Result r) {
  return r & (static_cast<Result>(VR_MASK) | static_cast<Result>(VC_MASK));
}

inline int
result_overflow(Result r) {
  switch (result_class(r)) {
  case VC_NORMAL:
    switch (r) {
    case V_LT_INF:
      return -1;
    case V_GT_SUP:
      return 1;
    default:
      break;
    }
    break;
  case VC_MINUS_INFINITY:
    return -1;
  case VC_PLUS_INFINITY:
    return 1;
  default:
    break;
  }
  return 0;
}

inline bool
result_representable(Result r) {
  return (r & V_UNREPRESENTABLE) != V_UNREPRESENTABLE;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Result_defs.hh line 194. */

/* Automatically generated from PPL source file ../src/fpu_defs.hh line 1. */
/* Floating point unit related functions.
*/


/* Automatically generated from PPL source file ../src/fpu_types.hh line 1. */


#ifdef PPL_HAVE_IEEEFP_H
#include <ieeefp.h>
#endif

namespace Parma_Polyhedra_Library {

enum fpu_rounding_direction_type {};
enum fpu_rounding_control_word_type {};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/compiler.hh line 1. */
/* C++ compiler related stuff.
*/


#include <cstddef>
#include <climits>
#include <cassert>

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  No-op macro that allows to avoid unused variable warnings from
  the compiler.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
#define PPL_USED(v) (void)(v)

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  No-op function that force the compiler to store the argument and
  to reread it from memory if needed (thus preventing CSE).
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
inline void
PPL_CC_FLUSH(const T& x) {
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
  __asm__ __volatile__ ("" : "+m" (const_cast<T&>(x)));
#else
  // FIXME: is it possible to achieve the same effect in a portable way?
  PPL_USED(x);
#endif
}

#ifndef PPL_SUPPRESS_UNINIT_WARNINGS
#define PPL_SUPPRESS_UNINIT_WARNINGS 1
#endif

#ifndef PPL_SUPPRESS_UNINITIALIZED_WARNINGS
#define PPL_SUPPRESS_UNINITIALIZED_WARNINGS 1
#endif

#if PPL_SUPPRESS_UNINITIALIZED_WARNINGS
template <typename T>
struct Suppress_Uninitialized_Warnings_Type {
  typedef T synonym;
};

#define PPL_UNINITIALIZED(type, name)                                   \
  PPL_U(type) PPL_U(name)                                               \
  = Suppress_Uninitialized_Warnings_Type<PPL_U(type)>::synonym()
#else
#define PPL_UNINITIALIZED(type, name)           \
  PPL_U(type) name
#endif

#define sizeof_to_bits(size)                    \
  ((size) * static_cast<std::size_t>(CHAR_BIT))

#if !defined(__GNUC__)

inline unsigned int
clz32(uint32_t w) {
  unsigned int r = 31;
  if ((w & 0xffff0000U) != 0) {
    w >>= 16;
    r -= 16;
  }
  if ((w & 0xff00U) != 0) {
    w >>= 8;
    r -= 8;
  }
  if ((w & 0xf0U) != 0) {
    w >>= 4;
    r -= 4;
  }
  if ((w & 0xcU) != 0) {
    w >>= 2;
    r -= 2;
  }
  if ((w & 0x2U) != 0) {
    r -= 1;
  }
  return r;
}

inline unsigned int
clz64(uint64_t w) {
  if ((w & 0xffffffff00000000ULL) == 0) {
    return clz32(static_cast<uint32_t>(w)) + 32;
  }
  else {
    return clz32(static_cast<uint32_t>(w >> 32));
  }
}

inline unsigned int
ctz32(uint32_t w) {
  static const unsigned int mod37_table[] = {
    32, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13,
    4, 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9,
    5, 20, 8, 19, 18
  };
  return mod37_table[(w & -w) % 37];
}

inline unsigned int
ctz64(uint64_t w) {
  if ((w & 0x00000000ffffffffULL) == 0) {
    return ctz32(static_cast<uint32_t>(w >> 32)) + 32;
  }
  else {
    return ctz32(static_cast<uint32_t>(w));
  }
}

#endif

inline unsigned int
clz(unsigned int u) {
  assert(u != 0);
#if defined(__GNUC__)
  return static_cast<unsigned int>(__builtin_clz(u));
#elif PPL_SIZEOF_INT == 4
  return clz32(u);
#elif PPL_SIZEOF_INT == 8
  return clz64(u);
#else
  #error "Unsupported unsigned int size"
#endif
}

inline unsigned int
clz(unsigned long ul) {
  assert(ul != 0);
#if defined(__GNUC__)
  return static_cast<unsigned int>(__builtin_clzl(ul));
#elif PPL_SIZEOF_LONG == 4
  return clz32(ul);
#elif PPL_SIZEOF_LONG == 8
  return clz64(ul);
#else
  #error "Unsupported unsigned long size"
#endif
}

inline unsigned int
clz(unsigned long long ull) {
  assert(ull != 0);
#if defined(__GNUC__)
  return static_cast<unsigned int>(__builtin_clzll(ull));
#elif PPL_SIZEOF_LONG_LONG == 4
  return clz32(ull);
#elif PPL_SIZEOF_LONG_LONG == 8
  return clz64(ull);
#else
  #error "Unsupported unsigned long long size"
#endif
}


inline unsigned int
ctz(unsigned int u) {
  assert(u != 0);
#if defined(__GNUC__)
  return static_cast<unsigned int>(__builtin_ctz(u));
#elif PPL_SIZEOF_INT == 4
  return ctz32(u);
#elif PPL_SIZEOF_INT == 8
  return ctz64(u);
#else
  #error "Unsupported unsigned int size"
#endif
}

inline unsigned int
ctz(unsigned long ul) {
  assert(ul != 0);
#if defined(__GNUC__)
  return static_cast<unsigned int>(__builtin_ctzl(ul));
#elif PPL_SIZEOF_LONG == 4
  return ctz32(ul);
#elif PPL_SIZEOF_LONG == 8
  return ctz64(ul);
#else
  #error "Unsupported unsigned long size"
#endif
}

inline unsigned int
ctz(unsigned long long ull) {
  assert(ull != 0);
#if defined(__GNUC__)
  return static_cast<unsigned int>(__builtin_ctzll(ull));
#elif PPL_SIZEOF_LONG_LONG == 4
  return ctz32(ull);
#elif PPL_SIZEOF_LONG_LONG == 8
  return ctz64(ull);
#else
  #error "Unsupported unsigned long long size"
#endif
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/fpu_defs.hh line 29. */

namespace Parma_Polyhedra_Library {

//! \name Functions Controlling Floating Point Unit
//@{

//! Initializes the FPU control functions.
void
fpu_initialize_control_functions();

//! Returns the current FPU rounding direction.
fpu_rounding_direction_type
fpu_get_rounding_direction();

//! Sets the FPU rounding direction to \p dir.
void
fpu_set_rounding_direction(fpu_rounding_direction_type dir);

/*! \brief
  Sets the FPU rounding direction to \p dir and returns the rounding
  control word previously in use.
*/
fpu_rounding_control_word_type
fpu_save_rounding_direction(fpu_rounding_direction_type dir);

/*! \brief
  Sets the FPU rounding direction to \p dir, clears the <EM>inexact
  computation</EM> status, and returns the rounding control word
  previously in use.
*/
fpu_rounding_control_word_type
fpu_save_rounding_direction_reset_inexact(fpu_rounding_direction_type dir);

//! Restores the FPU rounding rounding control word to \p cw.
void
fpu_restore_rounding_direction(fpu_rounding_control_word_type w);

//! Clears the <EM>inexact computation</EM> status.
void
fpu_reset_inexact();

/*! \brief
  Queries the <EM>inexact computation</EM> status.

  Returns 0 if the computation was definitely exact, 1 if it was
  definitely inexact, -1 if definite exactness information is unavailable.
*/
int
fpu_check_inexact();

//@} // Functions Controlling Floating Point Unit

} // namespace Parma_Polyhedra_Library

#if PPL_CAN_CONTROL_FPU

#if defined(__i386__) && (defined(__GNUC__) || defined(__INTEL_COMPILER))
/* Automatically generated from PPL source file ../src/fpu-ia32_inlines.hh line 1. */
/* IA-32 floating point unit inline related functions.
*/


#include <csetjmp>
#include <csignal>

#define FPU_INVALID       0x01
#define FPU_DIVBYZERO     0x04
#define FPU_OVERFLOW      0x08
#define FPU_UNDERFLOW     0x10
#define FPU_INEXACT       0x20

#define FPU_ALL_EXCEPT \
  (FPU_INEXACT | FPU_DIVBYZERO | FPU_UNDERFLOW | FPU_OVERFLOW | FPU_INVALID)

#define PPL_FPU_TONEAREST     0
#define PPL_FPU_DOWNWARD      0x400
#define PPL_FPU_UPWARD        0x800
#define PPL_FPU_TOWARDZERO    0xc00

#define FPU_ROUNDING_MASK 0xc00

#define SSE_INEXACT       0x20

#define PPL_FPU_CONTROL_DEFAULT_BASE 0x37f
#define PPL_SSE_CONTROL_DEFAULT_BASE 0x1f80

// This MUST be congruent with the definition of ROUND_DIRECT
#define PPL_FPU_CONTROL_DEFAULT \
  (PPL_FPU_CONTROL_DEFAULT_BASE | PPL_FPU_UPWARD)
#define PPL_SSE_CONTROL_DEFAULT \
  (PPL_SSE_CONTROL_DEFAULT_BASE | (PPL_FPU_UPWARD << 3))

namespace Parma_Polyhedra_Library {

typedef struct {
  unsigned short control_word;
  unsigned short unused1;
  unsigned short status_word;
  unsigned short unused2;
  unsigned short tags;
  unsigned short unused3;
  unsigned int eip;
  unsigned short cs_selector;
  unsigned int opcode:11;
  unsigned int unused4:5;
  unsigned int data_offset;
  unsigned short data_selector;
  unsigned short unused5;
} ia32_fenv_t;

inline int
fpu_get_control() {
  unsigned short cw;
  __asm__ __volatile__ ("fnstcw %0" : "=m" (*&cw) : : "memory");
  return cw;
}

inline void
fpu_set_control(int c) {
  unsigned short cw = static_cast<unsigned short>(c);
  __asm__ __volatile__ ("fldcw %0" : : "m" (*&cw) : "memory");
}

inline int
fpu_get_status() {
  unsigned short sw;
  __asm__ __volatile__ ("fnstsw %0" : "=a" (sw) : : "memory");
  return sw;
}

inline void
fpu_clear_status(unsigned short bits) {
  /* There is no fldsw instruction */
  ia32_fenv_t env;
  __asm__ __volatile__ ("fnstenv %0" : "=m" (env));
  env.status_word = static_cast<unsigned short>(env.status_word & ~bits);
  __asm__ __volatile__ ("fldenv %0" : : "m" (env) : "memory");
}

inline void
fpu_clear_exceptions() {
  __asm__ __volatile__ ("fnclex" : /* No outputs.  */ : : "memory");
}

#ifdef PPL_FPMATH_MAY_USE_SSE
inline void
sse_set_control(unsigned int cw) {
  __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&cw) : "memory");
}

inline unsigned int
sse_get_control() {
  unsigned int cw;
  __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&cw) : : "memory");
  return cw;
}
#endif

inline void
fpu_initialize_control_functions() {
#ifdef PPL_FPMATH_MAY_USE_SSE
  extern void detect_sse_unit();
  detect_sse_unit();
#endif
}

inline fpu_rounding_direction_type
fpu_get_rounding_direction() {
  return static_cast<fpu_rounding_direction_type>(fpu_get_control() & FPU_ROUNDING_MASK);
}

inline void
fpu_set_rounding_direction(fpu_rounding_direction_type dir) {
#ifdef PPL_FPMATH_MAY_USE_387
  fpu_set_control(PPL_FPU_CONTROL_DEFAULT_BASE | dir);
#endif
#ifdef PPL_FPMATH_MAY_USE_SSE
  extern bool have_sse_unit;
  if (have_sse_unit)
    sse_set_control(PPL_SSE_CONTROL_DEFAULT_BASE | (dir << 3));
#endif
}

inline fpu_rounding_control_word_type
fpu_save_rounding_direction(fpu_rounding_direction_type dir) {
#ifdef PPL_FPMATH_MAY_USE_387
  fpu_set_control(PPL_FPU_CONTROL_DEFAULT_BASE | dir);
#endif
#ifdef PPL_FPMATH_MAY_USE_SSE
  extern bool have_sse_unit;
  if (have_sse_unit)
    sse_set_control(PPL_SSE_CONTROL_DEFAULT_BASE | (dir << 3));
#endif
  return static_cast<fpu_rounding_control_word_type>(0);
}

inline void
fpu_reset_inexact() {
#ifdef PPL_FPMATH_MAY_USE_387
  fpu_clear_exceptions();
#endif
#ifdef PPL_FPMATH_MAY_USE_SSE
  // NOTE: on entry to this function the current rounding mode
  // has to be the default one.
  extern bool have_sse_unit;
  if (have_sse_unit) {
    sse_set_control(PPL_SSE_CONTROL_DEFAULT);
  }
#endif
}

inline void
fpu_restore_rounding_direction(fpu_rounding_control_word_type) {
#ifdef PPL_FPMATH_MAY_USE_387
  fpu_set_control(PPL_FPU_CONTROL_DEFAULT);
#endif
#ifdef PPL_FPMATH_MAY_USE_SSE
  extern bool have_sse_unit;
  if (have_sse_unit) {
    sse_set_control(PPL_SSE_CONTROL_DEFAULT);
  }
#endif
}

inline int
fpu_check_inexact() {
#ifdef PPL_FPMATH_MAY_USE_387
  if (fpu_get_status() & FPU_INEXACT) {
    return 1;
  }
#endif
#ifdef PPL_FPMATH_MAY_USE_SSE
  extern bool have_sse_unit;
  if (have_sse_unit && (sse_get_control() & SSE_INEXACT)) {
    return 1;
  }
#endif
  return 0;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/fpu_defs.hh line 87. */
#elif defined(PPL_HAVE_IEEEFP_H)                                        \
  && (defined(__sparc)                                                  \
      || defined(sparc)                                                 \
      || defined(__sparc__))
/* Automatically generated from PPL source file ../src/fpu-sparc_inlines.hh line 1. */
/* SPARC floating point unit related functions.
*/


#ifdef PPL_HAVE_IEEEFP_H
#include <ieeefp.h>

#define PPL_FPU_TONEAREST  ((int) FP_RN)
#define PPL_FPU_UPWARD     ((int) FP_RP)
#define PPL_FPU_DOWNWARD   ((int) FP_RM)
#define PPL_FPU_TOWARDZERO ((int) FP_RZ)

namespace Parma_Polyhedra_Library {

inline void
fpu_initialize_control_functions() {
}

inline fpu_rounding_direction_type
fpu_get_rounding_direction() {
  return static_cast<fpu_rounding_direction_type>(fpgetround());
}

inline void
fpu_set_rounding_direction(fpu_rounding_direction_type dir) {
  fpsetround((fp_rnd) dir);
}

inline fpu_rounding_control_word_type
fpu_save_rounding_direction(fpu_rounding_direction_type dir) {
  return static_cast<fpu_rounding_control_word_type>(fpsetround((fp_rnd) dir));
}

inline void
fpu_reset_inexact() {
  fp_except except = fpgetmask();
  except &= ~FP_X_IMP;
  fpsetmask(except);
}

inline void
fpu_restore_rounding_direction(fpu_rounding_control_word_type w) {
  fpsetround((fp_rnd) w);
}

inline int
fpu_check_inexact() {
  return (fpgetmask() & FP_X_IMP) ? 1 : 0;
}

} // namespace Parma_Polyhedra_Library

#endif // !defined(PPL_HAVE_IEEEFP_H)

/* Automatically generated from PPL source file ../src/fpu_defs.hh line 92. */
#elif defined(PPL_HAVE_FENV_H)
/* Automatically generated from PPL source file ../src/fpu-c99_inlines.hh line 1. */
/* C99 Floating point unit related functions.
*/


#ifdef PPL_HAVE_FENV_H
#include <fenv.h>
#include <stdexcept>

#ifdef FE_TONEAREST
#define PPL_FPU_TONEAREST FE_TONEAREST
#endif
#ifdef FE_UPWARD
#define PPL_FPU_UPWARD FE_UPWARD
#endif
#ifdef FE_DOWNWARD
#define PPL_FPU_DOWNWARD FE_DOWNWARD
#endif
#ifdef FE_TOWARDZERO
#define PPL_FPU_TOWARDZERO FE_TOWARDZERO
#endif

namespace Parma_Polyhedra_Library {

inline void
fpu_initialize_control_functions() {
  const int old = fegetround();
  if (fesetround(PPL_FPU_DOWNWARD) != 0
      || fesetround(PPL_FPU_UPWARD) != 0
      || fesetround(old) != 0) {
    throw std::logic_error("PPL configuration error:"
                           " PPL_CAN_CONTROL_FPU evaluates to true,"
                           " but fesetround() returns nonzero.");
  }
}

inline fpu_rounding_direction_type
fpu_get_rounding_direction() {
  return static_cast<fpu_rounding_direction_type>(fegetround());
}

inline void
fpu_set_rounding_direction(fpu_rounding_direction_type dir) {
  fesetround(dir);
}

inline fpu_rounding_control_word_type
fpu_save_rounding_direction(fpu_rounding_direction_type dir) {
  const fpu_rounding_control_word_type old
    = static_cast<fpu_rounding_control_word_type>(fegetround());
  fesetround(dir);
  return old;
}

inline void
fpu_reset_inexact() {
#if PPL_CXX_SUPPORTS_IEEE_INEXACT_FLAG
  feclearexcept(FE_INEXACT);
#endif
}

inline void
fpu_restore_rounding_direction(fpu_rounding_control_word_type w) {
  fesetround(w);
}

inline int
fpu_check_inexact() {
#if PPL_CXX_SUPPORTS_IEEE_INEXACT_FLAG
  return fetestexcept(FE_INEXACT) != 0 ? 1 : 0;
#else
  return -1;
#endif
}

} // namespace Parma_Polyhedra_Library

#endif // !defined(PPL_HAVE_FENV_H)

/* Automatically generated from PPL source file ../src/fpu_defs.hh line 94. */
#else
#error "PPL_CAN_CONTROL_FPU evaluates to true: why?"
#endif

#else // !PPL_CAN_CONTROL_FPU

/* Automatically generated from PPL source file ../src/fpu-none_inlines.hh line 1. */
/* Null floating point unit related functions.
*/


#include <stdexcept>

namespace Parma_Polyhedra_Library {

inline void
fpu_initialize_control_functions() {
  throw std::logic_error("PPL::fpu_initialize_control_functions():"
                         " cannot control the FPU");
}

inline fpu_rounding_direction_type
fpu_get_rounding_direction() {
  throw std::logic_error("PPL::fpu_get_rounding_direction():"
                         " cannot control the FPU");
}

inline void
fpu_set_rounding_direction(int) {
  throw std::logic_error("PPL::fpu_set_rounding_direction():"
                         " cannot control the FPU");
}

inline int
fpu_save_rounding_direction(int) {
  throw std::logic_error("PPL::fpu_save_rounding_direction():"
                         " cannot control the FPU");
}

inline void
fpu_reset_inexact() {
  throw std::logic_error("PPL::fpu_reset_inexact():"
                         " cannot control the FPU");
}

inline void
fpu_restore_rounding_direction(int) {
  throw std::logic_error("PPL::fpu_restore_rounding_direction():"
                         " cannot control the FPU");
}

inline int
fpu_check_inexact() {
  throw std::logic_error("PPL::fpu_check_inexact():"
                         " cannot control the FPU");
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/fpu_defs.hh line 101. */

#endif // !PPL_CAN_CONTROL_FPU

/* Automatically generated from PPL source file ../src/Rounding_Dir_defs.hh line 29. */

namespace Parma_Polyhedra_Library {

//! Rounding directions for arithmetic computations.
/*! \ingroup PPL_CXX_interface */
enum Rounding_Dir {
  /*! \hideinitializer
    Round toward \f$-\infty\f$.
  */
  ROUND_DOWN = 0U,

  /*! \hideinitializer
    Round toward \f$+\infty\f$.
  */
  ROUND_UP = 1U,

  /*! \hideinitializer
    Rounding is delegated to lower level. Result info is evaluated lazily.
  */
  ROUND_IGNORE = 6U,
  ROUND_NATIVE = ROUND_IGNORE,

  /*! \hideinitializer
    Rounding is not needed: client code must ensure that the operation
    result is exact and representable in the destination type.
    Result info is evaluated lazily.
  */
  ROUND_NOT_NEEDED = 7U,

  ROUND_DIRECT = ROUND_UP,
  ROUND_INVERSE = ROUND_DOWN,

  ROUND_DIR_MASK = 7U,

  /*! \hideinitializer
    The client code is willing to pay an extra price to know the exact
    relation between the exact result and the computed one.
   */
  ROUND_STRICT_RELATION = 8U,

  ROUND_CHECK = ROUND_DIRECT | ROUND_STRICT_RELATION
};

//! \name Functions Inspecting and/or Combining Rounding_Dir Values
//@{

/*! \ingroup PPL_CXX_interface */
Rounding_Dir operator&(Rounding_Dir x, Rounding_Dir y);

/*! \ingroup PPL_CXX_interface */
Rounding_Dir operator|(Rounding_Dir x, Rounding_Dir y);

/*! \ingroup PPL_CXX_interface \brief
  Returns the inverse rounding mode of \p dir,
  <CODE>ROUND_IGNORE</CODE> being the inverse of itself.
*/
Rounding_Dir inverse(Rounding_Dir dir);

/*! \ingroup PPL_CXX_interface */
Rounding_Dir round_dir(Rounding_Dir dir);

/*! \ingroup PPL_CXX_interface */
bool round_down(Rounding_Dir dir);

/*! \ingroup PPL_CXX_interface */
bool round_up(Rounding_Dir dir);

/*! \ingroup PPL_CXX_interface */
bool round_ignore(Rounding_Dir dir);

/*! \ingroup PPL_CXX_interface */
bool round_not_needed(Rounding_Dir dir);

/*! \ingroup PPL_CXX_interface */
bool round_not_requested(Rounding_Dir dir);

/*! \ingroup PPL_CXX_interface */
bool round_direct(Rounding_Dir dir);

/*! \ingroup PPL_CXX_interface */
bool round_inverse(Rounding_Dir dir);

/*! \ingroup PPL_CXX_interface */
bool round_strict_relation(Rounding_Dir dir);

/*! \ingroup PPL_CXX_interface */
fpu_rounding_direction_type round_fpu_dir(Rounding_Dir dir);

//@} // Functions Inspecting and/or Combining Rounding_Dir Values

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Rounding_Dir_inlines.hh line 1. */
/* Inline functions operating on enum Rounding_Dir values.
*/


/* Automatically generated from PPL source file ../src/Rounding_Dir_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

/*! \ingroup PPL_CXX_interface */
inline Rounding_Dir
operator&(Rounding_Dir x, Rounding_Dir y) {
  const unsigned res = static_cast<unsigned>(x) & static_cast<unsigned>(y);
  return static_cast<Rounding_Dir>(res);
}

/*! \ingroup PPL_CXX_interface */
inline Rounding_Dir
operator|(Rounding_Dir x, Rounding_Dir y) {
  const unsigned res = static_cast<unsigned>(x) | static_cast<unsigned>(y);
  return static_cast<Rounding_Dir>(res);
}

/*! \ingroup PPL_CXX_interface */
inline Rounding_Dir
round_dir(Rounding_Dir dir) {
  return dir & ROUND_DIR_MASK;
}

/*! \ingroup PPL_CXX_interface */
inline bool
round_down(Rounding_Dir dir) {
  return round_dir(dir) == ROUND_DOWN;
}

/*! \ingroup PPL_CXX_interface */
inline bool
round_up(Rounding_Dir dir) {
  return round_dir(dir) == ROUND_UP;
}

/*! \ingroup PPL_CXX_interface */
inline bool
round_ignore(Rounding_Dir dir) {
  return round_dir(dir) == ROUND_IGNORE;
}

/*! \ingroup PPL_CXX_interface */
inline bool
round_not_needed(Rounding_Dir dir) {
  return round_dir(dir) == ROUND_NOT_NEEDED;
}

/*! \ingroup PPL_CXX_interface */
inline bool
round_not_requested(Rounding_Dir dir) {
  return round_dir(dir) == ROUND_IGNORE || round_dir(dir) == ROUND_NOT_NEEDED;
}

/*! \ingroup PPL_CXX_interface */
inline bool
round_direct(Rounding_Dir dir) {
  return round_dir(dir) == ROUND_DIRECT;
}

/*! \ingroup PPL_CXX_interface */
inline bool
round_inverse(Rounding_Dir dir) {
  return round_dir(dir) == ROUND_INVERSE;
}

/*! \ingroup PPL_CXX_interface */
inline bool
round_strict_relation(Rounding_Dir dir) {
  return (dir & ROUND_STRICT_RELATION) == ROUND_STRICT_RELATION;
}

#if PPL_CAN_CONTROL_FPU

/*! \ingroup PPL_CXX_interface */
inline fpu_rounding_direction_type
round_fpu_dir(Rounding_Dir dir) {
  switch (round_dir(dir)) {
  case ROUND_UP:
    return static_cast<fpu_rounding_direction_type>(PPL_FPU_UPWARD);
  case ROUND_DOWN:
    return static_cast<fpu_rounding_direction_type>(PPL_FPU_DOWNWARD);
  case ROUND_IGNORE: // Fall through.
  default:
    PPL_UNREACHABLE;
    return static_cast<fpu_rounding_direction_type>(PPL_FPU_UPWARD);
  }
}

#undef PPL_FPU_DOWNWARD
#undef PPL_FPU_TONEAREST
#undef PPL_FPU_TOWARDZERO
#undef PPL_FPU_UPWARD

#endif

/*! \ingroup PPL_CXX_interface */
inline Rounding_Dir
inverse(Rounding_Dir dir) {
  switch (round_dir(dir)) {
  case ROUND_UP:
    return ROUND_DOWN | (dir & ROUND_STRICT_RELATION);
  case ROUND_DOWN:
    return ROUND_UP | (dir & ROUND_STRICT_RELATION);
  case ROUND_IGNORE:
    return dir;
  default:
    PPL_UNREACHABLE;
    return dir;
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Rounding_Dir_defs.hh line 122. */


/* Automatically generated from PPL source file ../src/Numeric_Format_defs.hh line 1. */
/* Numeric format.
*/


/* Automatically generated from PPL source file ../src/Numeric_Format_defs.hh line 29. */

namespace Parma_Polyhedra_Library {

class Numeric_Format {
};

} // namespace Parma_Polyhedra_Library


/* Automatically generated from PPL source file ../src/Float_defs.hh line 1. */
/* IEC 559 floating point format related functions.
*/


/* Automatically generated from PPL source file ../src/globals_types.hh line 1. */


#include <cstddef>

namespace Parma_Polyhedra_Library {

//! An unsigned integral type for representing space dimensions.
/*! \ingroup PPL_CXX_interface */
typedef size_t dimension_type;

//! An unsigned integral type for representing memory size in bytes.
/*! \ingroup PPL_CXX_interface */
typedef size_t memory_size_type;

//! Kinds of degenerate abstract elements.
/*! \ingroup PPL_CXX_interface */
enum Degenerate_Element {
  //! The universe element, i.e., the whole vector space.
  UNIVERSE,
  //! The empty element, i.e., the empty set.
  EMPTY
};

//! Relation symbols.
/*! \ingroup PPL_CXX_interface */
// This must be kept in sync with Result
enum Relation_Symbol {
  //! \hideinitializer Equal to.
  EQUAL = 1U,
  //! \hideinitializer Less than.
  LESS_THAN = 2U,
  //! \hideinitializer Less than or equal to.
  LESS_OR_EQUAL = LESS_THAN | EQUAL,
  //! \hideinitializer Greater than.
  GREATER_THAN = 4U,
  //! \hideinitializer Greater than or equal to.
  GREATER_OR_EQUAL = GREATER_THAN | EQUAL,
  //! \hideinitializer Not equal to.
  NOT_EQUAL = LESS_THAN | GREATER_THAN
};

//! Complexity pseudo-classes.
/*! \ingroup PPL_CXX_interface */
enum Complexity_Class {
  //! Worst-case polynomial complexity.
  POLYNOMIAL_COMPLEXITY,
  //! Worst-case exponential complexity but typically polynomial behavior.
  SIMPLEX_COMPLEXITY,
  //! Any complexity.
  ANY_COMPLEXITY
};

//! Possible optimization modes.
/*! \ingroup PPL_CXX_interface */
enum Optimization_Mode {
  //! Minimization is requested.
  MINIMIZATION,
  //! Maximization is requested.
  MAXIMIZATION
};

/*! \ingroup PPL_CXX_interface \brief
  Widths of bounded integer types.

  See the section on
  \ref Approximating_Bounded_Integers "approximating bounded integers".
*/
enum Bounded_Integer_Type_Width {
  //! \hideinitializer 8 bits.
  BITS_8 = 8,

  //! \hideinitializer 16 bits.
  BITS_16 = 16,

  //! \hideinitializer 32 bits.
  BITS_32 = 32,

  //! \hideinitializer 64 bits.
  BITS_64 = 64,

  //! \hideinitializer 128 bits.
  BITS_128 = 128
};

/*! \ingroup PPL_CXX_interface \brief
  Representation of bounded integer types.

  See the section on
  \ref Approximating_Bounded_Integers "approximating bounded integers".
*/
enum Bounded_Integer_Type_Representation {
  //! Unsigned binary.
  UNSIGNED,

  /*! \brief
    Signed binary where negative values are represented by the two's
    complement of the absolute value.
  */
  SIGNED_2_COMPLEMENT
};

/*! \ingroup PPL_CXX_interface \brief
  Overflow behavior of bounded integer types.

  See the section on
  \ref Approximating_Bounded_Integers "approximating bounded integers".
*/
enum Bounded_Integer_Type_Overflow {
  /*! \brief
    On overflow, wrapping takes place.

    This means that, for a \f$w\f$-bit bounded integer, the computation
    happens modulo \f$2^w\f$.
  */
  OVERFLOW_WRAPS,

  /*! \brief
    On overflow, the result is undefined.

    This simply means that the result of the operation resulting in an
    overflow can take any value.

    \note
    Even though something more serious can happen in the system
    being analyzed ---due to, e.g., C's undefined behavior---, here we
    are only concerned with the results of arithmetic operations.
    It is the responsibility of the analyzer to ensure that other
    manifestations of undefined behavior are conservatively approximated.
  */
  OVERFLOW_UNDEFINED,

  /*! \brief
    Overflow is impossible.

    This is for the analysis of languages where overflow is trapped
    before it affects the state, for which, thus, any indication that
    an overflow may have affected the state is necessarily due to
    the imprecision of the analysis.
  */
  OVERFLOW_IMPOSSIBLE
};

/*! \ingroup PPL_CXX_interface \brief
  Possible representations of coefficient sequences (i.e. linear expressions
  and more complex objects containing linear expressions, e.g. Constraints,
  Generators, etc.).
*/
enum Representation {
  /*! \brief
    Dense representation: the coefficient sequence is represented as a vector
    of coefficients, including the zero coefficients.
    If there are only a few nonzero coefficients, this representation is
    faster and also uses a bit less memory.
  */
  DENSE,

  /*! \brief
    Sparse representation: only the nonzero coefficient are stored.
    If there are many nonzero coefficients, this improves memory consumption
    and run time (both because there is less data to process in O(n)
    operations and because finding zeroes/nonzeroes is much faster since
    zeroes are not stored at all, so any stored coefficient is nonzero).
  */
  SPARSE
};

/*! \ingroup PPL_CXX_interface \brief
  Floating point formats known to the library.

  The parameters of each format are defined by a specific struct
  in file Float_defs.hh.  See the section on \ref floating_point
  "Analysis of floating point computations" for more information.
*/
enum Floating_Point_Format {
  //! IEEE 754 half precision, 16 bits (5 exponent, 10 mantissa).
  IEEE754_HALF,

  //! IEEE 754 single precision, 32 bits (8 exponent, 23 mantissa).
  IEEE754_SINGLE,

  //! IEEE 754 double precision, 64 bits (11 exponent, 52 mantissa).
  IEEE754_DOUBLE,

  //! IEEE 754 quad precision, 128 bits (15 exponent, 112 mantissa).
  IEEE754_QUAD,

  //! Intel double extended precision, 80 bits (15 exponent, 64 mantissa)
  INTEL_DOUBLE_EXTENDED,

  //! IBM single precision, 32 bits (7 exponent, 24 mantissa).
  IBM_SINGLE,

  //! IBM double precision, 64 bits (7 exponent, 56 mantissa).
  IBM_DOUBLE
};

struct Weightwatch_Traits;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Concrete_Expression_types.hh line 1. */


namespace Parma_Polyhedra_Library {

/*
  NOTE: Doxygen seems to ignore documentation blocks attached to
  template class declarations that are not provided with a definition.
  This justifies (here below) the explicit use of Doxygen command \class.
*/

/*! \brief The base class of all concrete expressions.
  \class Parma_Polyhedra_Library::Concrete_Expression
*/
template <typename Target>
class Concrete_Expression;

/*! \brief A binary operator applied to two concrete expressions.
  \class Parma_Polyhedra_Library::Binary_Operator
*/
template <typename Target>
class Binary_Operator;

/*! \brief A unary operator applied to one concrete expression.
  \class Parma_Polyhedra_Library::Unary_Operator
*/
template <typename Target>
class Unary_Operator;

/*! \brief A cast operator converting one concrete expression to some type.
  \class Parma_Polyhedra_Library::Cast_Operator
*/
template <typename Target>
class Cast_Operator;

/*! \brief An integer constant concrete expression.
  \class Parma_Polyhedra_Library::Integer_Constant
*/
template <typename Target>
class Integer_Constant;

/*! \brief A floating-point constant concrete expression.
  \class Parma_Polyhedra_Library::Floating_Point_Constant
*/
template <typename Target>
class Floating_Point_Constant;

/*! \brief A concrete expression representing a reference to some approximable.
  \class Parma_Polyhedra_Library::Approximable_Reference
*/
template <typename Target>
class Approximable_Reference;

class Concrete_Expression_Type;

/*! \brief
  Encodes the kind of concrete expression.

  The values should be defined by the particular instance
  and uniquely identify one of: Binary_Operator, Unary_Operator,
  Cast_Operator, Integer_Constant, Floating_Point_Constant, or
  Approximable_Reference.  For example, the Binary_Operator kind
  integer constant should be defined by an instance as the member
  <CODE>Binary_Operator\<T\>::%KIND</CODE>.
*/
typedef int Concrete_Expression_Kind;

/*! \brief
  Encodes a binary operator of concrete expressions.

  The values should be uniquely defined by the particular instance and
  named: ADD, SUB, MUL, DIV, REM, BAND, BOR, BXOR, LSHIFT, RSHIFT.
*/
typedef int Concrete_Expression_BOP;

/*! \brief
  Encodes a unary operator of concrete expressions.

  The values should be uniquely defined by the particular instance and
  named: PLUS, MINUS, BNOT.
*/
typedef int Concrete_Expression_UOP;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Variable_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Variable;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Form_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename C>
class Linear_Form;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Float_defs.hh line 33. */
#include <set>
#include <cmath>
#include <map>
#include <gmp.h>

#ifdef NAN
#define PPL_NAN NAN
#else
#define PPL_NAN (HUGE_VAL - HUGE_VAL)
#endif

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct float_ieee754_half {
  uint16_t word;
  static const uint16_t SGN_MASK = 0x8000U;
  static const uint16_t EXP_MASK = 0xfc00U;
  static const uint16_t WRD_MAX = 0x7bffU;
  static const uint16_t POS_INF = 0x7c00U;
  static const uint16_t NEG_INF = 0xfc00U;
  static const uint16_t POS_ZERO = 0x0000U;
  static const uint16_t NEG_ZERO = 0x8000U;
  static const unsigned int BASE = 2;
  static const unsigned int EXPONENT_BITS = 5;
  static const unsigned int MANTISSA_BITS = 10;
  static const int EXPONENT_MAX = (1 << (EXPONENT_BITS - 1)) - 1;
  static const int EXPONENT_BIAS = EXPONENT_MAX;
  static const int EXPONENT_MIN = -EXPONENT_MAX + 1;
  static const int EXPONENT_MIN_DENORM = EXPONENT_MIN
                                        - static_cast<int>(MANTISSA_BITS);
  static const Floating_Point_Format floating_point_format = IEEE754_HALF;
  int inf_sign() const;
  bool is_nan() const;
  int zero_sign() const;
  bool sign_bit() const;
  void negate();
  void dec();
  void inc();
  void set_max(bool negative);
  void build(bool negative, mpz_t mantissa, int exponent);

};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct float_ieee754_single {
  uint32_t word;
  static const uint32_t SGN_MASK = 0x80000000U;
  static const uint32_t EXP_MASK = 0x7f800000U;
  static const uint32_t WRD_MAX = 0x7f7fffffU;
  static const uint32_t POS_INF = 0x7f800000U;
  static const uint32_t NEG_INF = 0xff800000U;
  static const uint32_t POS_ZERO = 0x00000000U;
  static const uint32_t NEG_ZERO = 0x80000000U;
  static const unsigned int BASE = 2;
  static const unsigned int EXPONENT_BITS = 8;
  static const unsigned int MANTISSA_BITS = 23;
  static const int EXPONENT_MAX = (1 << (EXPONENT_BITS - 1)) - 1;
  static const int EXPONENT_BIAS = EXPONENT_MAX;
  static const int EXPONENT_MIN = -EXPONENT_MAX + 1;
  static const int EXPONENT_MIN_DENORM = EXPONENT_MIN
                                        - static_cast<int>(MANTISSA_BITS);
  static const Floating_Point_Format floating_point_format = IEEE754_SINGLE;
  int inf_sign() const;
  bool is_nan() const;
  int zero_sign() const;
  bool sign_bit() const;
  void negate();
  void dec();
  void inc();
  void set_max(bool negative);
  void build(bool negative, mpz_t mantissa, int exponent);
};

#ifdef WORDS_BIGENDIAN
#ifndef PPL_WORDS_BIGENDIAN
#define PPL_WORDS_BIGENDIAN
#endif
#endif

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct float_ieee754_double {
#ifdef PPL_WORDS_BIGENDIAN
  uint32_t msp;
  uint32_t lsp;
#else
  uint32_t lsp;
  uint32_t msp;
#endif
  static const uint32_t MSP_SGN_MASK = 0x80000000U;
  static const uint32_t MSP_POS_INF = 0x7ff00000U;
  static const uint32_t MSP_NEG_INF = 0xfff00000U;
  static const uint32_t MSP_POS_ZERO = 0x00000000U;
  static const uint32_t MSP_NEG_ZERO = 0x80000000U;
  static const uint32_t LSP_INF = 0;
  static const uint32_t LSP_ZERO = 0;
  static const uint32_t MSP_MAX = 0x7fefffffU;
  static const uint32_t LSP_MAX = 0xffffffffU;
  static const unsigned int BASE = 2;
  static const unsigned int EXPONENT_BITS = 11;
  static const unsigned int MANTISSA_BITS = 52;
  static const int EXPONENT_MAX = (1 << (EXPONENT_BITS - 1)) - 1;
  static const int EXPONENT_BIAS = EXPONENT_MAX;
  static const int EXPONENT_MIN = -EXPONENT_MAX + 1;
  static const int EXPONENT_MIN_DENORM = EXPONENT_MIN
                                        - static_cast<int>(MANTISSA_BITS);
  static const Floating_Point_Format floating_point_format = IEEE754_DOUBLE;
  int inf_sign() const;
  bool is_nan() const;
  int zero_sign() const;
  bool sign_bit() const;
  void negate();
  void dec();
  void inc();
  void set_max(bool negative);
  void build(bool negative, mpz_t mantissa, int exponent);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct float_ibm_single {
  uint32_t word;
  static const uint32_t SGN_MASK = 0x80000000U;
  static const uint32_t EXP_MASK = 0x7f000000U;
  static const uint32_t WRD_MAX = 0x7fffffffU;
  static const uint32_t POS_INF = 0x7f000000U;
  static const uint32_t NEG_INF = 0xff000000U;
  static const uint32_t POS_ZERO = 0x00000000U;
  static const uint32_t NEG_ZERO = 0x80000000U;
  static const unsigned int BASE = 16;
  static const unsigned int EXPONENT_BITS = 7;
  static const unsigned int MANTISSA_BITS = 24;
  static const int EXPONENT_BIAS = 64;
  static const int EXPONENT_MAX = (1 << (EXPONENT_BITS - 1)) - 1;
  static const int EXPONENT_MIN = -EXPONENT_MAX + 1;
  static const int EXPONENT_MIN_DENORM = EXPONENT_MIN
                                        - static_cast<int>(MANTISSA_BITS);
  static const Floating_Point_Format floating_point_format = IBM_SINGLE;
  int inf_sign() const;
  bool is_nan() const;
  int zero_sign() const;
  bool sign_bit() const;
  void negate();
  void dec();
  void inc();
  void set_max(bool negative);
  void build(bool negative, mpz_t mantissa, int exponent);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct float_ibm_double {
  static const unsigned int BASE = 16;
  static const unsigned int EXPONENT_BITS = 7;
  static const unsigned int MANTISSA_BITS = 56;
  static const int EXPONENT_BIAS = 64;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct float_intel_double_extended {
#ifdef PPL_WORDS_BIGENDIAN
  uint32_t msp;
  uint64_t lsp;
#else
  uint64_t lsp;
  uint32_t msp;
#endif
  static const uint32_t MSP_SGN_MASK = 0x00008000U;
  static const uint32_t MSP_POS_INF = 0x00007fffU;
  static const uint32_t MSP_NEG_INF = 0x0000ffffU;
  static const uint32_t MSP_POS_ZERO = 0x00000000U;
  static const uint32_t MSP_NEG_ZERO = 0x00008000U;
  static const uint64_t LSP_INF = static_cast<uint64_t>(0x8000000000000000ULL);
  static const uint64_t LSP_ZERO = 0;
  static const uint32_t MSP_MAX = 0x00007ffeU;
  static const uint64_t LSP_DMAX = static_cast<uint64_t>(0x7fffffffffffffffULL);
  static const uint64_t LSP_NMAX = static_cast<uint64_t>(0xffffffffffffffffULL);
  static const unsigned int BASE = 2;
  static const unsigned int EXPONENT_BITS = 15;
  static const unsigned int MANTISSA_BITS = 63;
  static const int EXPONENT_MAX = (1 << (EXPONENT_BITS - 1)) - 1;
  static const int EXPONENT_BIAS = EXPONENT_MAX;
  static const int EXPONENT_MIN = -EXPONENT_MAX + 1;
  static const int EXPONENT_MIN_DENORM = EXPONENT_MIN
                                        - static_cast<int>(MANTISSA_BITS);
  static const Floating_Point_Format floating_point_format =
                                     INTEL_DOUBLE_EXTENDED;
  int inf_sign() const;
  bool is_nan() const;
  int zero_sign() const;
  bool sign_bit() const;
  void negate();
  void dec();
  void inc();
  void set_max(bool negative);
  void build(bool negative, mpz_t mantissa, int exponent);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct float_ieee754_quad {
#ifdef PPL_WORDS_BIGENDIAN
  uint64_t msp;
  uint64_t lsp;
#else
  uint64_t lsp;
  uint64_t msp;
#endif
  static const uint64_t MSP_SGN_MASK = static_cast<uint64_t>(0x8000000000000000ULL);
  static const uint64_t MSP_POS_INF = static_cast<uint64_t>(0x7fff000000000000ULL);
  static const uint64_t MSP_NEG_INF = static_cast<uint64_t>(0xffff000000000000ULL);
  static const uint64_t MSP_POS_ZERO = static_cast<uint64_t>(0x0000000000000000ULL);
  static const uint64_t MSP_NEG_ZERO = static_cast<uint64_t>(0x8000000000000000ULL);
  static const uint64_t LSP_INF = 0;
  static const uint64_t LSP_ZERO = 0;
  static const uint64_t MSP_MAX = static_cast<uint64_t>(0x7ffeffffffffffffULL);
  static const uint64_t LSP_MAX = static_cast<uint64_t>(0xffffffffffffffffULL);
  static const unsigned int BASE = 2;
  static const unsigned int EXPONENT_BITS = 15;
  static const unsigned int MANTISSA_BITS = 112;
  static const int EXPONENT_MAX = (1 << (EXPONENT_BITS - 1)) - 1;
  static const int EXPONENT_BIAS = EXPONENT_MAX;
  static const int EXPONENT_MIN = -EXPONENT_MAX + 1;
  static const int EXPONENT_MIN_DENORM = EXPONENT_MIN
                                        - static_cast<int>(MANTISSA_BITS);
  static const Floating_Point_Format floating_point_format = IEEE754_QUAD;
  int inf_sign() const;
  bool is_nan() const;
  int zero_sign() const;
  bool sign_bit() const;
  void negate();
  void dec();
  void inc();
  void set_max(bool negative);
  void build(bool negative, mpz_t mantissa, int exponent);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Float : public False { };

#if PPL_SUPPORTED_FLOAT
template <>
class Float<float> : public True {
public:
#if PPL_CXX_FLOAT_BINARY_FORMAT == PPL_FLOAT_IEEE754_HALF
  typedef float_ieee754_half Binary;
#elif PPL_CXX_FLOAT_BINARY_FORMAT == PPL_FLOAT_IEEE754_SINGLE
  typedef float_ieee754_single Binary;
#elif PPL_CXX_FLOAT_BINARY_FORMAT == PPL_FLOAT_IEEE754_DOUBLE
  typedef float_ieee754_double Binary;
#elif PPL_CXX_FLOAT_BINARY_FORMAT == PPL_FLOAT_IBM_SINGLE
  typedef float_ibm_single Binary;
#elif PPL_CXX_FLOAT_BINARY_FORMAT == PPL_FLOAT_IEEE754_QUAD
  typedef float_ieee754_quad Binary;
#elif PPL_CXX_FLOAT_BINARY_FORMAT == PPL_FLOAT_INTEL_DOUBLE_EXTENDED
  typedef float_intel_double_extended Binary;
#else
#error "Invalid value for PPL_CXX_FLOAT_BINARY_FORMAT"
#endif
  union {
    float number;
    Binary binary;
  } u;
  Float();
  Float(float v);
  float value();
};
#endif

#if PPL_SUPPORTED_DOUBLE
template <>
class Float<double> : public True {
public:
#if PPL_CXX_DOUBLE_BINARY_FORMAT == PPL_FLOAT_IEEE754_HALF
  typedef float_ieee754_half Binary;
#elif PPL_CXX_DOUBLE_BINARY_FORMAT == PPL_FLOAT_IEEE754_SINGLE
  typedef float_ieee754_single Binary;
#elif PPL_CXX_DOUBLE_BINARY_FORMAT == PPL_FLOAT_IEEE754_DOUBLE
  typedef float_ieee754_double Binary;
#elif PPL_CXX_DOUBLE_BINARY_FORMAT == PPL_FLOAT_IBM_SINGLE
  typedef float_ibm_single Binary;
#elif PPL_CXX_DOUBLE_BINARY_FORMAT == PPL_FLOAT_IEEE754_QUAD
  typedef float_ieee754_quad Binary;
#elif PPL_CXX_DOUBLE_BINARY_FORMAT == PPL_FLOAT_INTEL_DOUBLE_EXTENDED
  typedef float_intel_double_extended Binary;
#else
#error "Invalid value for PPL_CXX_DOUBLE_BINARY_FORMAT"
#endif
  union {
    double number;
    Binary binary;
  } u;
  Float();
  Float(double v);
  double value();
};
#endif

#if PPL_SUPPORTED_LONG_DOUBLE
template <>
class Float<long double> : public True {
public:
#if PPL_CXX_LONG_DOUBLE_BINARY_FORMAT == PPL_FLOAT_IEEE754_HALF
  typedef float_ieee754_half Binary;
#elif PPL_CXX_LONG_DOUBLE_BINARY_FORMAT == PPL_FLOAT_IEEE754_SINGLE
  typedef float_ieee754_single Binary;
#elif PPL_CXX_LONG_DOUBLE_BINARY_FORMAT == PPL_FLOAT_IEEE754_DOUBLE
  typedef float_ieee754_double Binary;
#elif PPL_CXX_LONG_DOUBLE_BINARY_FORMAT == PPL_FLOAT_IBM_SINGLE
  typedef float_ibm_single Binary;
#elif PPL_CXX_LONG_DOUBLE_BINARY_FORMAT == PPL_FLOAT_IEEE754_QUAD
  typedef float_ieee754_quad Binary;
#elif PPL_CXX_LONG_DOUBLE_BINARY_FORMAT == PPL_FLOAT_INTEL_DOUBLE_EXTENDED
  typedef float_intel_double_extended Binary;
#else
#error "Invalid value for PPL_CXX_LONG_DOUBLE_BINARY_FORMAT"
#endif
  union {
    long double number;
    Binary binary;
  } u;
  Float();
  Float(long double v);
  long double value();
};
#endif

// FIXME: is this the right place for this function?
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  If \p v is nonzero, returns the position of the most significant bit
  in \p a.

  The behavior is undefined if \p v is zero.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
unsigned int msb_position(unsigned long long v);

/*! \brief
  An abstract class to be implemented by an external analyzer such
  as ECLAIR in order to provide to the PPL the necessary information
  for performing the analysis of floating point computations.

  \par Template type parameters

  - The class template parameter \p Target specifies the implementation
  of Concrete_Expression to be used.
  - The class template parameter \p FP_Interval_Type represents the type
  of the intervals used in the abstract domain. The interval bounds
  should have a floating point type.
*/
template <typename Target, typename FP_Interval_Type>
class FP_Oracle {
public:
  /*
    FIXME: the const qualifiers on expressions may raise CLANG
    compatibility issues. It may be necessary to omit them.
  */

  /*! \brief
    Asks the external analyzer for an interval that correctly
    approximates the floating point entity referenced by \p dim.
    Result is stored into \p result.

    \return <CODE>true</CODE> if the analyzer was able to find a correct
    approximation, <CODE>false</CODE> otherwise.
  */
  virtual bool get_interval(dimension_type dim, FP_Interval_Type& result) const
    = 0;

  /*! \brief
    Asks the external analyzer for an interval that correctly
    approximates the value of floating point constant \p expr.
    Result is stored into \p result.

    \return <CODE>true</CODE> if the analyzer was able to find a correct
    approximation, <CODE>false</CODE> otherwise.
  */
  virtual bool get_fp_constant_value(
               const Floating_Point_Constant<Target>& expr,
                     FP_Interval_Type& result) const = 0;

  /*! \brief
    Asks the external analyzer for an interval that correctly approximates
    the value of \p expr, which must be of integer type.
    Result is stored into \p result.

    \return <CODE>true</CODE> if the analyzer was able to find a correct
    approximation, <CODE>false</CODE> otherwise.
  */
  virtual bool get_integer_expr_value(const Concrete_Expression<Target>& expr,
                                      FP_Interval_Type& result) const = 0;

  /*! \brief
    Asks the external analyzer for the possible space dimensions that
    are associated to the approximable reference \p expr.
    Result is stored into \p result.

    \return <CODE>true</CODE> if the analyzer was able to return
    the (possibly empty!) set, <CODE>false</CODE> otherwise.

    The resulting set MUST NOT contain <CODE>not_a_dimension()</CODE>.
  */
  virtual bool get_associated_dimensions(
          const Approximable_Reference<Target>& expr,
          std::set<dimension_type>& result) const = 0;

};

/* FIXME: some of the following  documentation should probably be
   under PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS */

/*! \brief \relates Float
  Returns <CODE>true</CODE> if and only if there is some floating point
  number that is representable by \p f2 but not by \p f1.
*/
bool is_less_precise_than(Floating_Point_Format f1, Floating_Point_Format f2);

/*! \brief \relates Float
  Computes the absolute error of floating point computations.

  \par Template type parameters

  - The class template parameter \p FP_Interval_Type represents the type
  of the intervals used in the abstract domain. The interval bounds
  should have a floating point type.

  \param analyzed_format The floating point format used by the analyzed
  program.

  \return The interval \f$[-\omega, \omega]\f$ where \f$\omega\f$ is the
  smallest non-zero positive number in the less precise floating point
  format between the analyzer format and the analyzed format.
*/
template <typename FP_Interval_Type>
const FP_Interval_Type&
compute_absolute_error(Floating_Point_Format analyzed_format);

/*! \brief \relates Linear_Form
  Discards all linear forms containing variable \p var from the
  linear form abstract store \p lf_store.
*/
template <typename FP_Interval_Type>
void
discard_occurrences(std::map<dimension_type,
                             Linear_Form<FP_Interval_Type> >& lf_store,
                    Variable var);

/*! \brief \relates Linear_Form
  Assigns the linear form \p lf to \p var in the linear form abstract
  store \p lf_store, then discards all occurrences of \p var from it.
*/
template <typename FP_Interval_Type>
void
affine_form_image(std::map<dimension_type,
                           Linear_Form<FP_Interval_Type> >& lf_store,
                  Variable var,
                  const Linear_Form<FP_Interval_Type>& lf);

/*! \brief \relates Linear_Form
  Discards from \p ls1 all linear forms but those that are associated
  to the same variable in \p ls2.
*/
template <typename FP_Interval_Type>
void
upper_bound_assign(std::map<dimension_type,
                            Linear_Form<FP_Interval_Type> >& ls1,
                   const std::map<dimension_type,
                                  Linear_Form<FP_Interval_Type> >& ls2);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Float_inlines.hh line 1. */
/* IEC 559 floating point format related functions.
*/


/* Automatically generated from PPL source file ../src/Variable_defs.hh line 1. */
/* Variable class declaration.
*/


/* Automatically generated from PPL source file ../src/Init_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Init;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Variable_defs.hh line 30. */
#include <iosfwd>
#include <set>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Variable */
std::ostream&
operator<<(std::ostream& s, const Variable v);

} // namespace IO_Operators

//! Defines a total ordering on variables.
/*! \relates Variable */
bool less(Variable v, Variable w);

/*! \relates Variable */
void
swap(Variable& x, Variable& y);

} // namespace Parma_Polyhedra_Library

//! A dimension of the vector space.
/*! \ingroup PPL_CXX_interface
  An object of the class Variable represents a dimension of the space,
  that is one of the Cartesian axes.
  Variables are used as basic blocks in order to build
  more complex linear expressions.
  Each variable is identified by a non-negative integer,
  representing the index of the corresponding Cartesian axis
  (the first axis has index 0).
  The space dimension of a variable is the dimension of the vector space
  made by all the Cartesian axes having an index less than or equal to
  that of the considered variable; thus, if a variable has index \f$i\f$,
  its space dimension is \f$i+1\f$.

  Note that the ``meaning'' of an object of the class Variable
  is completely specified by the integer index provided to its
  constructor:
  be careful not to be mislead by C++ language variable names.
  For instance, in the following example the linear expressions
  <CODE>e1</CODE> and <CODE>e2</CODE> are equivalent,
  since the two variables <CODE>x</CODE> and <CODE>z</CODE> denote
  the same Cartesian axis.
  \code
  Variable x(0);
  Variable y(1);
  Variable z(0);
  Linear_Expression e1 = x + y;
  Linear_Expression e2 = y + z;
  \endcode

*/
class Parma_Polyhedra_Library::Variable {

public:
  //! Builds the variable corresponding to the Cartesian axis of index \p i.
  /*!
    \exception std::length_error
    Thrown if <CODE>i+1</CODE> exceeds
    <CODE>Variable::max_space_dimension()</CODE>.
  */
  explicit Variable(dimension_type i);

  //! Returns the index of the Cartesian axis associated to the variable.
  dimension_type id() const;

  //! Returns the maximum space dimension a Variable can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  /*!
    The returned value is <CODE>id()+1</CODE>.
  */
  dimension_type space_dimension() const;

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  //! Type of output functions.
  typedef void output_function_type(std::ostream& s, const Variable v);

  //! The default output function.
  static void default_output_function(std::ostream& s, const Variable v);

  //! Sets the output function to be used for printing Variable objects.
  static void set_output_function(output_function_type* p);

  //! Returns the pointer to the current output function.
  static output_function_type* get_output_function();

  //! Binary predicate defining the total ordering on variables.
  /*! \ingroup PPL_CXX_interface */
  struct Compare {
    //! Returns <CODE>true</CODE> if and only if \p x comes before \p y.
    bool operator()(Variable x, Variable y) const;
  };

  //! Swaps *this and v.
  void m_swap(Variable& v);

private:
  //! The index of the Cartesian axis.
  dimension_type varid;

  // The initialization class needs to set the default output function.
  friend class Init;

  friend std::ostream&
  Parma_Polyhedra_Library::IO_Operators::operator<<(std::ostream& s,
                                                    const Variable v);

  //! Pointer to the current output function.
  static output_function_type* current_output_function;

};

/* Automatically generated from PPL source file ../src/Variable_inlines.hh line 1. */
/* Variable class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/globals_defs.hh line 1. */
/* Declarations of global objects.
*/


/* Automatically generated from PPL source file ../src/C_Integer.hh line 1. */
/* C integers info.
*/


/* Automatically generated from PPL source file ../src/C_Integer.hh line 28. */
#include <climits>

// C99 defines LLONG_MIN, LLONG_MAX and ULLONG_MAX, but this part of
// C99 is not yet included into the C++ standard.
// GCC defines LONG_LONG_MIN, LONG_LONG_MAX and ULONG_LONG_MAX.
// Some compilers (such as Comeau C++ up to and including version 4.3.3)
// define nothing.  In this last case we make a reasonable guess.
#ifndef LLONG_MIN
#if defined(LONG_LONG_MIN)
#define LLONG_MIN LONG_LONG_MIN
#elif PPL_SIZEOF_LONG_LONG == 8
#define LLONG_MIN 0x8000000000000000LL
#endif
#endif

#ifndef LLONG_MAX
#if defined(LONG_LONG_MAX)
#define LLONG_MAX LONG_LONG_MAX
#elif PPL_SIZEOF_LONG_LONG == 8
#define LLONG_MAX 0x7fffffffffffffffLL
#endif
#endif

#ifndef ULLONG_MAX
#if defined(ULONG_LONG_MAX)
#define ULLONG_MAX ULONG_LONG_MAX
#elif PPL_SIZEOF_LONG_LONG == 8
#define ULLONG_MAX 0xffffffffffffffffULL
#endif
#endif

namespace Parma_Polyhedra_Library {

template <typename T>
struct C_Integer : public False { };

template <>
struct C_Integer<char> : public True {
  enum const_bool_value {
#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
    is_signed = true
#else
    is_signed = false
#endif
  };
  typedef void smaller_type;
  typedef void smaller_signed_type;
  typedef void smaller_unsigned_type;
#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
  typedef unsigned char other_type;
#else
  typedef signed char other_type;
#endif
  static const char min = static_cast<char>(CHAR_MIN);
  static const char max = static_cast<char>(CHAR_MAX);
};

template <>
struct C_Integer<signed char> : public True {
  enum const_bool_value {
    is_signed = true
  };
  typedef void smaller_type;
  typedef void smaller_signed_type;
  typedef void smaller_unsigned_type;
  typedef unsigned char other_type;
  static const signed char min = static_cast<signed char>(SCHAR_MIN);
  static const signed char max = static_cast<signed char>(SCHAR_MAX);
};

template <>
struct C_Integer<signed short> : public True {
  enum const_bool_value {
    is_signed = true
  };
  typedef signed char smaller_type;
  typedef signed char smaller_signed_type;
  typedef unsigned char smaller_unsigned_type;
  typedef unsigned short other_type;
  static const signed short min = static_cast<signed short>(SHRT_MIN);
  static const signed short max = static_cast<signed short>(SHRT_MAX);
};

template <>
struct C_Integer<signed int> : public True {
  enum const_bool_value {
    is_signed = true
  };
  typedef signed short smaller_type;
  typedef signed short smaller_signed_type;
  typedef unsigned short smaller_unsigned_type;
  typedef unsigned int other_type;
  static const signed int min = INT_MIN;
  static const signed int max = INT_MAX;
};

template <>
struct C_Integer<signed long> : public True {
  enum const_bool_value {
    is_signed = true
  };
  typedef signed int smaller_type;
  typedef signed int smaller_signed_type;
  typedef unsigned int smaller_unsigned_type;
  typedef unsigned long other_type;
  static const signed long min = LONG_MIN;
  static const signed long max = LONG_MAX;
};

template <>
struct C_Integer<signed long long> : public True {
  enum const_bool_value {
    is_signed = true
  };
  typedef signed long smaller_type;
  typedef signed long smaller_signed_type;
  typedef unsigned long smaller_unsigned_type;
  typedef unsigned long long other_type;
  static const signed long long min = LLONG_MIN;
  static const signed long long max = LLONG_MAX;
};

template <>
struct C_Integer<unsigned char> : public True {
  enum const_bool_value {
    is_signed = false
  };
  typedef void smaller_type;
  typedef void smaller_signed_type;
  typedef void smaller_unsigned_type;
  typedef signed char other_type;
  static const unsigned char min = static_cast<unsigned char>(0U);
  static const unsigned char max = static_cast<unsigned char>(UCHAR_MAX);
};

template <>
struct C_Integer<unsigned short> : public True {
  enum const_bool_value {
    is_signed = false
  };
  typedef unsigned char smaller_type;
  typedef signed char smaller_signed_type;
  typedef unsigned char smaller_unsigned_type;
  typedef signed short other_type;
  static const unsigned short min = static_cast<unsigned short>(0U);
  static const unsigned short max = static_cast<unsigned short>(USHRT_MAX);
};

template <>
struct C_Integer<unsigned int> : public True {
  enum const_bool_value {
    is_signed = false
  };
  typedef unsigned short smaller_type;
  typedef signed short smaller_signed_type;
  typedef unsigned short smaller_unsigned_type;
  typedef signed int other_type;
  static const unsigned int min = 0U;
  static const unsigned int max = UINT_MAX;
};

template <>
struct C_Integer<unsigned long> : public True {
  enum const_bool_value {
    is_signed = false
  };
  typedef unsigned int smaller_type;
  typedef signed int smaller_signed_type;
  typedef unsigned int smaller_unsigned_type;
  typedef signed long other_type;
  static const unsigned long min = 0UL;
  static const unsigned long max = ULONG_MAX;
};

template <>
struct C_Integer<unsigned long long> : public True {
  enum const_bool_value {
    is_signed = false
  };
  typedef unsigned long smaller_type;
  typedef signed long smaller_signed_type;
  typedef unsigned long smaller_unsigned_type;
  typedef signed long long other_type;
  static const unsigned long long min = 0ULL;
  static const unsigned long long max = ULLONG_MAX;
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/globals_defs.hh line 32. */
#include <exception>
#include <gmpxx.h>

#ifndef PPL_PROFILE_ADD_WEIGHT
#define PPL_PROFILE_ADD_WEIGHT 0
#endif

#if defined(NDEBUG) && PPL_PROFILE_ADD_WEIGHT
/* Automatically generated from PPL source file ../src/Weight_Profiler_defs.hh line 1. */
/* Weight_Profiler class declaration.
*/

#ifndef Weight_Profiler_defs_hh
#define Weight_Profiler_defs_hh 1

#include <cassert>

namespace Parma_Polyhedra_Library {

class Weight_Profiler {
private:
  enum { DISCARDED = 0, VALID = 1 };

public:
  Weight_Profiler(const char* file, int line,
                  Weightwatch_Traits::Delta delta,
                  double min_threshold = 0, double max_threshold = 0)
    : file(file), line(line), delta(delta),
      min_threshold(min_threshold), max_threshold(max_threshold) {
    for (int i = 0; i < 2; ++i) {
      stat[i].samples = 0;
      stat[i].count = 0;
      stat[i].sum = 0;
      stat[i].squares_sum = 0;
      stat[i].min = 0;
      stat[i].max = 0;
    }
  }

  ~Weight_Profiler() {
    output_stats();
  }

  void output_stats();

  static void begin() {
#ifndef NDEBUG
    int r = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &stamp);
    assert(r >= 0);
#else
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &stamp);
#endif
  }

  void end(unsigned int factor = 1) {
    Weightwatch_Traits::weight
      += (Weightwatch_Traits::Threshold) delta * factor;
    struct timespec start = stamp;
    begin();
    double elapsed;
    if (stamp.tv_nsec >= start.tv_nsec) {
      elapsed = (stamp.tv_nsec - start.tv_nsec)
        + (stamp.tv_sec - start.tv_sec) * 1e9;
    }
    else {
      elapsed = (1000000000 - start.tv_nsec + stamp.tv_nsec )
        + (stamp.tv_sec - start.tv_sec - 1) * 1e9;
    }
    elapsed -= adjustment;
    double elapsed1 = elapsed / factor;
    int i = (elapsed1 < min_threshold
             || (max_threshold > 0 && elapsed1 > max_threshold))
      ? DISCARDED
      : VALID;
    ++stat[i].samples;
    if (stat[i].count == 0) {
      stat[i].min = stat[i].max = elapsed1;
    }
    else if (stat[i].min > elapsed1) {
      stat[i].min = elapsed1;
    }
    else if (stat[i].max < elapsed1) {
      stat[i].max = elapsed1;
    }
    stat[i].sum += elapsed;
    stat[i].squares_sum += elapsed * elapsed1;
    stat[i].count += factor;
  }

  static double tune_adjustment();

 private:
  //! File of this profiling point.
  const char *file;

  //! Line of this profiling point.
  int line;

  //! Computational weight to be added at each iteration.
  Weightwatch_Traits::Delta delta;

  //! Times less than this value are discarded.
  double min_threshold;

  //! Times greater than this value are discarded.
  double max_threshold;

  //! Statistical data for samples (both DISCARDED and VALID)
  struct {
    //! Number of collected samples.
    unsigned int samples;

    /*! \brief
      Number of collected iterations.

      \note
      Multiple iterations are possibly collected for each sample.
    */
    unsigned int count;

    //! Sum of the measured times.
    double sum;

    //! Sum of the squares of the measured times (to compute variance).
    double squares_sum;

    //! Minimum measured time.
    double min;

    //! Maximum measured time.
    double max;
  } stat[2];

  //! Holds the time corresponding to last time begin() was called.
  static struct timespec stamp;

  /*! \brief
    Time quantity used to adjust the elapsed times so as not to take
    into account the time spent by the measurement infrastructure.
  */
  static double adjustment;
};

}

#endif // Weight_Profiler_defs_hh
/* Automatically generated from PPL source file ../src/globals_defs.hh line 41. */
#endif

#if defined(NDEBUG)

#if PPL_PROFILE_ADD_WEIGHT

#define WEIGHT_BEGIN() Weight_Profiler::begin()

#define WEIGHT_ADD(delta)                                     \
  do {                                                        \
    static Weight_Profiler wp__(__FILE__, __LINE__, delta);   \
    wp__.end();                                               \
  } while (false)

#define WEIGHT_ADD_MUL(delta, factor)                                   \
  do {                                                                  \
    static Weight_Profiler wp__(__FILE__, __LINE__, delta);             \
    wp__.end(factor);                                                   \
  } while (false)

#else // !PPL_PROFILE_ADD_WEIGHT

#define WEIGHT_BEGIN()                          \
  do {                                          \
  } while (false)

#define WEIGHT_ADD(delta)                       \
  do {                                          \
    Weightwatch_Traits::weight += (delta);      \
  } while (false)

#define WEIGHT_ADD_MUL(delta, factor)                   \
  do {                                                  \
    Weightwatch_Traits::weight += (delta)*(factor);     \
  } while (false)

#endif // !PPL_PROFILE_ADD_WEIGHT

#else // !defined(NDEBUG)

#define WEIGHT_BEGIN()

#define WEIGHT_ADD(delta)                       \
  do {                                          \
    if (!In_Assert::asserting()) {              \
      Weightwatch_Traits::weight += delta;      \
    }                                           \
  } while (false)

#define WEIGHT_ADD_MUL(delta, factor)                   \
  do {                                                  \
    if (!In_Assert::asserting()) {                      \
      Weightwatch_Traits::weight += delta * factor;     \
    }                                                   \
  } while (false)

#endif // !defined(NDEBUG)


namespace Parma_Polyhedra_Library {

//! Returns a value that does not designate a valid dimension.
dimension_type
not_a_dimension();

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns the hash code for space dimension \p dim.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
int32_t
hash_code_from_dimension(dimension_type dim);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Make sure swap() is specialized when needed.

  This will cause a compile-time error whenever a specialization for \p T
  is beneficial but missing.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
inline typename Enable_If<Slow_Copy<T>::value, void>::type
swap(T&, T&) {
  PPL_COMPILE_TIME_CHECK(!Slow_Copy<T>::value, "missing swap specialization");
}

/*! \brief
  Declare a local variable named \p id, of type Coefficient, and containing
  an unknown initial value.

  Use of this macro to declare temporaries of type Coefficient results
  in decreased memory allocation overhead and in better locality.
*/
#define PPL_DIRTY_TEMP_COEFFICIENT(id) \
PPL_DIRTY_TEMP(Parma_Polyhedra_Library::Coefficient, id)

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Speculative allocation function.
/*!
  \return
  The actual capacity to be allocated.

  \param requested_size
  The number of elements we need.

  \param maximum_size
  The maximum number of elements to be allocated. It is assumed
  to be no less than \p requested_size.

  Computes a capacity given a requested size.
  Allows for speculative allocation aimed at reducing the number of
  reallocations enough to guarantee amortized constant insertion time
  for our vector-like data structures. In all cases, the speculative
  allocation will not exceed \p maximum_size.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
dimension_type
compute_capacity(dimension_type requested_size,
                 dimension_type maximum_size);


#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Traits class for the deterministic timeout mechanism.
/*! \ingroup PPL_CXX_interface
  This abstract base class should be instantiated by those users
  willing to provide a polynomial upper bound to the time spent
  by any invocation of a library operator.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct Weightwatch_Traits {
  //! The type used to specify thresholds for computational weight.
  typedef unsigned long long Threshold;

  //! The type used to specify increments of computational weight.
  typedef unsigned long long Delta;

  //! Returns the current computational weight.
  static const Threshold& get();

  //! Compares the two weights \p a and \p b.
  static bool less_than(const Threshold& a, const Threshold& b);

  //! Computes a \c Delta value from \p unscaled and \p scale.
  /*!
    \return
    \f$u \cdot 2^s\f$, where \f$u\f$ is the value of \p unscaled and
    \f$s\f$ is the value of \p scale.

    \param unscaled
    The value of delta before scaling.

    \param scale
    The scaling to be applied to \p unscaled.
  */
  static Delta compute_delta(unsigned long unscaled, unsigned scale);

  //! Sets \p threshold to be \p delta units bigger than the current weight.
  static void from_delta(Threshold& threshold, const Delta& delta);

  //! The current computational weight.
  static Threshold weight;

  /*! \brief
    A pointer to the function that has to be called when checking
    the reaching of thresholds.

    The pointer can be null if no thresholds are set.
  */
  static void (*check_function)(void);
};


#ifndef NDEBUG

class In_Assert {
private:
  //! Non zero during evaluation of PPL_ASSERT expression.
  static unsigned int count;
public:
  In_Assert() {
    ++count;
  }
  ~In_Assert() {
    --count;
  }
  static bool asserting() {
    return count != 0;
  }
};

#endif


//! User objects the PPL can throw.
/*! \ingroup PPL_CXX_interface
  This abstract base class should be instantiated by those users
  willing to provide a polynomial upper bound to the time spent
  by any invocation of a library operator.
*/
class Throwable {
public:
  //! Throws the user defined exception object.
  virtual void throw_me() const = 0;

  //! Virtual destructor.
  virtual ~Throwable();
};

/*! \brief
  A pointer to an exception object.

  \ingroup PPL_CXX_interface
  This pointer, which is initialized to zero, is repeatedly checked
  along any super-linear (i.e., computationally expensive) computation
  path in the library.
  When it is found nonzero the exception it points to is thrown.
  In other words, making this pointer point to an exception (and
  leaving it in this state) ensures that the library will return
  control to the client application, possibly by throwing the given
  exception, within a time that is a linear function of the size
  of the representation of the biggest object (powerset of polyhedra,
  polyhedron, system of constraints or generators) on which the library
  is operating upon.

  \note
  The only sensible way to assign to this pointer is from within a
  signal handler or from a parallel thread.  For this reason, the
  library, apart from ensuring that the pointer is initially set to zero,
  never assigns to it.  In particular, it does not zero it again when
  the exception is thrown: it is the client's responsibility to do so.
*/
extern const Throwable* volatile abandon_expensive_computations;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  If the pointer abandon_expensive_computations is found
  to be nonzero, the exception it points to is thrown.

  \relates Throwable
*/
#endif
void
maybe_abandon();

//! A tag class.
/*! \ingroup PPL_CXX_interface
  Tag class to distinguish those constructors that recycle the data
  structures of their arguments, instead of taking a copy.
*/
struct Recycle_Input {
};

// Turn s into a string: PPL_STR(x + y) => "x + y".
#define PPL_STR(s) #s
// Turn the expansion of s into a string: PPL_XSTR(x) => "x expanded".
#define PPL_XSTR(s) PPL_STR(s)

#define PPL_OUTPUT_DECLARATIONS                                         \
  /*! \brief Writes to \c std::cerr an ASCII representation of \p *this. */ \
  void ascii_dump() const;                                              \
  /*! \brief Writes to \p s an ASCII representation of \p *this. */     \
  void ascii_dump(std::ostream& s) const;                               \
  /*! \brief Prints \p *this to \c std::cerr using \c operator<<. */    \
  void print() const;

#define PPL_OUTPUT_DEFINITIONS(class_name)                      \
  void                                                          \
  Parma_Polyhedra_Library::class_name::ascii_dump() const {     \
    ascii_dump(std::cerr);                                      \
  }                                                             \
                                                                \
  void                                                          \
  Parma_Polyhedra_Library::class_name::print() const {          \
    using IO_Operators::operator<<;                             \
    std::cerr << *this;                                         \
  }

#define PPL_OUTPUT_DEFINITIONS_ASCII_ONLY(class_name)                   \
  void                                                                  \
  Parma_Polyhedra_Library::class_name::ascii_dump() const {             \
    ascii_dump(std::cerr);                                              \
  }                                                                     \
                                                                        \
  void                                                                  \
  Parma_Polyhedra_Library::class_name::print() const {                  \
    std::cerr << "No user level output operator defined "               \
              << "for class " PPL_XSTR(class_name) << "." << std::endl; \
  }

#define PPL_OUTPUT_TEMPLATE_DEFINITIONS(type_symbol, class_prefix)      \
  template <typename type_symbol>                                       \
  void                                                                  \
  class_prefix::ascii_dump() const {                             \
    ascii_dump(std::cerr);                                              \
  }                                                                     \
                                                                        \
  template <typename type_symbol>                                       \
  void                                                                  \
  class_prefix::print() const {                                  \
    using IO_Operators::operator<<;                                     \
    std::cerr << *this;                                                 \
  }

#define PPL_OUTPUT_2_PARAM_TEMPLATE_DEFINITIONS(type_symbol1,           \
                                                type_symbol2,           \
                                                class_prefix)           \
  template <typename type_symbol1, typename type_symbol2>               \
  void                                                                  \
  PPL_U(class_prefix)<PPL_U(type_symbol1), PPL_U(type_symbol2)>         \
  ::ascii_dump() const {                                                \
    ascii_dump(std::cerr);                                              \
  }                                                                     \
                                                                        \
  template <typename type_symbol1, typename type_symbol2>               \
  void                                                                  \
  PPL_U(class_prefix)<PPL_U(type_symbol1), PPL_U(type_symbol2)>         \
  ::print() const {                                                     \
    using IO_Operators::operator<<;                                     \
    std::cerr << *this;                                                 \
  }

#define PPL_OUTPUT_3_PARAM_TEMPLATE_DEFINITIONS(type_symbol1,           \
                                                type_symbol2,           \
                                                type_symbol3,           \
                                                class_prefix)           \
  template <typename type_symbol1, typename type_symbol2,               \
            typename type_symbol3>                                      \
  void                                                                  \
  PPL_U(class_prefix)<PPL_U(type_symbol1), type_symbol2,                \
                      PPL_U(type_symbol3)>::ascii_dump()                \
    const {                                                             \
    ascii_dump(std::cerr);                                              \
  }                                                                     \
                                                                        \
    template <typename type_symbol1, typename type_symbol2,             \
              typename type_symbol3>                                    \
    void                                                                \
    PPL_U(class_prefix)<PPL_U(type_symbol1), type_symbol2,              \
                        PPL_U(type_symbol3)>::print()                   \
      const {                                                           \
      using IO_Operators::operator<<;                                   \
      std::cerr << *this;                                               \
    }

#define PPL_OUTPUT_TEMPLATE_DEFINITIONS_ASCII_ONLY(type_symbol, class_prefix) \
  template <typename type_symbol>                                       \
  void                                                                  \
  class_prefix::ascii_dump() const {                                    \
    ascii_dump(std::cerr);                                              \
  }                                                                     \
                                                                        \
  template <typename type_symbol>                                       \
  void                                                                  \
  class_prefix::print() const {                                         \
    std::cerr << "No user level output operator defined "               \
              << "for " PPL_XSTR(class_prefix) << "." << std::endl;     \
  }

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if \p c is any kind of space character.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool is_space(char c);

template <typename T, long long v, typename Enable = void>
struct Fit : public False {
};

template <typename T, long long v>
struct Fit<T, v, typename Enable_If<C_Integer<T>::value>::type>  {
  enum {
    value = (v >= static_cast<long long>(C_Integer<T>::min)
             && v <= static_cast<long long>(C_Integer<T>::max))
  };
};

template <typename T, T v>
struct TConstant {
  static const T value = v;
};


template <typename T, T v>
const T TConstant<T, v>::value;

template <typename T, long long v, bool prefer_signed = true,
          typename Enable = void>
struct Constant_ : public TConstant<T, v> {
};

//! \cond
// Keep Doxygen off until it learns how to deal properly with `||'.

template <typename T, long long v, bool prefer_signed>
struct Constant_<T, v, prefer_signed,
                 typename Enable_If<(Fit<typename C_Integer<T>::smaller_signed_type, v>::value
                                     && (prefer_signed
                                         || !Fit<typename C_Integer<T>::smaller_unsigned_type, v>::value))>::type>
  : public Constant_<typename C_Integer<T>::smaller_signed_type, v, prefer_signed> {
};

template <typename T, long long v, bool prefer_signed>
struct Constant_<T, v, prefer_signed,
                 typename Enable_If<(Fit<typename C_Integer<T>::smaller_unsigned_type, v>::value
                                     && (!prefer_signed
                                         || !Fit<typename C_Integer<T>::smaller_signed_type, v>::value))>::type>
  : public Constant_<typename C_Integer<T>::smaller_unsigned_type, v, prefer_signed> {
};

//! \endcond

template <long long v, bool prefer_signed = true>
struct Constant : public Constant_<long long, v, prefer_signed> {
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! \name Memory Size Inspection Functions
//@{
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  For native types, returns the total size in bytes of the memory
  occupied by the type of the (unused) parameter, i.e., 0.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
typename Enable_If<Is_Native<T>::value, memory_size_type>::type
total_memory_in_bytes(const T&);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  For native types, returns the size in bytes of the memory managed
  by the type of the (unused) parameter, i.e., 0.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
typename Enable_If<Is_Native<T>::value, memory_size_type>::type
external_memory_in_bytes(const T&);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns the total size in bytes of the memory occupied by \p x.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
memory_size_type
total_memory_in_bytes(const mpz_class& x);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns the size in bytes of the memory managed by \p x.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
memory_size_type
external_memory_in_bytes(const mpz_class& x);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns the total size in bytes of the memory occupied by \p x.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
memory_size_type
total_memory_in_bytes(const mpq_class& x);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns the size in bytes of the memory managed by \p x.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
memory_size_type
external_memory_in_bytes(const mpq_class& x);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//@} // Memory Size Inspection Functions
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)


template <typename T, typename Enable = void>
struct Has_OK : public False { };

template <typename T>
struct Has_OK<T, typename Enable_If_Is<bool (T::*)() const, &T::OK>::type>
  : public True {
};

template <typename T>
inline typename Enable_If<Has_OK<T>::value, bool>::type
f_OK(const T& to) {
  return to.OK();
}

#define FOK(T) inline bool f_OK(const T&) { return true; }

FOK(char)
FOK(signed char)
FOK(unsigned char)
FOK(signed short)
FOK(unsigned short)
FOK(signed int)
FOK(unsigned int)
FOK(signed long)
FOK(unsigned long)
FOK(signed long long)
FOK(unsigned long long)
FOK(float)
FOK(double)
FOK(long double)
FOK(mpz_class)
FOK(mpq_class)

void ascii_dump(std::ostream& s, Representation r);
bool ascii_load(std::istream& s, Representation& r);

dimension_type
check_space_dimension_overflow(dimension_type dim,
                               dimension_type max,
                               const char* domain,
                               const char* method,
                               const char* reason);

template <typename RA_Container>
typename RA_Container::iterator
nth_iter(RA_Container& cont, dimension_type n);

template <typename RA_Container>
typename RA_Container::const_iterator
nth_iter(const RA_Container& cont, dimension_type n);

dimension_type
least_significant_one_mask(dimension_type i);

} // namespace Parma_Polyhedra_Library

// By default, use sparse matrices both for MIP_Problem and PIP_Problem.
#ifndef PPL_USE_SPARSE_MATRIX
#define PPL_USE_SPARSE_MATRIX 1
#endif

/* Automatically generated from PPL source file ../src/globals_inlines.hh line 1. */
/* Implementation of global objects: inline functions.
*/


/* Automatically generated from PPL source file ../src/globals_inlines.hh line 28. */
#include <limits>
#include <cassert>
#include <istream>
#include <ostream>
#include <cctype>
#include <stdexcept>

namespace Parma_Polyhedra_Library {

inline dimension_type
not_a_dimension() {
  return std::numeric_limits<dimension_type>::max();
}

inline int32_t
hash_code_from_dimension(dimension_type dim) {
  const dimension_type divisor = 1U << (32 - 1);
  dim = dim % divisor;
  return static_cast<int32_t>(dim);
}

inline const Weightwatch_Traits::Threshold&
Weightwatch_Traits::get() {
  return weight;
}

inline bool
Weightwatch_Traits::less_than(const Threshold& a, const Threshold& b) {
  return b - a < (1ULL << (sizeof_to_bits(sizeof(Threshold)) - 1));
}

inline Weightwatch_Traits::Delta
Weightwatch_Traits::compute_delta(unsigned long unscaled, unsigned scale) {
  if ((std::numeric_limits<Delta>::max() >> scale) < unscaled) {
    throw std::invalid_argument("PPL::Weightwatch_Traits::"
                                "compute_delta(u, s):\n"
                                "values of u and s cause wrap around.");
  }
  return static_cast<Delta>(unscaled) << scale;
}

inline void
Weightwatch_Traits::from_delta(Threshold& threshold, const Delta& delta) {
  threshold = weight + delta;
}

inline void
maybe_abandon() {
#ifndef NDEBUG
  if (In_Assert::asserting()) {
    return;
  }
#endif
  if (Weightwatch_Traits::check_function != 0) {
    Weightwatch_Traits::check_function();
  }
  if (const Throwable* const p = abandon_expensive_computations) {
    p->throw_me();
  }
}

inline dimension_type
compute_capacity(const dimension_type requested_size,
                 const dimension_type maximum_size) {
  assert(requested_size <= maximum_size);
  // Speculation factor 2.
  return (requested_size < maximum_size/2)
    ? (2*(requested_size + 1))
    : maximum_size;
  // Speculation factor 1.5.
  // return (maximum_size - requested_size > requested_size/2)
  //   ? requested_size + requested_size/2 + 1
  //   : maximum_size;
}

template <typename T>
inline typename
Enable_If<Is_Native<T>::value, memory_size_type>::type
external_memory_in_bytes(const T&) {
  return 0;
}

template <typename T>
inline typename
Enable_If<Is_Native<T>::value, memory_size_type>::type
total_memory_in_bytes(const T&) {
  return sizeof(T);
}

inline memory_size_type
external_memory_in_bytes(const mpz_class& x) {
  return static_cast<memory_size_type>(x.get_mpz_t()[0]._mp_alloc)
    * PPL_SIZEOF_MP_LIMB_T;
}

inline memory_size_type
total_memory_in_bytes(const mpz_class& x) {
  return sizeof(x) + external_memory_in_bytes(x);
}

inline memory_size_type
external_memory_in_bytes(const mpq_class& x) {
  return external_memory_in_bytes(x.get_num())
    + external_memory_in_bytes(x.get_den());
}

inline memory_size_type
total_memory_in_bytes(const mpq_class& x) {
  return sizeof(x) + external_memory_in_bytes(x);
}

inline void
ascii_dump(std::ostream& s, Representation r) {
  if (r == DENSE) {
    s << "DENSE";
  }
  else {
    s << "SPARSE";
  }
}

inline bool
ascii_load(std::istream& is, Representation& r) {
  std::string s;
  if (!(is >> s)) {
    return false;
  }

  if (s == "DENSE")  {
    r = DENSE;
    return true;
  }
  if (s == "SPARSE")  {
    r = SPARSE;
    return true;
  }
  return false;
}

inline bool
is_space(char c) {
  return isspace(c) != 0;
}

template <typename RA_Container>
inline typename RA_Container::iterator
nth_iter(RA_Container& cont, dimension_type n) {
  typedef typename RA_Container::difference_type diff_t;
  return cont.begin() + static_cast<diff_t>(n);
}

template <typename RA_Container>
inline typename RA_Container::const_iterator
nth_iter(const RA_Container& cont, dimension_type n) {
  typedef typename RA_Container::difference_type diff_t;
  return cont.begin() + static_cast<diff_t>(n);
}

inline dimension_type
least_significant_one_mask(const dimension_type i) {
  return i & (~i + 1U);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/globals_defs.hh line 570. */

/* Automatically generated from PPL source file ../src/Variable_inlines.hh line 28. */
#include <stdexcept>

namespace Parma_Polyhedra_Library {

inline dimension_type
Variable::max_space_dimension() {
  return not_a_dimension() - 1;
}

inline
Variable::Variable(dimension_type i)
  : varid((i < max_space_dimension())
          ? i
          : (throw std::length_error("PPL::Variable::Variable(i):\n"
                                     "i exceeds the maximum allowed "
                                     "variable identifier."), i)) {
}

inline dimension_type
Variable::id() const {
  return varid;
}

inline dimension_type
Variable::space_dimension() const {
  return varid + 1;
}

inline memory_size_type
Variable::external_memory_in_bytes() const {
  return 0;
}

inline memory_size_type
Variable::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

inline void
Variable::set_output_function(output_function_type* p) {
  current_output_function = p;
}

inline Variable::output_function_type*
Variable::get_output_function() {
  return current_output_function;
}

/*! \relates Variable */
inline bool
less(const Variable v, const Variable w) {
  return v.id() < w.id();
}

inline bool
Variable::Compare::operator()(const Variable x, const Variable y) const {
  return less(x, y);
}

inline void
Variable::m_swap(Variable& v) {
  using std::swap;
  swap(varid, v.varid);
}

inline void
swap(Variable& x, Variable& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Variable_defs.hh line 156. */

/* Automatically generated from PPL source file ../src/Linear_Form_defs.hh line 1. */
/* Linear_Form class declaration.
*/


/* Automatically generated from PPL source file ../src/Linear_Expression_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Linear_Expression;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Box_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename Interval>
class Box;

class Box_Helpers;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Form_defs.hh line 32. */
#include <vector>

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Linear_Form */
template <typename C>
void swap(Linear_Form<C>& x, Linear_Form<C>& y);

// Put them in the namespace here to declare them friend later.

//! Returns the linear form \p f1 + \p f2.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator+(const Linear_Form<C>& f1, const Linear_Form<C>& f2);

//! Returns the linear form \p v + \p f.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator+(Variable v, const Linear_Form<C>& f);

//! Returns the linear form \p f + \p v.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator+(const Linear_Form<C>& f, Variable v);

//! Returns the linear form \p n + \p f.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator+(const C& n, const Linear_Form<C>& f);

//! Returns the linear form \p f + \p n.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator+(const Linear_Form<C>& f, const C& n);

//! Returns the linear form \p f.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator+(const Linear_Form<C>& f);

//! Returns the linear form - \p f.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator-(const Linear_Form<C>& f);

//! Returns the linear form \p f1 - \p f2.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator-(const Linear_Form<C>& f1, const Linear_Form<C>& f2);

//! Returns the linear form \p v - \p f.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator-(Variable v, const Linear_Form<C>& f);

//! Returns the linear form \p f - \p v.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator-(const Linear_Form<C>& f, Variable v);

//! Returns the linear form \p n - \p f.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator-(const C& n, const Linear_Form<C>& f);

//! Returns the linear form \p f - \p n.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator-(const Linear_Form<C>& f, const C& n);

//! Returns the linear form \p n * \p f.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator*(const C& n, const Linear_Form<C>& f);

//! Returns the linear form \p f * \p n.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator*(const Linear_Form<C>& f, const C& n);

//! Returns the linear form \p f1 + \p f2 and assigns it to \p e1.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>&
operator+=(Linear_Form<C>& f1, const Linear_Form<C>& f2);

//! Returns the linear form \p f + \p v and assigns it to \p f.
/*! \relates Linear_Form
  \exception std::length_error
  Thrown if the space dimension of \p v exceeds
  <CODE>Linear_Form::max_space_dimension()</CODE>.
 */
template <typename C>
Linear_Form<C>&
operator+=(Linear_Form<C>& f, Variable v);

//! Returns the linear form \p f + \p n and assigns it to \p f.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>&
operator+=(Linear_Form<C>& f, const C& n);

//! Returns the linear form \p f1 - \p f2 and assigns it to \p f1.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>&
operator-=(Linear_Form<C>& f1, const Linear_Form<C>& f2);

//! Returns the linear form \p f - \p v and assigns it to \p f.
/*! \relates Linear_Form
  \exception std::length_error
  Thrown if the space dimension of \p v exceeds
  <CODE>Linear_Form::max_space_dimension()</CODE>.
 */
template <typename C>
Linear_Form<C>&
operator-=(Linear_Form<C>& f, Variable v);

//! Returns the linear form \p f - \p n and assigns it to \p f.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>&
operator-=(Linear_Form<C>& f, const C& n);

//! Returns the linear form \p n * \p f and assigns it to \p f.
/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>&
operator*=(Linear_Form<C>& f, const C& n);

//! Returns the linear form \p f / \p n and assigns it to \p f.
/*!
   \relates Linear_Form
   Performs the division of a linear form by a scalar. It is up to the user to
   ensure that division by 0 is not performed.
*/
template <typename C>
Linear_Form<C>&
operator/=(Linear_Form<C>& f, const C& n);

//! Returns <CODE>true</CODE> if and only if \p x and \p y are equal.
/*! \relates Linear_Form */
template <typename C>
bool
operator==(const Linear_Form<C>& x, const Linear_Form<C>& y);

//! Returns <CODE>true</CODE> if and only if \p x and \p y are different.
/*! \relates Linear_Form */
template <typename C>
bool
operator!=(const Linear_Form<C>& x, const Linear_Form<C>& y);

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Linear_Form */
template <typename C>
std::ostream& operator<<(std::ostream& s, const Linear_Form<C>& f);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library

//! A linear form with interval coefficients.
/*! \ingroup PPL_CXX_interface
  An object of the class Linear_Form represents the interval linear form
  \f[
    \sum_{i=0}^{n-1} a_i x_i + b
  \f]
  where \f$n\f$ is the dimension of the vector space,
  each \f$a_i\f$ is the coefficient
  of the \f$i\f$-th variable \f$x_i\f$
  and \f$b\f$ is the inhomogeneous term.
  The coefficients and the inhomogeneous term of the linear form
  have the template parameter \p C as their type. \p C must be the
  type of an Interval.

  \par How to build a linear form.
  A full set of functions is defined in order to provide a convenient
  interface for building complex linear forms starting from simpler ones
  and from objects of the classes Variable and \p C. Available operators
  include binary addition and subtraction, as well as multiplication and
  division by a coefficient.
  The space dimension of a linear form is defined as
  the highest variable dimension among variables that have a nonzero
  coefficient in the linear form, or zero if no such variable exists.
  The space dimension for each variable \f$x_i\f$ is given by \f$i + 1\f$.

  \par Example
  Given the type \p T of an Interval with floating point coefficients (though
  any integral type may also be used), the following code builds the interval
  linear form \f$lf = x_5 - x_2 + 1\f$ with space dimension 6:
  \code
  Variable x5(5);
  Variable x2(2);
  T x5_coefficient;
  x5_coefficient.lower() = 2.0;
  x5_coefficient.upper() = 3.0;
  T inhomogeneous_term;
  inhomogeneous_term.lower() = 4.0;
  inhomogeneous_term.upper() = 8.0;
  Linear_Form<T> lf(x2);
  lf = -lf;
  lf += Linear_Form<T>(x2);
  Linear_Form<T> lf_x5(x5);
  lf_x5 *= x5_coefficient;
  lf += lf_x5;
  \endcode
  Note that \c lf_x5 is created with space dimension 6, while \c lf is
  created with space dimension 0 and then extended first to space
  dimension 2 when \c x2 is subtracted and finally to space dimension
  6 when \c lf_x5 is added.
*/
template <typename C>
class Parma_Polyhedra_Library::Linear_Form {
public:
  //! Default constructor: returns a copy of Linear_Form::zero().
  Linear_Form();

  //! Ordinary copy constructor.
  Linear_Form(const Linear_Form& f);

  //! Destructor.
  ~Linear_Form();

  //! Builds the linear form corresponding to the inhomogeneous term \p n.
  explicit Linear_Form(const C& n);

  //! Builds the linear form corresponding to the variable \p v.
  /*!
    \exception std::length_error
    Thrown if the space dimension of \p v exceeds
    <CODE>Linear_Form::max_space_dimension()</CODE>.
  */
  Linear_Form(Variable v);

  //! Builds a linear form approximating the linear expression \p e.
  Linear_Form(const Linear_Expression& e);

  //! Returns the maximum space dimension a Linear_Form can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  //! Returns the coefficient of \p v in \p *this.
  const C& coefficient(Variable v) const;

  //! Returns the inhomogeneous term of \p *this.
  const C& inhomogeneous_term() const;

  //! Negates all the coefficients of \p *this.
  void negate();

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  //! Swaps \p *this with \p y.
  void m_swap(Linear_Form& y);

  // Floating point analysis related methods.

  /*! \brief
    Verifies if the linear form overflows.

    \return
    Returns <CODE>false</CODE> if all coefficients in \p lf are bounded,
    <CODE>true</CODE> otherwise.

    \p T must be the type of possibly unbounded quantities.
  */
  bool overflows() const;

  /*! \brief
    Computes the relative error associated to floating point computations
    that operate on a quantity that is overapproximated by \p *this.

    \param analyzed_format The floating point format used by the analyzed
    program.
    \param result Becomes the linear form corresponding to the relative
    error committed.

    This method makes <CODE>result</CODE> become a linear form
    obtained by evaluating the function \f$\varepsilon_{\mathbf{f}}(l)\f$
    on the linear form. This function is defined as:
    \f[
    \varepsilon_{\mathbf{f}}\left([a, b]+\sum_{v \in \cV}[a_{v}, b_{v}]v\right)
    \defeq
    (\textrm{max}(|a|, |b|) \amifp [-\beta^{-\textrm{p}}, \beta^{-\textrm{p}}])
    +
    \sum_{v \in \cV}(\textrm{max}(|a_{v}|,|b_{v}|)
    \amifp
    [-\beta^{-\textrm{p}}, \beta^{-\textrm{p}}])v
    \f]
    where p is the fraction size in bits for the format \f$\mathbf{f}\f$ and
    \f$\beta\f$ the base.

    The result is undefined if \p T is not the type of an interval with
    floating point boundaries.
  */
  void relative_error(Floating_Point_Format analyzed_format,
                      Linear_Form& result) const;

  /*! \brief
    Makes \p result become an interval that overapproximates all the
    possible values of \p *this.

    \param oracle The FP_Oracle to be queried.
    \param result The linear form that will store the result.

    \return <CODE>true</CODE> if the operation was successful,
    <CODE>false</CODE> otherwise (the possibility of failure
    depends on the oracle's implementation).

    \par Template type parameters

    - The class template parameter \p Target specifies the implementation
    of Concrete_Expression to be used.

    This method makes <CODE>result</CODE> become
    \f$\iota(lf)\rho^{\#}\f$, that is an interval defined as:
    \f[
    \iota\left(i + \sum_{v \in \cV}i_{v}v\right)\rho^{\#}
    \defeq
    i \asifp \left(\bigoplus_{v \in \cV}{}^{\#}i_{v} \amifp
    \rho^{\#}(v)\right)
    \f]
    where \f$\rho^{\#}(v)\f$ is an interval (provided by the oracle)
    that correctly approximates the value of \f$v\f$.

    The result is undefined if \p C is not the type of an interval with
    floating point boundaries.
  */
  template <typename Target>
  bool intervalize(const FP_Oracle<Target,C>& oracle, C& result) const;

private:
  //! The generic coefficient equal to the singleton zero.
  static C zero;

  //! Type of the container vector.
  typedef std::vector<C> vec_type;

  //! The container vector.
  vec_type vec;

  //! Implementation sizing constructor.
  /*!
    The bool parameter is just to avoid problems with
    the constructor Linear_Form(const C& n).
  */
  Linear_Form(dimension_type sz, bool);

  /*! \brief
    Builds the linear form corresponding to the difference of
    \p v and \p w.

    \exception std::length_error
    Thrown if the space dimension of \p v or the one of \p w exceed
    <CODE>Linear_Form::max_space_dimension()</CODE>.
  */
  Linear_Form(Variable v, Variable w);

  //! Gives the number of generic coefficients currently in use.
  dimension_type size() const;

  //! Extends the vector of \p *this to size \p sz.
  void extend(dimension_type sz);

  //! Returns a reference to \p vec[i].
  C& operator[](dimension_type i);

  //! Returns a const reference to \p vec[i].
  const C& operator[](dimension_type i) const;

  friend Linear_Form<C>
  operator+<C>(const Linear_Form<C>& f1, const Linear_Form<C>& f2);
  friend Linear_Form<C>
  operator+<C>(const C& n, const Linear_Form<C>& f);
  friend Linear_Form<C>
  operator+<C>(const Linear_Form<C>& f, const C& n);
  friend Linear_Form<C>
  operator+<C>(Variable v, const Linear_Form<C>& f);

  friend Linear_Form<C>
  operator-<C>(const Linear_Form<C>& f);

  friend Linear_Form<C>
  operator-<C>(const Linear_Form<C>& f1, const Linear_Form<C>& f2);
  friend Linear_Form<C>
  operator-<C>(const C& n, const Linear_Form<C>& f);
  friend Linear_Form<C>
  operator-<C>(const Linear_Form<C>& f, const C& n);
  friend Linear_Form<C>
  operator-<C>(Variable v, const Linear_Form<C>& f);
  friend Linear_Form<C>
  operator-<C>(const Linear_Form<C>& f, Variable v);

  friend Linear_Form<C>
  operator*<C>(const C& n, const Linear_Form<C>& f);
  friend Linear_Form<C>
  operator*<C>(const Linear_Form<C>& f, const C& n);

  friend Linear_Form<C>&
  operator+=<C>(Linear_Form<C>& f1, const Linear_Form<C>& f2);
  friend Linear_Form<C>&
  operator+=<C>(Linear_Form<C>& f, Variable v);
  friend Linear_Form<C>&
  operator+=<C>(Linear_Form<C>& f, const C& n);

  friend Linear_Form<C>&
  operator-=<C>(Linear_Form<C>& f1, const Linear_Form<C>& f2);
  friend Linear_Form<C>&
  operator-=<C>(Linear_Form<C>& f, Variable v);
  friend Linear_Form<C>&
  operator-=<C>(Linear_Form<C>& f, const C& n);

  friend Linear_Form<C>&
  operator*=<C>(Linear_Form<C>& f, const C& n);

  friend Linear_Form<C>&
  operator/=<C>(Linear_Form<C>& f, const C& n);

  friend bool
  operator==<C>(const Linear_Form<C>& x, const Linear_Form<C>& y);

  friend std::ostream&
  Parma_Polyhedra_Library::IO_Operators
  ::operator<<<C>(std::ostream& s, const Linear_Form<C>& f);
};

/* Automatically generated from PPL source file ../src/Linear_Form_inlines.hh line 1. */
/* Linear_Form class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Linear_Form_inlines.hh line 28. */
#include <iostream>
#include <stdexcept>

namespace Parma_Polyhedra_Library {

template <typename C>
inline dimension_type
Linear_Form<C>::max_space_dimension() {
  return vec_type().max_size() - 1;
}

template <typename C>
inline
Linear_Form<C>::Linear_Form()
  : vec(1, zero) {
  vec.reserve(compute_capacity(1, vec_type().max_size()));
}

template <typename C>
inline
Linear_Form<C>::Linear_Form(dimension_type sz, bool)
  : vec(sz, zero) {
  vec.reserve(compute_capacity(sz, vec_type().max_size()));
}

template <typename C>
inline
Linear_Form<C>::Linear_Form(const Linear_Form& f)
  : vec(f.vec) {
}

template <typename C>
inline
Linear_Form<C>::~Linear_Form() {
}

template <typename C>
inline dimension_type
Linear_Form<C>::size() const {
  return vec.size();
}

template <typename C>
inline void
Linear_Form<C>::extend(dimension_type sz) {
  assert(sz > size());
  vec.reserve(compute_capacity(sz, vec_type().max_size()));
  vec.resize(sz, zero);
}

template <typename C>
inline
Linear_Form<C>::Linear_Form(const C& n)
  : vec(1, n) {
  vec.reserve(compute_capacity(1, vec_type().max_size()));
}

template <typename C>
inline dimension_type
Linear_Form<C>::space_dimension() const {
  return size() - 1;
}

template <typename C>
inline const C&
Linear_Form<C>::coefficient(Variable v) const {
  if (v.space_dimension() > space_dimension()) {
    return zero;
  }
  return vec[v.id()+1];
}

template <typename C>
inline C&
Linear_Form<C>::operator[](dimension_type i) {
  assert(i < size());
  return vec[i];
}

template <typename C>
inline const C&
Linear_Form<C>::operator[](dimension_type i) const {
  assert(i < size());
  return vec[i];
}

template <typename C>
inline const C&
Linear_Form<C>::inhomogeneous_term() const {
  return vec[0];
}

template <typename C>
inline memory_size_type
Linear_Form<C>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

/*! \relates Linear_Form */
template <typename C>
inline Linear_Form<C>
operator+(const Linear_Form<C>& f) {
  return f;
}

/*! \relates Linear_Form */
template <typename C>
inline Linear_Form<C>
operator+(const Linear_Form<C>& f, const C& n) {
  return n + f;
}

/*! \relates Linear_Form */
template <typename C>
inline Linear_Form<C>
operator+(const Linear_Form<C>& f, const Variable v) {
  return v + f;
}

/*! \relates Linear_Form */
template <typename C>
inline Linear_Form<C>
operator-(const Linear_Form<C>& f, const C& n) {
  return -n + f;
}

/*! \relates Linear_Form */
template <typename C>
inline Linear_Form<C>
operator-(const Variable v, const Variable w) {
  return Linear_Form<C>(v, w);
}

/*! \relates Linear_Form */
template <typename C>
inline Linear_Form<C>
operator*(const Linear_Form<C>& f, const C& n) {
  return n * f;
}

/*! \relates Linear_Form */
template <typename C>
inline Linear_Form<C>&
operator+=(Linear_Form<C>& f, const C& n) {
  f[0] += n;
  return f;
}

/*! \relates Linear_Form */
template <typename C>
inline Linear_Form<C>&
operator-=(Linear_Form<C>& f, const C& n) {
  f[0] -= n;
  return f;
}

/*! \relates Linear_Form */
template <typename C>
inline bool
operator!=(const Linear_Form<C>& x, const Linear_Form<C>& y) {
  return !(x == y);
}

template <typename C>
inline void
Linear_Form<C>::m_swap(Linear_Form& y) {
  using std::swap;
  swap(vec, y.vec);
}

template <typename C>
inline void
Linear_Form<C>::ascii_dump(std::ostream& s) const {
  using namespace IO_Operators;
  dimension_type space_dim = space_dimension();
  s << space_dim << "\n";
  for (dimension_type i = 0; i <= space_dim; ++i) {
    const char separator = ' ';
    s << vec[i] << separator;
  }
  s << "\n";
}

template <typename C>
inline bool
Linear_Form<C>::ascii_load(std::istream& s) {
  using namespace IO_Operators;
  dimension_type new_dim;
  if (!(s >> new_dim)) {
    return false;
  }

  vec.resize(new_dim + 1, zero);
  for (dimension_type i = 0; i <= new_dim; ++i) {
    if (!(s >> vec[i])) {
      return false;
    }
  }

  PPL_ASSERT(OK());
  return true;
}

// Floating point analysis related methods.
template <typename C>
inline bool
Linear_Form<C>::overflows() const {
  if (!inhomogeneous_term().is_bounded()) {
    return true;
  }
  for (dimension_type i = space_dimension(); i-- > 0; ) {
    if (!coefficient(Variable(i)).is_bounded()) {
      return true;
    }
  }

  return false;
}

/*! \relates Linear_Form */
template <typename C>
inline void
swap(Linear_Form<C>& x, Linear_Form<C>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Form_defs.hh line 497. */
// Linear_Form_templates.hh is not included here on purpose.

/* Automatically generated from PPL source file ../src/Float_inlines.hh line 30. */
#include <climits>

namespace Parma_Polyhedra_Library {

inline int
float_ieee754_half::inf_sign() const {
  if (word == NEG_INF) {
    return -1;
  }
  if (word == POS_INF) {
    return 1;
  }
  return 0;
}

inline bool
float_ieee754_half::is_nan() const {
  return (word & ~SGN_MASK) > POS_INF;
}

inline int
float_ieee754_half::zero_sign() const {
  if (word == NEG_ZERO) {
    return -1;
  }
  if (word == POS_ZERO) {
    return 1;
  }
  return 0;
}

inline void
float_ieee754_half::negate() {
  word ^= SGN_MASK;
}

inline bool
float_ieee754_half::sign_bit() const {
  return (word & SGN_MASK) != 0;
}

inline void
float_ieee754_half::dec() {
  --word;
}

inline void
float_ieee754_half::inc() {
  ++word;
}

inline void
float_ieee754_half::set_max(bool negative) {
  word = WRD_MAX;
  if (negative) {
    word |= SGN_MASK;
  }
}

inline void
float_ieee754_half::build(bool negative, mpz_t mantissa, int exponent) {
  word = static_cast<uint16_t>(mpz_get_ui(mantissa)
                               & ((1UL << MANTISSA_BITS) - 1));
  if (negative) {
    word |= SGN_MASK;
  }
  const int exponent_repr = exponent + EXPONENT_BIAS;
  PPL_ASSERT(exponent_repr >= 0 && exponent_repr < (1 << EXPONENT_BITS));
  word |= static_cast<uint16_t>(exponent_repr) << MANTISSA_BITS;
}

inline int
float_ieee754_single::inf_sign() const {
  if (word == NEG_INF) {
    return -1;
  }
  if (word == POS_INF) {
    return 1;
  }
  return 0;
}

inline bool
float_ieee754_single::is_nan() const {
  return (word & ~SGN_MASK) > POS_INF;
}

inline int
float_ieee754_single::zero_sign() const {
  if (word == NEG_ZERO) {
    return -1;
  }
  if (word == POS_ZERO) {
    return 1;
  }
  return 0;
}

inline void
float_ieee754_single::negate() {
  word ^= SGN_MASK;
}

inline bool
float_ieee754_single::sign_bit() const {
  return (word & SGN_MASK) != 0;
}

inline void
float_ieee754_single::dec() {
  --word;
}

inline void
float_ieee754_single::inc() {
  ++word;
}

inline void
float_ieee754_single::set_max(bool negative) {
  word = WRD_MAX;
  if (negative) {
    word |= SGN_MASK;
  }
}

inline void
float_ieee754_single::build(bool negative, mpz_t mantissa, int exponent) {
  word = static_cast<uint32_t>(mpz_get_ui(mantissa)
                               & ((1UL << MANTISSA_BITS) - 1));
  if (negative) {
    word |= SGN_MASK;
  }
  const int exponent_repr = exponent + EXPONENT_BIAS;
  PPL_ASSERT(exponent_repr >= 0 && exponent_repr < (1 << EXPONENT_BITS));
  word |= static_cast<uint32_t>(exponent_repr) << MANTISSA_BITS;
}

inline int
float_ieee754_double::inf_sign() const {
  if (lsp != LSP_INF) {
    return 0;
  }
  if (msp == MSP_NEG_INF) {
    return -1;
  }
  if (msp == MSP_POS_INF) {
    return 1;
  }
  return 0;
}

inline bool
float_ieee754_double::is_nan() const {
  const uint32_t a = msp & ~MSP_SGN_MASK;
  return a > MSP_POS_INF || (a == MSP_POS_INF && lsp != LSP_INF);
}

inline int
float_ieee754_double::zero_sign() const {
  if (lsp != LSP_ZERO) {
    return 0;
  }
  if (msp == MSP_NEG_ZERO) {
    return -1;
  }
  if (msp == MSP_POS_ZERO) {
    return 1;
  }
  return 0;
}

inline void
float_ieee754_double::negate() {
  msp ^= MSP_SGN_MASK;
}

inline bool
float_ieee754_double::sign_bit() const {
  return (msp & MSP_SGN_MASK) != 0;
}

inline void
float_ieee754_double::dec() {
  if (lsp == 0) {
    --msp;
    lsp = LSP_MAX;
  }
  else {
    --lsp;
  }
}

inline void
float_ieee754_double::inc() {
  if (lsp == LSP_MAX) {
    ++msp;
    lsp = 0;
  }
  else {
    ++lsp;
  }
}

inline void
float_ieee754_double::set_max(bool negative) {
  msp = MSP_MAX;
  lsp = LSP_MAX;
  if (negative) {
    msp |= MSP_SGN_MASK;
  }
}

inline void
float_ieee754_double::build(bool negative, mpz_t mantissa, int exponent) {
  unsigned long m;
#if ULONG_MAX == 0xffffffffUL
  lsp = mpz_get_ui(mantissa);
  mpz_tdiv_q_2exp(mantissa, mantissa, 32);
  m = mpz_get_ui(mantissa);
#else
  m = mpz_get_ui(mantissa);
  lsp = static_cast<uint32_t>(m & LSP_MAX);
  m >>= 32;
#endif
  msp = static_cast<uint32_t>(m & ((1UL << (MANTISSA_BITS - 32)) - 1));
  if (negative) {
    msp |= MSP_SGN_MASK;
  }
  const int exponent_repr = exponent + EXPONENT_BIAS;
  PPL_ASSERT(exponent_repr >= 0 && exponent_repr < (1 << EXPONENT_BITS));
  msp |= static_cast<uint32_t>(exponent_repr) << (MANTISSA_BITS - 32);
}

inline int
float_ibm_single::inf_sign() const {
  if (word == NEG_INF) {
    return -1;
  }
  if (word == POS_INF) {
    return 1;
  }
  return 0;
}

inline bool
float_ibm_single::is_nan() const {
  return (word & ~SGN_MASK) > POS_INF;
}

inline int
float_ibm_single::zero_sign() const {
  if (word == NEG_ZERO) {
    return -1;
  }
  if (word == POS_ZERO) {
    return 1;
  }
  return 0;
}

inline void
float_ibm_single::negate() {
  word ^= SGN_MASK;
}

inline bool
float_ibm_single::sign_bit() const {
  return (word & SGN_MASK) != 0;
}

inline void
float_ibm_single::dec() {
  --word;
}

inline void
float_ibm_single::inc() {
  ++word;
}

inline void
float_ibm_single::set_max(bool negative) {
  word = WRD_MAX;
  if (negative) {
    word |= SGN_MASK;
  }
}

inline void
float_ibm_single::build(bool negative, mpz_t mantissa, int exponent) {
  word = static_cast<uint32_t>(mpz_get_ui(mantissa)
                               & ((1UL << MANTISSA_BITS) - 1));
  if (negative) {
    word |= SGN_MASK;
  }
  const int exponent_repr = exponent + EXPONENT_BIAS;
  PPL_ASSERT(exponent_repr >= 0 && exponent_repr < (1 << EXPONENT_BITS));
  word |= static_cast<uint32_t>(exponent_repr) << MANTISSA_BITS;
}

inline int
float_intel_double_extended::inf_sign() const {
  if (lsp != LSP_INF) {
    return 0;
  }
  const uint32_t a = msp & MSP_NEG_INF;
  if (a == MSP_NEG_INF) {
    return -1;
  }
  if (a == MSP_POS_INF) {
    return 1;
  }
  return 0;
}

inline bool
float_intel_double_extended::is_nan() const {
  return (msp & MSP_POS_INF) == MSP_POS_INF
    && lsp != LSP_INF;
}

inline int
float_intel_double_extended::zero_sign() const {
  if (lsp != LSP_ZERO) {
    return 0;
  }
  const uint32_t a = msp & MSP_NEG_INF;
  if (a == MSP_NEG_ZERO) {
    return -1;
  }
  if (a == MSP_POS_ZERO) {
    return 1;
  }
  return 0;
}

inline void
float_intel_double_extended::negate() {
  msp ^= MSP_SGN_MASK;
}

inline bool
float_intel_double_extended::sign_bit() const {
  return (msp & MSP_SGN_MASK) != 0;
}

inline void
float_intel_double_extended::dec() {
  if ((lsp & LSP_DMAX) == 0) {
    --msp;
    lsp = ((msp & MSP_NEG_INF) == 0) ? LSP_DMAX : LSP_NMAX;
  }
  else {
    --lsp;
  }
}

inline void
float_intel_double_extended::inc() {
  if ((lsp & LSP_DMAX) == LSP_DMAX) {
    ++msp;
    lsp = LSP_DMAX + 1;
  }
  else {
    ++lsp;
  }
}

inline void
float_intel_double_extended::set_max(bool negative) {
  msp = MSP_MAX;
  lsp = LSP_NMAX;
  if (negative) {
    msp |= MSP_SGN_MASK;
  }
}

inline void
float_intel_double_extended::build(bool negative,
                                   mpz_t mantissa, int exponent) {
#if ULONG_MAX == 0xffffffffUL
  mpz_export(&lsp, 0, -1, sizeof(lsp), 0, 0, mantissa);
#else
  lsp = mpz_get_ui(mantissa);
#endif
  msp = (negative ? MSP_SGN_MASK : 0);
  const int exponent_repr = exponent + EXPONENT_BIAS;
  PPL_ASSERT(exponent_repr >= 0 && exponent_repr < (1 << EXPONENT_BITS));
  msp |= static_cast<uint32_t>(exponent_repr);
}

inline int
float_ieee754_quad::inf_sign() const {
  if (lsp != LSP_INF) {
    return 0;
  }
  if (msp == MSP_NEG_INF) {
    return -1;
  }
  if (msp == MSP_POS_INF) {
    return 1;
  }
  return 0;
}

inline bool
float_ieee754_quad::is_nan() const {
  return (msp & ~MSP_SGN_MASK) == MSP_POS_INF
    && lsp != LSP_INF;
}

inline int
float_ieee754_quad::zero_sign() const {
  if (lsp != LSP_ZERO) {
    return 0;
  }
  if (msp == MSP_NEG_ZERO) {
    return -1;
  }
  if (msp == MSP_POS_ZERO) {
    return 1;
  }
  return 0;
}

inline void
float_ieee754_quad::negate() {
  msp ^= MSP_SGN_MASK;
}

inline bool
float_ieee754_quad::sign_bit() const {
  return (msp & MSP_SGN_MASK) != 0;
}

inline void
float_ieee754_quad::dec() {
  if (lsp == 0) {
    --msp;
    lsp = LSP_MAX;
  }
  else {
    --lsp;
  }
}

inline void
float_ieee754_quad::inc() {
  if (lsp == LSP_MAX) {
    ++msp;
    lsp = 0;
  }
  else {
    ++lsp;
  }
}

inline void
float_ieee754_quad::set_max(bool negative) {
  msp = MSP_MAX;
  lsp = LSP_MAX;
  if (negative) {
    msp |= MSP_SGN_MASK;
  }
}

inline void
float_ieee754_quad::build(bool negative, mpz_t mantissa, int exponent) {
  uint64_t parts[2];
  mpz_export(parts, 0, -1, sizeof(parts[0]), 0, 0, mantissa);
  lsp = parts[0];
  msp = parts[1];
  msp &= ((static_cast<uint64_t>(1) << (MANTISSA_BITS - 64)) - 1);
  if (negative) {
    msp |= MSP_SGN_MASK;
  }
  const int exponent_repr = exponent + EXPONENT_BIAS;
  PPL_ASSERT(exponent_repr >= 0 && exponent_repr < (1 << EXPONENT_BITS));
  msp |= static_cast<uint64_t>(exponent_repr) << (MANTISSA_BITS - 64);
}

inline bool
is_less_precise_than(Floating_Point_Format f1, Floating_Point_Format f2) {
  return f1 < f2;
}

inline unsigned int
msb_position(unsigned long long v) {
  return static_cast<unsigned int>(sizeof_to_bits(sizeof(v))) - 1U - clz(v);
}

template <typename FP_Interval_Type>
inline void
affine_form_image(std::map<dimension_type,
                           Linear_Form<FP_Interval_Type> >& lf_store,
                  const Variable var,
                  const Linear_Form<FP_Interval_Type>& lf) {
  // Assign the new linear form for var.
  lf_store[var.id()] = lf;
  // Now invalidate all linear forms in which var occurs.
  discard_occurrences(lf_store, var);
}

#if PPL_SUPPORTED_FLOAT
inline
Float<float>::Float() {
}

inline
Float<float>::Float(float v) {
  u.number = v;
}

inline float
Float<float>::value() {
  return u.number;
}
#endif

#if PPL_SUPPORTED_DOUBLE
inline
Float<double>::Float() {
}

inline
Float<double>::Float(double v) {
  u.number = v;
}

inline double
Float<double>::value() {
  return u.number;
}
#endif

#if PPL_SUPPORTED_LONG_DOUBLE
inline
Float<long double>::Float() {
}

inline
Float<long double>::Float(long double v) {
  u.number = v;
}

inline long double
Float<long double>::value() {
  return u.number;
}
#endif

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Float_templates.hh line 1. */
/* IEC 559 floating point format related functions:
   non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/Float_templates.hh line 30. */
#include <cmath>

namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type>
const FP_Interval_Type& compute_absolute_error(
                        const Floating_Point_Format analyzed_format) {
  typedef typename FP_Interval_Type::boundary_type analyzer_format;

  // FIXME: check if initializing caches with EMPTY is better.
  static const FP_Interval_Type ZERO_INTERVAL = FP_Interval_Type(0);
  // Cached results for each different analyzed format.
  static FP_Interval_Type ieee754_half_result = ZERO_INTERVAL;
  static FP_Interval_Type ieee754_single_result = ZERO_INTERVAL;
  static FP_Interval_Type ieee754_double_result = ZERO_INTERVAL;
  static FP_Interval_Type ibm_single_result = ZERO_INTERVAL;
  static FP_Interval_Type ieee754_quad_result = ZERO_INTERVAL;
  static FP_Interval_Type intel_double_extended_result = ZERO_INTERVAL;

  FP_Interval_Type* to_compute = NULL;
  // Get the necessary information on the analyzed's format.
  unsigned int f_base;
  int f_exponent_bias;
  unsigned int f_mantissa_bits;
  switch (analyzed_format) {
    case IEEE754_HALF:
      if (ieee754_half_result != ZERO_INTERVAL) {
        return ieee754_half_result;
      }
      to_compute = &ieee754_half_result;
      f_base = float_ieee754_half::BASE;
      f_exponent_bias = float_ieee754_half::EXPONENT_BIAS;
      f_mantissa_bits = float_ieee754_half::MANTISSA_BITS;
      break;
    case IEEE754_SINGLE:
      if (ieee754_single_result != ZERO_INTERVAL) {
        return ieee754_single_result;
      }

      to_compute = &ieee754_single_result;
      f_base = float_ieee754_single::BASE;
      f_exponent_bias = float_ieee754_single::EXPONENT_BIAS;
      f_mantissa_bits = float_ieee754_single::MANTISSA_BITS;
      break;
    case IEEE754_DOUBLE:
      if (ieee754_double_result != ZERO_INTERVAL) {
        return ieee754_double_result;
      }

      to_compute = &ieee754_double_result;
      f_base = float_ieee754_double::BASE;
      f_exponent_bias = float_ieee754_double::EXPONENT_BIAS;
      f_mantissa_bits = float_ieee754_double::MANTISSA_BITS;
      break;
    case IBM_SINGLE:
      if (ibm_single_result != ZERO_INTERVAL) {
        return ibm_single_result;
      }

      to_compute = &ibm_single_result;
      f_base = float_ibm_single::BASE;
      f_exponent_bias = float_ibm_single::EXPONENT_BIAS;
      f_mantissa_bits = float_ibm_single::MANTISSA_BITS;
      break;
    case IEEE754_QUAD:
      if (ieee754_quad_result != ZERO_INTERVAL) {
        return ieee754_quad_result;
      }

      to_compute = &ieee754_quad_result;
      f_base = float_ieee754_quad::BASE;
      f_exponent_bias = float_ieee754_quad::EXPONENT_BIAS;
      f_mantissa_bits = float_ieee754_quad::MANTISSA_BITS;
      break;
    case INTEL_DOUBLE_EXTENDED:
      if (intel_double_extended_result != ZERO_INTERVAL) {
        return intel_double_extended_result;
      }

      to_compute = &intel_double_extended_result;
      f_base = float_intel_double_extended::BASE;
      f_exponent_bias = float_intel_double_extended::EXPONENT_BIAS;
      f_mantissa_bits = float_intel_double_extended::MANTISSA_BITS;
      break;
    default:
      PPL_UNREACHABLE;
      break;
  }

  PPL_ASSERT(to_compute != NULL);

  // We assume that f_base is a power of 2.
  analyzer_format omega;
  int power = static_cast<int>(msb_position(f_base))
    * ((1 - f_exponent_bias) - static_cast<int>(f_mantissa_bits));
  omega = std::max(static_cast<analyzer_format>(ldexp(1.0, power)),
                   std::numeric_limits<analyzer_format>::denorm_min());

  to_compute->build(i_constraint(GREATER_OR_EQUAL, -omega),
                    i_constraint(LESS_OR_EQUAL, omega));
  return *to_compute;
}

template <typename FP_Interval_Type>
void
discard_occurrences(std::map<dimension_type,
                             Linear_Form<FP_Interval_Type> >& lf_store,
                    Variable var) {
  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;
  typedef typename std::map<dimension_type, FP_Linear_Form>::iterator Iter;
  for (Iter i = lf_store.begin(); i != lf_store.end(); ) {
    if ((i->second).coefficient(var) != 0) {
      i = lf_store.erase(i);
    }
    else {
      ++i;
    }
  }
}

/* FIXME: improve efficiency by adding the list of potentially conflicting
   variables as an argument. */
template <typename FP_Interval_Type>
void upper_bound_assign(std::map<dimension_type,
                                 Linear_Form<FP_Interval_Type> >& ls1,
                        const std::map<dimension_type,
                                       Linear_Form<FP_Interval_Type> >& ls2) {
  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;
  typedef typename std::map<dimension_type, FP_Linear_Form>::iterator Iter;
  typedef typename std::map<dimension_type,
                            FP_Linear_Form>::const_iterator Const_Iter;

  Const_Iter i2_end = ls2.end();
  for (Iter i1 = ls1.begin(), i1_end = ls1.end(); i1 != i1_end; ) {
    Const_Iter i2 = ls2.find(i1->first);
    if ((i2 == i2_end) || (i1->second != i2->second)) {
      i1 = ls1.erase(i1);
    }
    else {
      ++i1;
    }
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Float_defs.hh line 521. */

/* Automatically generated from PPL source file ../src/checked_defs.hh line 32. */
#include <cassert>
#include <iostream>
#include <gmpxx.h>

namespace Parma_Polyhedra_Library {

namespace Checked {


// It is a pity that function partial specialization is not permitted
// by C++.  To (partly) overcome this limitation, we use class
// encapsulated functions and partial specialization of containing
// classes.

#define PPL_FUNCTION_CLASS(name) name ## _function_struct

#define PPL_DECLARE_FUN1_0_0(name, ret_type, qual, type)                \
  template <typename Policy, typename type>                             \
  struct PPL_FUNCTION_CLASS(name);                                      \
  template <typename Policy, typename type>                             \
  inline ret_type PPL_U(name)(PPL_U(qual) PPL_U(type)& arg) {           \
    return PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)>::function(arg); \
  }

#define PPL_DECLARE_FUN1_0_1(name, ret_type, qual, type, after1)        \
  template <typename Policy, typename type>                             \
  struct PPL_FUNCTION_CLASS(name);                                      \
  template <typename Policy, typename type>                             \
  inline ret_type PPL_U(name)(PPL_U(qual) PPL_U(type)& arg, PPL_U(after1) a1) { \
    return                                                              \
      PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)>::function(arg, a1); \
  }

#define PPL_DECLARE_FUN1_0_2(name, ret_type, qual, type, after1, after2) \
  template <typename Policy, typename type>                             \
  struct PPL_FUNCTION_CLASS(name);                                      \
  template <typename Policy, typename type>                             \
  inline ret_type PPL_U(name)(PPL_U(qual) PPL_U(type)& arg, PPL_U(after1) a1, \
                       PPL_U(after2) a2) {                              \
    return                                                              \
      PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)>::function(arg,      \
                                                              a1, a2);  \
  }

#define PPL_DECLARE_FUN1_0_3(name, ret_type, qual, type,                \
                             after1, after2, after3)                    \
  template <typename Policy, typename type>                             \
  struct PPL_FUNCTION_CLASS(name);                                      \
  template <typename Policy, typename type>                             \
  inline ret_type PPL_U(name)(PPL_U(qual) PPL_U(type)& arg,             \
                       PPL_U(after1) a1, PPL_U(after2) a2,              \
                       PPL_U(after3) a3) {                              \
    return                                                              \
      PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)>::function(arg,      \
                                                              a1, a2,   \
                                                              a3);      \
  }

#define PPL_DECLARE_FUN1_1_1(name, ret_type, before1, qual, type, after1) \
  template <typename Policy, typename type>                             \
  struct PPL_FUNCTION_CLASS(name);                                      \
  template <typename Policy, typename type>                             \
  inline ret_type PPL_U(name)(PPL_U(before1) b1, PPL_U(qual) PPL_U(type)& arg, \
                       PPL_U(after1) a1) {                              \
    return                                                              \
      PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)>::function(b1, arg,  \
                                                              a1);      \
  }

#define PPL_DECLARE_FUN1_1_2(name, ret_type, before1, qual, type,       \
                             after1, after2)                            \
  template <typename Policy, typename type>                             \
  struct PPL_FUNCTION_CLASS(name);                                      \
  template <typename Policy, typename type>                             \
  inline ret_type PPL_U(name)(PPL_U(before1) b1, PPL_U(qual) PPL_U(type)& arg, \
                       PPL_U(after1) a1, PPL_U(after2) a2) {            \
    return                                                              \
      PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)>::function(b1, arg,  \
                                                              a1, a2);  \
  }

#define PPL_DECLARE_FUN1_2_2(name, ret_type, before1, before2, qual, type, \
                             after1, after2)                            \
  template <typename Policy, typename type>                             \
  struct PPL_FUNCTION_CLASS(name);                                      \
  template <typename Policy, typename type>                             \
  inline ret_type PPL_U(name)(PPL_U(before1) b1, PPL_U(before2) b2,     \
                       PPL_U(qual) PPL_U(type)& arg,                    \
                       PPL_U(after1) a1, PPL_U(after2) a2) {            \
    return                                                              \
      PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)>::function(b1, b2,   \
                                                              arg,      \
                                                              a1, a2);  \
  }

#define PPL_DECLARE_FUN2_0_0(name, ret_type, qual1, type1, qual2, type2) \
  template <typename Policy1, typename Policy2,                         \
            typename type1, typename type2>                             \
  struct PPL_FUNCTION_CLASS(name);                                      \
  template <typename Policy1, typename Policy2,                         \
            typename type1, typename type2>                             \
  inline ret_type PPL_U(name)(PPL_U(qual1) PPL_U(type1)& arg1,          \
                       PPL_U(qual2) PPL_U(type2)& arg2) {               \
    return PPL_FUNCTION_CLASS(name)<Policy1, Policy2,                   \
      type1, PPL_U(type2)>::function(arg1, arg2);                       \
  }

#define PPL_DECLARE_FUN2_0_1(name, ret_type, qual1, type1,      \
                             qual2, type2, after1)              \
  template <typename Policy1, typename Policy2,                 \
            typename type1, typename type2>                     \
  struct PPL_FUNCTION_CLASS(name);                              \
  template <typename Policy1, typename Policy2,                 \
            typename type1, typename type2>                     \
  inline ret_type PPL_U(name)(PPL_U(qual1) PPL_U(type1)& arg1,  \
                       PPL_U(qual2) PPL_U(type2)& arg2,         \
                       PPL_U(after1) a1) {                      \
    return PPL_FUNCTION_CLASS(name)<Policy1, Policy2,           \
      type1, PPL_U(type2)>::function(arg1, arg2, a1);           \
  }

#define PPL_DECLARE_FUN2_0_2(name, ret_type, qual1, type1, qual2, type2, \
                             after1, after2)                            \
  template <typename Policy1, typename Policy2,                         \
            typename type1, typename type2>                             \
  struct PPL_FUNCTION_CLASS(name);                                      \
  template <typename Policy1, typename Policy2,                         \
            typename type1, typename type2>                             \
  inline ret_type PPL_U(name)(PPL_U(qual1) PPL_U(type1)& arg1,          \
                       PPL_U(qual2) PPL_U(type2)& arg2,                 \
                       PPL_U(after1) a1, PPL_U(after2) a2) {            \
    return PPL_FUNCTION_CLASS(name)<Policy1, Policy2,                   \
      type1, PPL_U(type2)>::function(arg1, arg2, a1, a2);               \
  }

#define PPL_DECLARE_FUN3_0_1(name, ret_type, qual1, type1,              \
                             qual2, type2, qual3, type3, after1)        \
  template <typename Policy1, typename Policy2, typename Policy3,       \
            typename type1, typename type2, typename type3>             \
  struct PPL_FUNCTION_CLASS(name);                                      \
  template <typename Policy1, typename Policy2, typename Policy3,       \
            typename type1, typename type2, typename type3>             \
  inline ret_type PPL_U(name)(PPL_U(qual1) PPL_U(type1)& arg1,          \
                       PPL_U(qual2) PPL_U(type2)& arg2,                 \
                       PPL_U(qual3) PPL_U(type3)& arg3,                 \
                       PPL_U(after1) a1) {                              \
    return PPL_FUNCTION_CLASS(name)<Policy1, Policy2, Policy3,          \
      type1, type2, PPL_U(type3)>                                       \
      ::function(arg1, arg2, arg3, a1);                                 \
  }

#define PPL_DECLARE_FUN5_0_1(name, ret_type,                            \
                             qual1, type1, qual2, type2, qual3, type3,  \
                             qual4, type4, qual5, type5,                \
                             after1)                                    \
  template <typename Policy1, typename Policy2, typename Policy3,       \
            typename Policy4,typename Policy5,                          \
            typename type1, typename type2, typename type3,             \
            typename type4, typename type5>                             \
  struct PPL_FUNCTION_CLASS(name);                                      \
  template <typename Policy1, typename Policy2, typename Policy3,       \
            typename Policy4,typename Policy5,                          \
            typename type1, typename type2, typename type3,             \
            typename type4, typename type5>                             \
  inline ret_type PPL_U(name)(PPL_U(qual1) PPL_U(type1)& arg1, PPL_U(qual2) \
                       PPL_U(type2)& arg2,                              \
                       PPL_U(qual3) PPL_U(type3)& arg3, PPL_U(qual4)    \
                       PPL_U(type4)& arg4,                              \
                       PPL_U(qual5) PPL_U(type5)& arg5,                 \
                       PPL_U(after1) a1) {                              \
    return PPL_FUNCTION_CLASS(name)<Policy1, Policy2, Policy3,          \
      Policy4, Policy5,                                                 \
      type1, type2,                                                     \
      type3, type4,                                                     \
      PPL_U(type5)>                                                     \
      ::function(arg1, arg2, arg3, arg4, arg5, a1);                     \
  }

#define PPL_SPECIALIZE_FUN1_0_0(name, func, ret_type, qual, type)       \
  template <typename Policy>                                            \
  struct PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)> {                \
    static inline ret_type function(PPL_U(qual) PPL_U(type)& arg) {     \
      return PPL_U(func)<Policy>(arg);                                  \
    }                                                                   \
  };

#define PPL_SPECIALIZE_FUN1_0_1(name, func, ret_type, qual, type, after1) \
  template <typename Policy>                                            \
  struct PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)> {                \
    static inline ret_type function(PPL_U(qual) PPL_U(type)& arg,       \
                                    PPL_U(after1) a1) {                 \
      return PPL_U(func)<Policy>(arg, a1);                              \
    }                                                                   \
  };

#define PPL_SPECIALIZE_FUN1_0_2(name, func, ret_type, qual, type,       \
                                after1, after2)                         \
  template <typename Policy>                                            \
  struct PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)> {                \
    static inline ret_type function(PPL_U(qual) PPL_U(type)& arg,       \
                                    PPL_U(after1) a1, PPL_U(after2) a2) \
    {                                                                   \
      return PPL_U(func)<Policy>(arg, a1, a2);                          \
    }                                                                   \
  };

#define PPL_SPECIALIZE_FUN1_0_3(name, func, ret_type, qual, type,       \
                                after1, after2, after3)                 \
  template <typename Policy>                                            \
  struct PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)> {                \
    static inline ret_type function(PPL_U(qual) PPL_U(type)& arg,       \
                                    PPL_U(after1) a1, PPL_U(after2) a2, \
                                    PPL_U(after3) a3) {                 \
      return PPL_U(func)<Policy>(arg, a1, a2, a3);                      \
    }                                                                   \
  };

#define PPL_SPECIALIZE_FUN1_1_1(name, func, ret_type, before1,          \
                                qual, type, after1)                     \
  template <typename Policy>                                            \
  struct PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)> {                \
    static inline ret_type function(PPL_U(before1) b1, PPL_U(qual)      \
                                    PPL_U(type)& arg,                   \
                                    PPL_U(after1) a1) {                 \
      return PPL_U(func)<Policy>(b1, arg, a1);                          \
    }                                                                   \
  };

#define PPL_SPECIALIZE_FUN1_1_2(name, func, ret_type, before1,          \
                                qual, type, after1, after2)             \
  template <typename Policy>                                            \
  struct PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)> {                \
    static inline ret_type function(PPL_U(before1) b1, PPL_U(qual)      \
                                    PPL_U(type)& arg,                   \
                                    PPL_U(after1) a1, PPL_U(after2) a2) \
    {                                                                   \
      return PPL_U(func)<Policy>(b1, arg, a1, a2);                      \
    }                                                                   \
  };

#define PPL_SPECIALIZE_FUN1_2_2(name, func, ret_type, before1, before2, \
                                qual, type, after1, after2)             \
  template <typename Policy>                                            \
  struct PPL_FUNCTION_CLASS(name)<Policy, PPL_U(type)> {                \
    static inline ret_type function(PPL_U(before1) b1, PPL_U(before2) b2, \
                                    PPL_U(qual) PPL_U(type)& arg,       \
                                    PPL_U(after1) a1, PPL_U(after2) a2) \
    {                                                                   \
      return PPL_U(func)<Policy>(b1, b2, arg, a1, a2);                  \
    }                                                                   \
  };

#define PPL_SPECIALIZE_FUN2_0_0(name, func, ret_type, qual1, type1,     \
                                qual2, type2)                           \
  template <typename Policy1, typename Policy2>                         \
  struct PPL_FUNCTION_CLASS(name)<Policy1, Policy2, type1,              \
                                  PPL_U(type2)> {                       \
    static inline ret_type function(PPL_U(qual1) PPL_U(type1)& arg1,    \
                                    PPL_U(qual2) PPL_U(type2) &arg2) {  \
      return PPL_U(func)<Policy1, Policy2>(arg1, arg2);                 \
    }                                                                   \
  };

#define PPL_SPECIALIZE_FUN2_0_1(name, func, ret_type, qual1, type1,     \
                                qual2, type2, after1)                   \
  template <typename Policy1, typename Policy2>                         \
  struct PPL_FUNCTION_CLASS(name)<Policy1, Policy2, type1,              \
                                  PPL_U(type2)> {                       \
    static inline ret_type function(PPL_U(qual1) PPL_U(type1)& arg1,    \
                                    PPL_U(qual2) PPL_U(type2) &arg2,    \
                                    PPL_U(after1) a1) {                 \
      return PPL_U(func)<Policy1, Policy2>(arg1, arg2, a1);             \
    }                                                                   \
  };

#define PPL_SPECIALIZE_FUN2_0_2(name, func, ret_type, qual1, type1,     \
                                qual2, type2, after1, after2)           \
  template <typename Policy1, typename Policy2>                         \
  struct PPL_FUNCTION_CLASS(name)<Policy1, Policy2, type1,              \
                                  PPL_U(type2)> {                       \
    static inline ret_type function(PPL_U(qual1) PPL_U(type1)& arg1,    \
                                    PPL_U(qual2) PPL_U(type2) &arg2,    \
                                    PPL_U(after1) a1, PPL_U(after2) a2) \
    {                                                                   \
      return PPL_U(func)<Policy1, Policy2>(arg1, arg2, a1, a2);         \
    }                                                                   \
  };

#define PPL_SPECIALIZE_FUN3_0_1(name, func, ret_type, qual1, type1,     \
                                qual2, type2, qual3, type3, after1)     \
  template <typename Policy1, typename Policy2, typename Policy3>       \
  struct PPL_FUNCTION_CLASS(name) <Policy1, Policy2, Policy3,           \
                                   type1, type2,                        \
                                   PPL_U(type3)> {                      \
    static inline Result function(PPL_U(qual1) PPL_U(type1)& arg1,      \
                                  PPL_U(qual2) PPL_U(type2) &arg2,      \
                                  PPL_U(qual3) PPL_U(type3) &arg3,      \
                                  PPL_U(after1) a1) {                   \
      return PPL_U(func)<Policy1, Policy2, Policy3>(arg1, arg2, arg3,   \
                                                    a1);                \
    }                                                                   \
  };

#define PPL_SPECIALIZE_FUN5_0_1(name, func, ret_type,                   \
                                qual1, type1, qual2, type2,             \
                                qual3, type3,                           \
                                qual4, type4, qual5, type5, after1)     \
  template <typename Policy1, typename Policy2, typename Policy3,       \
            typename Policy4, typename Policy5>                         \
  struct PPL_FUNCTION_CLASS(name) <Policy1, Policy2, Policy3, Policy4,  \
                                   Policy5,                             \
                                   type1, type2,                        \
                                   type3, type4,                        \
                                   PPL_U(type5)> {                      \
    static inline Result                                                \
      function(PPL_U(qual1) PPL_U(type1)& arg1, PPL_U(qual2)            \
               PPL_U(type2) &arg2,                                      \
               PPL_U(qual3) PPL_U(type3) &arg3, PPL_U(qual4)            \
               PPL_U(type4) &arg4,                                      \
               PPL_U(qual5) PPL_U(type5) &arg5, PPL_U(after1) a1) {     \
      return PPL_U(func)<Policy1, Policy2, Policy3, Policy4,            \
        Policy5>(arg1, arg2, arg3, arg4, arg5, a1);                     \
    }                                                                   \
  };

// The `nonconst' macro helps readability of the sequel.
#ifdef nonconst
#define PPL_SAVED_nonconst nonconst
#undef nonconst
#endif
#define nonconst

#define PPL_SPECIALIZE_COPY(func, Type)                                 \
  PPL_SPECIALIZE_FUN2_0_0(copy, func, void, nonconst, Type, const, Type)
#define PPL_SPECIALIZE_SGN(func, From)                                  \
  PPL_SPECIALIZE_FUN1_0_0(sgn, func, Result_Relation, const, From)
#define PPL_SPECIALIZE_CMP(func, Type1, Type2)                          \
  PPL_SPECIALIZE_FUN2_0_0(cmp, func, Result_Relation, const, Type1, const, Type2)
#define PPL_SPECIALIZE_CLASSIFY(func, Type)                             \
  PPL_SPECIALIZE_FUN1_0_3(classify, func, Result, const, Type, bool, bool, bool)
#define PPL_SPECIALIZE_IS_NAN(func, Type)                       \
  PPL_SPECIALIZE_FUN1_0_0(is_nan, func, bool, const, Type)
#define PPL_SPECIALIZE_IS_MINF(func, Type)                      \
  PPL_SPECIALIZE_FUN1_0_0(is_minf, func, bool, const, Type)
#define PPL_SPECIALIZE_IS_PINF(func, Type)                      \
  PPL_SPECIALIZE_FUN1_0_0(is_pinf, func, bool, const, Type)
#define PPL_SPECIALIZE_IS_INT(func, Type)                       \
  PPL_SPECIALIZE_FUN1_0_0(is_int, func, bool, const, Type)
#define PPL_SPECIALIZE_ASSIGN_SPECIAL(func, Type)                       \
  PPL_SPECIALIZE_FUN1_0_2(assign_special, func, Result,                 \
                          nonconst, Type, Result_Class, Rounding_Dir)
#define PPL_SPECIALIZE_CONSTRUCT_SPECIAL(func, Type)                    \
  PPL_SPECIALIZE_FUN1_0_2(construct_special, func, Result, nonconst,    \
                          Type, Result_Class, Rounding_Dir)
#define PPL_SPECIALIZE_CONSTRUCT(func, To, From)                        \
  PPL_SPECIALIZE_FUN2_0_1(construct, func, Result, nonconst, To,        \
                          const, From, Rounding_Dir)
#define PPL_SPECIALIZE_ASSIGN(func, To, From)                   \
  PPL_SPECIALIZE_FUN2_0_1(assign, func, Result, nonconst, To,   \
                          const, From, Rounding_Dir)
#define PPL_SPECIALIZE_FLOOR(func, To, From)                    \
  PPL_SPECIALIZE_FUN2_0_1(floor, func, Result, nonconst, To,    \
                          const, From, Rounding_Dir)
#define PPL_SPECIALIZE_CEIL(func, To, From)                     \
  PPL_SPECIALIZE_FUN2_0_1(ceil, func, Result, nonconst, To,     \
                          const, From, Rounding_Dir)
#define PPL_SPECIALIZE_TRUNC(func, To, From)                    \
  PPL_SPECIALIZE_FUN2_0_1(trunc, func, Result, nonconst, To,    \
                          const, From, Rounding_Dir)
#define PPL_SPECIALIZE_NEG(func, To, From)                      \
  PPL_SPECIALIZE_FUN2_0_1(neg, func, Result, nonconst, To,      \
                          const, From, Rounding_Dir)
#define PPL_SPECIALIZE_ABS(func, To, From)                      \
  PPL_SPECIALIZE_FUN2_0_1(abs, func, Result, nonconst, To,      \
                          const, From, Rounding_Dir)
#define PPL_SPECIALIZE_SQRT(func, To, From)                     \
  PPL_SPECIALIZE_FUN2_0_1(sqrt, func, Result, nonconst, To,     \
                          const, From, Rounding_Dir)
#define PPL_SPECIALIZE_ADD(func, To, From1, From2)                      \
  PPL_SPECIALIZE_FUN3_0_1(add, func, Result, nonconst, To,              \
                          const, From1, const, From2, Rounding_Dir)
#define PPL_SPECIALIZE_SUB(func, To, From1, From2)                      \
  PPL_SPECIALIZE_FUN3_0_1(sub, func, Result, nonconst, To,              \
                          const, From1, const, From2, Rounding_Dir)
#define PPL_SPECIALIZE_MUL(func, To, From1, From2)                      \
  PPL_SPECIALIZE_FUN3_0_1(mul, func, Result, nonconst, To,              \
                          const, From1, const, From2, Rounding_Dir)
#define PPL_SPECIALIZE_DIV(func, To, From1, From2)                      \
  PPL_SPECIALIZE_FUN3_0_1(div, func, Result, nonconst, To,              \
                          const, From1, const, From2, Rounding_Dir)
#define PPL_SPECIALIZE_REM(func, To, From1, From2)                      \
  PPL_SPECIALIZE_FUN3_0_1(rem, func, Result, nonconst, To,              \
                          const, From1, const, From2, Rounding_Dir)
#define PPL_SPECIALIZE_IDIV(func, To, From1, From2)                     \
  PPL_SPECIALIZE_FUN3_0_1(idiv, func, Result, nonconst, To,             \
                          const, From1, const, From2, Rounding_Dir)
#define PPL_SPECIALIZE_ADD_2EXP(func, To, From)                         \
  PPL_SPECIALIZE_FUN2_0_2(add_2exp, func, Result, nonconst, To,         \
                          const, From, unsigned int, Rounding_Dir)
#define PPL_SPECIALIZE_SUB_2EXP(func, To, From)                         \
  PPL_SPECIALIZE_FUN2_0_2(sub_2exp, func, Result, nonconst, To,         \
                          const, From, unsigned int, Rounding_Dir)
#define PPL_SPECIALIZE_MUL_2EXP(func, To, From)                         \
  PPL_SPECIALIZE_FUN2_0_2(mul_2exp, func, Result, nonconst, To,         \
                          const, From, unsigned int, Rounding_Dir)
#define PPL_SPECIALIZE_DIV_2EXP(func, To, From)                         \
  PPL_SPECIALIZE_FUN2_0_2(div_2exp, func, Result, nonconst, To,         \
                          const, From, unsigned int, Rounding_Dir)
#define PPL_SPECIALIZE_SMOD_2EXP(func, To, From)                        \
  PPL_SPECIALIZE_FUN2_0_2(smod_2exp, func, Result, nonconst, To,        \
                          const, From, unsigned int, Rounding_Dir)
#define PPL_SPECIALIZE_UMOD_2EXP(func, To, From)                        \
  PPL_SPECIALIZE_FUN2_0_2(umod_2exp, func, Result, nonconst, To,        \
                          const, From, unsigned int, Rounding_Dir)
#define PPL_SPECIALIZE_ADD_MUL(func, To, From1, From2)                  \
  PPL_SPECIALIZE_FUN3_0_1(add_mul, func, Result, nonconst, To,          \
                          const, From1, const, From2, Rounding_Dir)
#define PPL_SPECIALIZE_SUB_MUL(func, To, From1, From2)                  \
  PPL_SPECIALIZE_FUN3_0_1(sub_mul, func, Result, nonconst, To,          \
                          const, From1, const, From2, Rounding_Dir)
#define PPL_SPECIALIZE_GCD(func, To, From1, From2)                      \
  PPL_SPECIALIZE_FUN3_0_1(gcd, func, Result, nonconst, To,              \
                          const, From1, const, From2, Rounding_Dir)
#define PPL_SPECIALIZE_GCDEXT(func, To1, From1, From2, To2, To3)        \
  PPL_SPECIALIZE_FUN5_0_1(gcdext, func, Result, nonconst, To1,          \
                          nonconst, To2, nonconst, To3,                 \
                          const, From1, const, From2, Rounding_Dir)
#define PPL_SPECIALIZE_LCM(func, To, From1, From2)                      \
  PPL_SPECIALIZE_FUN3_0_1(lcm, func, Result, nonconst, To,              \
                          const, From1, const, From2, Rounding_Dir)
#define PPL_SPECIALIZE_INPUT(func, Type)                        \
  PPL_SPECIALIZE_FUN1_0_2(input, func, Result, nonconst, Type,  \
                          std::istream&, Rounding_Dir)
#define PPL_SPECIALIZE_OUTPUT(func, Type)                       \
  PPL_SPECIALIZE_FUN1_1_2(output, func, Result, std::ostream&,  \
                          const, Type,                          \
                          const Numeric_Format&, Rounding_Dir)


PPL_DECLARE_FUN2_0_0(copy,
                     void, nonconst, Type1, const, Type2)
PPL_DECLARE_FUN1_0_0(sgn,
                     Result_Relation, const, From)
PPL_DECLARE_FUN2_0_0(cmp,
                     Result_Relation, const, Type1, const, Type2)
PPL_DECLARE_FUN1_0_3(classify,
                     Result, const, Type, bool, bool, bool)
PPL_DECLARE_FUN1_0_0(is_nan,
                     bool, const, Type)
PPL_DECLARE_FUN1_0_0(is_minf,
                     bool, const, Type)
PPL_DECLARE_FUN1_0_0(is_pinf,
                     bool, const, Type)
PPL_DECLARE_FUN1_0_0(is_int,
                     bool, const, Type)
PPL_DECLARE_FUN1_0_2(assign_special,
                     Result, nonconst, Type, Result_Class, Rounding_Dir)
PPL_DECLARE_FUN1_0_2(construct_special,
                     Result, nonconst, Type, Result_Class, Rounding_Dir)
PPL_DECLARE_FUN2_0_1(construct,
                     Result, nonconst, To, const, From, Rounding_Dir)
PPL_DECLARE_FUN2_0_1(assign,
                     Result, nonconst, To, const, From, Rounding_Dir)
PPL_DECLARE_FUN2_0_1(floor,
                     Result, nonconst, To, const, From, Rounding_Dir)
PPL_DECLARE_FUN2_0_1(ceil,
                     Result, nonconst, To, const, From, Rounding_Dir)
PPL_DECLARE_FUN2_0_1(trunc,
                     Result, nonconst, To, const, From, Rounding_Dir)
PPL_DECLARE_FUN2_0_1(neg,
                     Result, nonconst, To, const, From, Rounding_Dir)
PPL_DECLARE_FUN2_0_1(abs,
                     Result, nonconst, To, const, From, Rounding_Dir)
PPL_DECLARE_FUN2_0_1(sqrt,
                     Result, nonconst, To, const, From, Rounding_Dir)
PPL_DECLARE_FUN3_0_1(add,
                     Result, nonconst, To,
                     const, From1, const, From2, Rounding_Dir)
PPL_DECLARE_FUN3_0_1(sub,
                     Result, nonconst, To,
                     const, From1, const, From2, Rounding_Dir)
PPL_DECLARE_FUN3_0_1(mul,
                     Result, nonconst, To,
                     const, From1, const, From2, Rounding_Dir)
PPL_DECLARE_FUN3_0_1(div,
                     Result, nonconst, To,
                     const, From1, const, From2, Rounding_Dir)
PPL_DECLARE_FUN3_0_1(rem,
                     Result, nonconst, To,
                     const, From1, const, From2, Rounding_Dir)
PPL_DECLARE_FUN3_0_1(idiv,
                     Result, nonconst, To,
                     const, From1, const, From2, Rounding_Dir)
PPL_DECLARE_FUN2_0_2(add_2exp,
                     Result, nonconst, To,
                     const, From, unsigned int, Rounding_Dir)
PPL_DECLARE_FUN2_0_2(sub_2exp,
                     Result, nonconst, To,
                     const, From, unsigned int, Rounding_Dir)
PPL_DECLARE_FUN2_0_2(mul_2exp,
                     Result, nonconst, To,
                     const, From, unsigned int, Rounding_Dir)
PPL_DECLARE_FUN2_0_2(div_2exp,
                     Result, nonconst, To,
                     const, From, unsigned int, Rounding_Dir)
PPL_DECLARE_FUN2_0_2(smod_2exp,
                     Result, nonconst, To,
                     const, From, unsigned int, Rounding_Dir)
PPL_DECLARE_FUN2_0_2(umod_2exp,
                     Result, nonconst, To,
                     const, From, unsigned int, Rounding_Dir)
PPL_DECLARE_FUN3_0_1(add_mul,
                     Result, nonconst, To,
                     const, From1, const, From2, Rounding_Dir)
PPL_DECLARE_FUN3_0_1(sub_mul,
                     Result, nonconst, To,
                     const, From1, const, From2, Rounding_Dir)
PPL_DECLARE_FUN3_0_1(gcd,
                     Result, nonconst, To,
                     const, From1, const, From2, Rounding_Dir)
PPL_DECLARE_FUN5_0_1(gcdext,
                     Result, nonconst, To1, nonconst, To2, nonconst, To3,
                     const, From1, const, From2, Rounding_Dir)
PPL_DECLARE_FUN3_0_1(lcm,
                     Result, nonconst, To,
                     const, From1, const, From2, Rounding_Dir)
PPL_DECLARE_FUN1_0_2(input,
                     Result, nonconst, Type, std::istream&, Rounding_Dir)
PPL_DECLARE_FUN1_1_2(output,
                     Result, std::ostream&, const, Type,
                     const Numeric_Format&, Rounding_Dir)

#undef PPL_DECLARE_FUN1_0_0
#undef PPL_DECLARE_FUN1_0_1
#undef PPL_DECLARE_FUN1_0_2
#undef PPL_DECLARE_FUN1_0_3
#undef PPL_DECLARE_FUN1_1_1
#undef PPL_DECLARE_FUN1_1_2
#undef PPL_DECLARE_FUN1_2_2
#undef PPL_DECLARE_FUN2_0_0
#undef PPL_DECLARE_FUN2_0_1
#undef PPL_DECLARE_FUN2_0_2
#undef PPL_DECLARE_FUN3_0_1
#undef PPL_DECLARE_FUN5_0_1

template <typename Policy, typename To>
Result round(To& to, Result r, Rounding_Dir dir);

Result input_mpq(mpq_class& to, std::istream& is);

std::string float_mpq_to_string(mpq_class& q);

} // namespace Checked

struct Minus_Infinity {
  static const Result_Class vclass = VC_MINUS_INFINITY;
};
struct Plus_Infinity {
  static const Result_Class vclass = VC_PLUS_INFINITY;
};
struct Not_A_Number {
  static const Result_Class vclass = VC_NAN;
};

template <typename T>
struct Is_Special : public False { };

template <>
struct Is_Special<Minus_Infinity> : public True {};

template <>
struct Is_Special<Plus_Infinity> : public True {};

template <>
struct Is_Special<Not_A_Number> : public True {};

extern Minus_Infinity MINUS_INFINITY;
extern Plus_Infinity PLUS_INFINITY;
extern Not_A_Number NOT_A_NUMBER;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
struct Checked_Number_Transparent_Policy {
  //! Do not check for overflowed result.
  const_bool_nodef(check_overflow, false);

  //! Do not check for attempts to add infinities with different sign.
  const_bool_nodef(check_inf_add_inf, false);

  //! Do not check for attempts to subtract infinities with same sign.
  const_bool_nodef(check_inf_sub_inf, false);

  //! Do not check for attempts to multiply infinities by zero.
  const_bool_nodef(check_inf_mul_zero, false);

  //! Do not check for attempts to divide by zero.
  const_bool_nodef(check_div_zero, false);

  //! Do not check for attempts to divide infinities.
  const_bool_nodef(check_inf_div_inf, false);

  //! Do not check for attempts to compute remainder of infinities.
  const_bool_nodef(check_inf_mod, false);

  //! Do not check for attempts to take the square root of a negative number.
  const_bool_nodef(check_sqrt_neg, false);

  //! Handle not-a-number special value if \p T has it.
  const_bool_nodef(has_nan, std::numeric_limits<T>::has_quiet_NaN);

  //! Handle infinity special values if \p T have them.
  const_bool_nodef(has_infinity, std::numeric_limits<T>::has_infinity);

  /*! \brief
    The checked number can always be safely converted to the
    underlying type \p T and vice-versa.
  */
  const_bool_nodef(convertible, true);

  //! Do not honor requests to check for FPU inexact results.
  const_bool_nodef(fpu_check_inexact, false);

  //! Do not make extra checks to detect FPU NaN results.
  const_bool_nodef(fpu_check_nan_result, false);

  /*! \brief
    For constructors, by default use the same rounding used by
    underlying type.
  */
  static const Rounding_Dir ROUND_DEFAULT_CONSTRUCTOR = ROUND_NATIVE;

  /*! \brief
    For overloaded operators (operator+(), operator-(), ...), by
    default use the same rounding used by the underlying type.
  */
  static const Rounding_Dir ROUND_DEFAULT_OPERATOR = ROUND_NATIVE;

  /*! \brief
    For input functions, by default use the same rounding used by
    the underlying type.
  */
  static const Rounding_Dir ROUND_DEFAULT_INPUT = ROUND_NATIVE;

  /*! \brief
    For output functions, by default use the same rounding used by
    the underlying type.
  */
  static const Rounding_Dir ROUND_DEFAULT_OUTPUT = ROUND_NATIVE;

  /*! \brief
    For all other functions, by default use the same rounding used by
    the underlying type.
  */
  static const Rounding_Dir ROUND_DEFAULT_FUNCTION = ROUND_NATIVE;

  /*! \brief
    Handles \p r: called by all constructors, operators and functions that
    do not return a Result value.
  */
  static void handle_result(Result r);
};

} // namespace Parma_Polyhedra_Library

#define CHECK_P(cond, check) ((cond) ? (check) : (assert(!(check)), false))

/* Automatically generated from PPL source file ../src/checked_inlines.hh line 1. */
/* Abstract checked arithmetic functions: fall-backs.
*/


/* Automatically generated from PPL source file ../src/checked_inlines.hh line 31. */

/*! \brief
  Performs the test <CODE>a < b</CODE> avoiding the warning about the
  comparison being always false due to limited range of data type.
  FIXME: we have not found a working solution. The GCC option
  -Wno-type-limits suppresses the warning
*/
#define PPL_LT_SILENT(a, b) ((a) < (b))
#define PPL_GT_SILENT(a, b) ((a) > (b))

namespace Parma_Polyhedra_Library {

namespace Checked {

template <typename T1, typename T2>
struct Safe_Conversion : public False {
};
template <typename T>
struct Safe_Conversion<T, T> : public True {
};

#define PPL_SAFE_CONVERSION(To, From)                        \
  template <> struct Safe_Conversion<PPL_U(To), PPL_U(From)> \
    : public True { }

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(signed short, char);
#endif
PPL_SAFE_CONVERSION(signed short, signed char);
#if PPL_SIZEOF_CHAR < PPL_SIZEOF_SHORT
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(signed short, char);
#endif
PPL_SAFE_CONVERSION(signed short, unsigned char);
#endif

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(signed int, char);
#endif
PPL_SAFE_CONVERSION(signed int, signed char);
PPL_SAFE_CONVERSION(signed int, signed short);
#if PPL_SIZEOF_CHAR < PPL_SIZEOF_INT
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(signed int, char);
#endif
PPL_SAFE_CONVERSION(signed int, unsigned char);
#endif
#if PPL_SIZEOF_SHORT < PPL_SIZEOF_INT
PPL_SAFE_CONVERSION(signed int, unsigned short);
#endif

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(signed long, char);
#endif
PPL_SAFE_CONVERSION(signed long, signed char);
PPL_SAFE_CONVERSION(signed long, signed short);
PPL_SAFE_CONVERSION(signed long, signed int);
#if PPL_SIZEOF_CHAR < PPL_SIZEOF_LONG
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(signed long, char);
#endif
PPL_SAFE_CONVERSION(signed long, unsigned char);
#endif
#if PPL_SIZEOF_SHORT < PPL_SIZEOF_LONG
PPL_SAFE_CONVERSION(signed long, unsigned short);
#endif
#if PPL_SIZEOF_INT < PPL_SIZEOF_LONG
PPL_SAFE_CONVERSION(signed long, unsigned int);
#endif

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(signed long long, char);
#endif
PPL_SAFE_CONVERSION(signed long long, signed char);
PPL_SAFE_CONVERSION(signed long long, signed short);
PPL_SAFE_CONVERSION(signed long long, signed int);
PPL_SAFE_CONVERSION(signed long long, signed long);
#if PPL_SIZEOF_CHAR < PPL_SIZEOF_LONG_LONG
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(signed long long, char);
#endif
PPL_SAFE_CONVERSION(signed long long, unsigned char);
#endif
#if PPL_SIZEOF_SHORT < PPL_SIZEOF_LONG_LONG
PPL_SAFE_CONVERSION(signed long long, unsigned short);
#endif
#if PPL_SIZEOF_INT < PPL_SIZEOF_LONG_LONG
PPL_SAFE_CONVERSION(signed long long, unsigned int);
#endif
#if PPL_SIZEOF_LONG < PPL_SIZEOF_LONG_LONG
PPL_SAFE_CONVERSION(signed long long, unsigned long);
#endif

#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(unsigned short, char);
#endif
PPL_SAFE_CONVERSION(unsigned short, unsigned char);

#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(unsigned int, char);
#endif
PPL_SAFE_CONVERSION(unsigned int, unsigned char);
PPL_SAFE_CONVERSION(unsigned int, unsigned short);

#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(unsigned long, char);
#endif
PPL_SAFE_CONVERSION(unsigned long, unsigned char);
PPL_SAFE_CONVERSION(unsigned long, unsigned short);
PPL_SAFE_CONVERSION(unsigned long, unsigned int);

#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SAFE_CONVERSION(unsigned long long, char);
#endif
PPL_SAFE_CONVERSION(unsigned long long, unsigned char);
PPL_SAFE_CONVERSION(unsigned long long, unsigned short);
PPL_SAFE_CONVERSION(unsigned long long, unsigned int);
PPL_SAFE_CONVERSION(unsigned long long, unsigned long);


#if PPL_SIZEOF_CHAR <= PPL_SIZEOF_FLOAT - 2
PPL_SAFE_CONVERSION(float, char);
PPL_SAFE_CONVERSION(float, signed char);
PPL_SAFE_CONVERSION(float, unsigned char);
#endif
#if PPL_SIZEOF_SHORT <= PPL_SIZEOF_FLOAT - 2
PPL_SAFE_CONVERSION(float, signed short);
PPL_SAFE_CONVERSION(float, unsigned short);
#endif
#if PPL_SIZEOF_INT <= PPL_SIZEOF_FLOAT - 2
PPL_SAFE_CONVERSION(float, signed int);
PPL_SAFE_CONVERSION(float, unsigned int);
#endif
#if PPL_SIZEOF_LONG <= PPL_SIZEOF_FLOAT - 2
PPL_SAFE_CONVERSION(float, signed long);
PPL_SAFE_CONVERSION(float, unsigned long);
#endif
#if PPL_SIZEOF_LONG_LONG <= PPL_SIZEOF_FLOAT - 2
PPL_SAFE_CONVERSION(float, signed long long);
PPL_SAFE_CONVERSION(float, unsigned long long);
#endif

#if PPL_SIZEOF_CHAR <= PPL_SIZEOF_DOUBLE - 4
PPL_SAFE_CONVERSION(double, char);
PPL_SAFE_CONVERSION(double, signed char);
PPL_SAFE_CONVERSION(double, unsigned char);
#endif
#if PPL_SIZEOF_SHORT <= PPL_SIZEOF_DOUBLE - 4
PPL_SAFE_CONVERSION(double, signed short);
PPL_SAFE_CONVERSION(double, unsigned short);
#endif
#if PPL_SIZEOF_INT <= PPL_SIZEOF_DOUBLE - 4
PPL_SAFE_CONVERSION(double, signed int);
PPL_SAFE_CONVERSION(double, unsigned int);
#endif
#if PPL_SIZEOF_LONG <= PPL_SIZEOF_DOUBLE - 4
PPL_SAFE_CONVERSION(double, signed long);
PPL_SAFE_CONVERSION(double, unsigned long);
#endif
#if PPL_SIZEOF_LONG_LONG <= PPL_SIZEOF_DOUBLE - 4
PPL_SAFE_CONVERSION(double, signed long long);
PPL_SAFE_CONVERSION(double, unsigned long long);
#endif
PPL_SAFE_CONVERSION(double, float);

#if PPL_SIZEOF_CHAR <= PPL_SIZEOF_LONG_DOUBLE - 4
PPL_SAFE_CONVERSION(long double, char);
PPL_SAFE_CONVERSION(long double, signed char);
PPL_SAFE_CONVERSION(long double, unsigned char);
#endif
#if PPL_SIZEOF_SHORT <= PPL_SIZEOF_LONG_DOUBLE - 4
PPL_SAFE_CONVERSION(long double, signed short);
PPL_SAFE_CONVERSION(long double, unsigned short);
#endif
#if PPL_SIZEOF_INT <= PPL_SIZEOF_LONG_DOUBLE - 4
PPL_SAFE_CONVERSION(long double, signed int);
PPL_SAFE_CONVERSION(long double, unsigned int);
#endif
#if PPL_SIZEOF_LONG <= PPL_SIZEOF_LONG_DOUBLE - 4
PPL_SAFE_CONVERSION(long double, signed long);
PPL_SAFE_CONVERSION(long double, unsigned long);
#endif
#if PPL_SIZEOF_LONG_LONG <= PPL_SIZEOF_LONG_DOUBLE - 4
PPL_SAFE_CONVERSION(long double, signed long long);
PPL_SAFE_CONVERSION(long double, unsigned long long);
#endif
PPL_SAFE_CONVERSION(long double, float);
PPL_SAFE_CONVERSION(long double, double);

PPL_SAFE_CONVERSION(mpz_class, char);
PPL_SAFE_CONVERSION(mpz_class, signed char);
PPL_SAFE_CONVERSION(mpz_class, signed short);
PPL_SAFE_CONVERSION(mpz_class, signed int);
PPL_SAFE_CONVERSION(mpz_class, signed long);
// GMP's API does not support signed long long.
PPL_SAFE_CONVERSION(mpz_class, unsigned char);
PPL_SAFE_CONVERSION(mpz_class, unsigned short);
PPL_SAFE_CONVERSION(mpz_class, unsigned int);
PPL_SAFE_CONVERSION(mpz_class, unsigned long);
// GMP's API does not support unsigned long long.

PPL_SAFE_CONVERSION(mpq_class, char);
PPL_SAFE_CONVERSION(mpq_class, signed char);
PPL_SAFE_CONVERSION(mpq_class, signed short);
PPL_SAFE_CONVERSION(mpq_class, signed int);
PPL_SAFE_CONVERSION(mpq_class, signed long);
// GMP's API does not support signed long long.
PPL_SAFE_CONVERSION(mpq_class, unsigned char);
PPL_SAFE_CONVERSION(mpq_class, unsigned short);
PPL_SAFE_CONVERSION(mpq_class, unsigned int);
PPL_SAFE_CONVERSION(mpq_class, unsigned long);
// GMP's API does not support unsigned long long.
PPL_SAFE_CONVERSION(mpq_class, float);
PPL_SAFE_CONVERSION(mpq_class, double);
// GMP's API does not support long double.

#undef PPL_SAFE_CONVERSION

template <typename Policy, typename Type>
struct PPL_FUNCTION_CLASS(construct)<Policy, Policy, Type, Type> {
  static inline Result function(Type& to, const Type& from, Rounding_Dir) {
    new(&to) Type(from);
    return V_EQ;
  }
};

template <typename To_Policy, typename From_Policy, typename To, typename From>
struct PPL_FUNCTION_CLASS(construct) {
  static inline Result function(To& to, const From& from, Rounding_Dir dir) {
    new(&to) To();
    return assign<To_Policy, From_Policy>(to, from, dir);
  }
};

template <typename To_Policy, typename To>
struct PPL_FUNCTION_CLASS(construct_special) {
  static inline Result function(To& to, Result_Class r, Rounding_Dir dir) {
    new(&to) To();
    return assign_special<To_Policy>(to, r, dir);
  }
};

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
assign_exact(To& to, const From& from, Rounding_Dir) {
  to = from;
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline typename Enable_If<Is_Same<To_Policy, From_Policy>::value, void>::type
copy_generic(Type& to, const Type& from) {
  to = from;
}

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
abs_generic(To& to, const From& from, Rounding_Dir dir) {
  if (from < 0) {
    return neg<To_Policy, From_Policy>(to, from, dir);
  }
  else {
    return assign<To_Policy, From_Policy>(to, from, dir);
  }
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From>
inline void
gcd_exact_no_abs(To& to, const From& x, const From& y) {
  To w_x = x;
  To w_y = y;
  To remainder;
  while (w_y != 0) {
    // The following is derived from the assumption that w_x % w_y
    // is always representable. This is true for both native integers
    // and IEC 559 floating point numbers.
    rem<To_Policy, From1_Policy, From2_Policy>(remainder, w_x, w_y,
                                               ROUND_NOT_NEEDED);
    w_x = w_y;
    w_y = remainder;
  }
  to = w_x;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
gcd_exact(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  gcd_exact_no_abs<To_Policy, From1_Policy, From2_Policy>(to, x, y);
  return abs<To_Policy, To_Policy>(to, to, dir);
}

template <typename To1_Policy, typename To2_Policy, typename To3_Policy,
          typename From1_Policy, typename From2_Policy,
          typename To1, typename To2, typename To3,
          typename From1, typename From2>
inline Result
gcdext_exact(To1& to, To2& s, To3& t, const From1& x, const From2& y,
             Rounding_Dir dir) {
  // In case this becomes a bottleneck, we may consider using the
  // Stehle'-Zimmermann algorithm (see R. Crandall and C. Pomerance,
  // Prime Numbers - A Computational Perspective, Second Edition,
  // Springer, 2005).
  if (y == 0) {
    if (x == 0) {
      s = 0;
      t = 1;
      return V_EQ;
    }
    else {
      if (x < 0) {
        s = -1;
      }
      else {
        s = 1;
      }
      t = 0;
      return abs<To1_Policy, From1_Policy>(to, x, dir);
    }
  }

  s = 1;
  t = 0;
  bool negative_x = x < 0;
  bool negative_y = y < 0;

  Result r;
  r = abs<To1_Policy, From1_Policy>(to, x, dir);
  if (r != V_EQ) {
    return r;
  }

  From2 a_y;
  r = abs<To1_Policy, From2_Policy>(a_y, y, dir);
  if (r != V_EQ) {
    return r;
  }

  // If PPL_MATCH_GMP_GCDEXT is defined then s is favored when the absolute
  // values of the given numbers are equal.  For instance if x and y
  // are both 5 then s will be 1 and t will be 0, instead of the other
  // way round.  This is to match the behavior of GMP.
#define PPL_MATCH_GMP_GCDEXT 1
#ifdef PPL_MATCH_GMP_GCDEXT
  if (to == a_y) {
    goto sign_check;
  }
#endif

  {
    To2 v1 = 0;
    To3 v2 = 1;
    To1 v3 = static_cast<To1>(a_y);
    while (true) {
      To1 q = to / v3;
      // Remainder, next candidate GCD.
      To1 t3 = to - q*v3;
      To2 t1 = s - static_cast<To2>(q)*v1;
      To3 t2 = t - static_cast<To3>(q)*v2;
      s = v1;
      t = v2;
      to = v3;
      if (t3 == 0) {
        break;
      }
      v1 = t1;
      v2 = t2;
      v3 = t3;
    }
  }

#ifdef PPL_MATCH_GMP_GCDEXT
 sign_check:
#endif
  if (negative_x) {
    r = neg<To2_Policy, To2_Policy>(s, s, dir);
    if (r != V_EQ) {
      return r;
    }
  }
  if (negative_y) {
    return neg<To3_Policy, To3_Policy>(t, t, dir);
  }
  return V_EQ;
#undef PPL_MATCH_GMP_GCDEXT
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
lcm_gcd_exact(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  if (x == 0 || y == 0) {
    to = 0;
    return V_EQ;
  }
  To a_x;
  To a_y;
  Result r;
  r = abs<From1_Policy, From1_Policy>(a_x, x, dir);
  if (r != V_EQ) {
    return r;
  }
  r = abs<From2_Policy, From2_Policy>(a_y, y, dir);
  if (r != V_EQ) {
    return r;
  }
  To gcd;
  gcd_exact_no_abs<To_Policy, From1_Policy, From2_Policy>(gcd, a_x, a_y);
  // The following is derived from the assumption that a_x / gcd(a_x, a_y)
  // is always representable. This is true for both native integers
  // and IEC 559 floating point numbers.
  div<To_Policy, From1_Policy, To_Policy>(to, a_x, gcd, ROUND_NOT_NEEDED);
  return mul<To_Policy, To_Policy, From2_Policy>(to, to, a_y, dir);
}

template <typename Policy, typename Type>
inline Result_Relation
sgn_generic(const Type& x) {
  if (x > 0) {
    return VR_GT;
  }
  if (x == 0) {
    return VR_EQ;
  }
  return VR_LT;
}

template <typename T1, typename T2, typename Enable = void>
struct Safe_Int_Comparison : public False {
};

template <typename T1, typename T2>
struct Safe_Int_Comparison<T1, T2, typename Enable_If<(C_Integer<T1>::value && C_Integer<T2>::value)>::type>
  : public Bool<(C_Integer<T1>::is_signed
                 ? (C_Integer<T2>::is_signed
                    || sizeof(T2) < sizeof(T1)
                    || sizeof(T2) < sizeof(int))
                 : (!C_Integer<T2>::is_signed
                    || sizeof(T1) < sizeof(T2)
                    || sizeof(T1) < sizeof(int)))> {
};


template <typename T1, typename T2>
inline typename Enable_If<(Safe_Int_Comparison<T1, T2>::value
                           || Safe_Conversion<T1, T2>::value
                           || Safe_Conversion<T2, T1>::value), bool>::type
lt(const T1& x, const T2& y) {
  return x < y;
}
template <typename T1, typename T2>
inline typename Enable_If<(Safe_Int_Comparison<T1, T2>::value
                           || Safe_Conversion<T1, T2>::value
                           || Safe_Conversion<T2, T1>::value), bool>::type
le(const T1& x, const T2& y) {
  return x <= y;
}
template <typename T1, typename T2>
inline typename Enable_If<(Safe_Int_Comparison<T1, T2>::value
                           || Safe_Conversion<T1, T2>::value
                           || Safe_Conversion<T2, T1>::value), bool>::type
eq(const T1& x, const T2& y) {
  return x == y;
}

template <typename S, typename U>
inline typename Enable_If<(!Safe_Int_Comparison<S, U>::value
                           && C_Integer<U>::value
                           && C_Integer<S>::is_signed), bool>::type
lt(const S& x, const U& y) {
  return x < 0 || static_cast<typename C_Integer<S>::other_type>(x) < y;
}

template <typename U, typename S>
inline typename Enable_If<(!Safe_Int_Comparison<S, U>::value
                           && C_Integer<U>::value
                           && C_Integer<S>::is_signed), bool>::type
lt(const U& x, const S& y) {
  return y >= 0 && x < static_cast<typename C_Integer<S>::other_type>(y);
}

template <typename S, typename U>
inline typename Enable_If<(!Safe_Int_Comparison<S, U>::value
                           && C_Integer<U>::value
                           && C_Integer<S>::is_signed), bool>::type
le(const S& x, const U& y) {
  return x < 0 || static_cast<typename C_Integer<S>::other_type>(x) <= y;
}

template <typename U, typename S>
inline typename Enable_If<(!Safe_Int_Comparison<S, U>::value
                           && C_Integer<U>::value
                           && C_Integer<S>::is_signed), bool>::type
le(const U& x, const S& y) {
  return y >= 0 && x <= static_cast<typename C_Integer<S>::other_type>(y);
}

template <typename S, typename U>
inline typename Enable_If<(!Safe_Int_Comparison<S, U>::value
                           && C_Integer<U>::value
                           && C_Integer<S>::is_signed), bool>::type
eq(const S& x, const U& y) {
  return x >= 0 && static_cast<typename C_Integer<S>::other_type>(x) == y;
}

template <typename U, typename S>
inline typename Enable_If<(!Safe_Int_Comparison<S, U>::value
                           && C_Integer<U>::value
                           && C_Integer<S>::is_signed), bool>::type
eq(const U& x, const S& y) {
  return y >= 0 && x == static_cast<typename C_Integer<S>::other_type>(y);
}

template <typename T1, typename T2>
inline typename Enable_If<(!Safe_Conversion<T1, T2>::value
                           && !Safe_Conversion<T2, T1>::value
                           && (!C_Integer<T1>::value || !C_Integer<T2>::value)), bool>::type
eq(const T1& x, const T2& y) {
  PPL_DIRTY_TEMP(T1, tmp);
  Result r = assign_r(tmp, y, ROUND_CHECK);
  // FIXME: We can do this also without fpu inexact check using a
  // conversion back and forth and then testing equality.  We should
  // code this in checked_float_inlines.hh, probably it's faster also
  // if fpu supports inexact check.
  PPL_ASSERT(r != V_LE && r != V_GE && r != V_LGE);
  return r == V_EQ && x == tmp;
}

template <typename T1, typename T2>
inline typename Enable_If<(!Safe_Conversion<T1, T2>::value
                           && !Safe_Conversion<T2, T1>::value
                           && (!C_Integer<T1>::value || !C_Integer<T2>::value)), bool>::type
lt(const T1& x, const T2& y) {
  PPL_DIRTY_TEMP(T1, tmp);
  Result r = assign_r(tmp, y, ROUND_UP);
  if (!result_representable(r)) {
    return true;
  }
  switch (result_relation(r)) {
  case VR_EQ:
  case VR_LT:
  case VR_LE:
    return x < tmp;
  default:
    return false;
  }
}

template <typename T1, typename T2>
inline typename
Enable_If<(!Safe_Conversion<T1, T2>::value
           && !Safe_Conversion<T2, T1>::value
           && (!C_Integer<T1>::value || !C_Integer<T2>::value)), bool>::type
le(const T1& x, const T2& y) {
  PPL_DIRTY_TEMP(T1, tmp);
  Result r = assign_r(tmp, y, (ROUND_UP | ROUND_STRICT_RELATION));
  // FIXME: We can do this also without fpu inexact check using a
  // conversion back and forth and then testing equality.  We should
  // code this in checked_float_inlines.hh, probably it's faster also
  // if fpu supports inexact check.
  PPL_ASSERT(r != V_LE && r != V_GE && r != V_LGE);
  if (!result_representable(r)) {
    return true;
  }
  switch (result_relation(r)) {
  case VR_EQ:
    return x <= tmp;
  case VR_LT:
    return x < tmp;
  case VR_LE:
  case VR_GE:
  case VR_LGE:
    // See comment above.
    PPL_UNREACHABLE;
    return false;
  default:
    return false;
  }
}

template <typename Policy1, typename Policy2,
          typename Type1, typename Type2>
inline bool
lt_p(const Type1& x, const Type2& y) {
  return lt(x, y);
}

template <typename Policy1, typename Policy2,
          typename Type1, typename Type2>
inline bool
le_p(const Type1& x, const Type2& y) {
  return le(x, y);
}

template <typename Policy1, typename Policy2,
          typename Type1, typename Type2>
inline bool
eq_p(const Type1& x, const Type2& y) {
  return eq(x, y);
}

template <typename Policy1, typename Policy2,
          typename Type1, typename Type2>
inline Result_Relation
cmp_generic(const Type1& x, const Type2& y) {
  if (lt(y, x)) {
    return VR_GT;
  }
  if (lt(x, y)) {
    return VR_LT;
  }
  return VR_EQ;
}

template <typename Policy, typename Type>
inline Result
assign_nan(Type& to, Result r) {
  assign_special<Policy>(to, VC_NAN, ROUND_IGNORE);
  return r;
}

template <typename Policy, typename Type>
inline Result
input_generic(Type& to, std::istream& is, Rounding_Dir dir) {
  PPL_DIRTY_TEMP(mpq_class, q);
  Result r = input_mpq(q, is);
  Result_Class c = result_class(r);
  switch (c) {
  case VC_MINUS_INFINITY:
  case VC_PLUS_INFINITY:
    return assign_special<Policy>(to, c, dir);
  case VC_NAN:
    return assign_nan<Policy>(to, r);
  default:
    break;
  }
  PPL_ASSERT(r == V_EQ);
  return assign<Policy, void>(to, q, dir);
}

} // namespace Checked

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/checked_int_inlines.hh line 1. */
/* Specialized "checked" functions for native integer numbers.
*/


/* Automatically generated from PPL source file ../src/checked_int_inlines.hh line 28. */
#include <cerrno>
#include <cstdlib>
#include <climits>
#include <string>

#if !PPL_HAVE_DECL_STRTOLL
signed long long
strtoll(const char* nptr, char** endptr, int base);
#endif

#if !PPL_HAVE_DECL_STRTOULL
unsigned long long
strtoull(const char* nptr, char** endptr, int base);
#endif

namespace Parma_Polyhedra_Library {

namespace Checked {

#ifndef PPL_HAVE_INT_FAST16_T
typedef int16_t int_fast16_t;
#endif

#ifndef PPL_HAVE_INT_FAST32_T
typedef int32_t int_fast32_t;
#endif

#ifndef PPL_HAVE_INT_FAST64_T
typedef int64_t int_fast64_t;
#endif

#ifndef PPL_HAVE_UINT_FAST16_T
typedef uint16_t uint_fast16_t;
#endif

#ifndef PPL_HAVE_UINT_FAST32_T
typedef uint32_t uint_fast32_t;
#endif

#ifndef PPL_HAVE_UINT_FAST64_T
typedef uint64_t uint_fast64_t;
#endif

template <typename Policy, typename Type>
struct Extended_Int {
  static const Type plus_infinity = C_Integer<Type>::max;
  static const Type minus_infinity = ((C_Integer<Type>::min >= 0)
                                      ? (C_Integer<Type>::max - 1)
                                      : C_Integer<Type>::min);
  static const Type not_a_number
  = ((C_Integer<Type>::min >= 0)
     ? (C_Integer<Type>::max - 2 * (Policy::has_infinity ? 1 : 0))
     : (C_Integer<Type>::min + (Policy::has_infinity ? 1 : 0)));
  static const Type min
  = (C_Integer<Type>::min
     + ((C_Integer<Type>::min >= 0)
        ? 0
        : ((Policy::has_infinity ? 1 : 0) + (Policy::has_nan ? 1 : 0))));
  static const Type max
  = (C_Integer<Type>::max
     - ((C_Integer<Type>::min >= 0)
        ? (2 * (Policy::has_infinity ? 1 : 0) + (Policy::has_nan ? 1 : 0))
        : (Policy::has_infinity ? 1 : 0)));
};

template <typename Policy, typename To>
inline Result
set_neg_overflow_int(To& to, Rounding_Dir dir) {
  if (round_up(dir)) {
    to = Extended_Int<Policy, To>::min;
    return V_LT_INF;
  }
  else {
    if (Policy::has_infinity) {
      to = Extended_Int<Policy, To>::minus_infinity;
      return V_GT_MINUS_INFINITY;
    }
    return V_GT_MINUS_INFINITY | V_UNREPRESENTABLE;
  }
}

template <typename Policy, typename To>
inline Result
set_pos_overflow_int(To& to, Rounding_Dir dir) {
  if (round_down(dir)) {
    to = Extended_Int<Policy, To>::max;
    return V_GT_SUP;
  }
  else {
    if (Policy::has_infinity) {
      to = Extended_Int<Policy, To>::plus_infinity;
      return V_LT_PLUS_INFINITY;
    }
    return V_LT_PLUS_INFINITY | V_UNREPRESENTABLE;
  }
}

template <typename Policy, typename To>
inline Result
round_lt_int_no_overflow(To& to, Rounding_Dir dir) {
  if (round_down(dir)) {
    --to;
    return V_GT;
  }
  return V_LT;
}

template <typename Policy, typename To>
inline Result
round_gt_int_no_overflow(To& to, Rounding_Dir dir) {
  if (round_up(dir)) {
    ++to;
    return V_LT;
  }
  return V_GT;
}

template <typename Policy, typename To>
inline Result
round_lt_int(To& to, Rounding_Dir dir) {
  if (round_down(dir)) {
    if (to == Extended_Int<Policy, To>::min) {
      if (Policy::has_infinity) {
        to = Extended_Int<Policy, To>::minus_infinity;
        return V_GT_MINUS_INFINITY;
      }
      return V_GT_MINUS_INFINITY | V_UNREPRESENTABLE;
    }
    else {
      --to;
      return V_GT;
    }
  }
  return V_LT;
}

template <typename Policy, typename To>
inline Result
round_gt_int(To& to, Rounding_Dir dir) {
  if (round_up(dir)) {
    if (to == Extended_Int<Policy, To>::max) {
      if (Policy::has_infinity) {
        to = Extended_Int<Policy, To>::plus_infinity;
        return V_LT_PLUS_INFINITY;
      }
      return V_LT_PLUS_INFINITY | V_UNREPRESENTABLE;
    }
    else {
      ++to;
      return V_LT;
    }
  }
  return V_GT;
}

PPL_SPECIALIZE_COPY(copy_generic, char)
PPL_SPECIALIZE_COPY(copy_generic, signed char)
PPL_SPECIALIZE_COPY(copy_generic, signed short)
PPL_SPECIALIZE_COPY(copy_generic, signed int)
PPL_SPECIALIZE_COPY(copy_generic, signed long)
PPL_SPECIALIZE_COPY(copy_generic, signed long long)
PPL_SPECIALIZE_COPY(copy_generic, unsigned char)
PPL_SPECIALIZE_COPY(copy_generic, unsigned short)
PPL_SPECIALIZE_COPY(copy_generic, unsigned int)
PPL_SPECIALIZE_COPY(copy_generic, unsigned long)
PPL_SPECIALIZE_COPY(copy_generic, unsigned long long)

template <typename Policy, typename Type>
inline Result
classify_int(const Type v, bool nan, bool inf, bool sign) {
  if (Policy::has_nan
      && (nan || sign)
      && v == Extended_Int<Policy, Type>::not_a_number) {
    return V_NAN;
  }
  if (!inf && !sign) {
    return V_LGE;
  }
  if (Policy::has_infinity) {
    if (v == Extended_Int<Policy, Type>::minus_infinity) {
      return inf ? V_EQ_MINUS_INFINITY : V_LT;
    }
    if (v == Extended_Int<Policy, Type>::plus_infinity) {
      return inf ? V_EQ_PLUS_INFINITY : V_GT;
    }
  }
  if (sign) {
    if (v < 0) {
      return V_LT;
    }
    if (v > 0) {
      return V_GT;
    }
    return V_EQ;
  }
  return V_LGE;
}

PPL_SPECIALIZE_CLASSIFY(classify_int, char)
PPL_SPECIALIZE_CLASSIFY(classify_int, signed char)
PPL_SPECIALIZE_CLASSIFY(classify_int, signed short)
PPL_SPECIALIZE_CLASSIFY(classify_int, signed int)
PPL_SPECIALIZE_CLASSIFY(classify_int, signed long)
PPL_SPECIALIZE_CLASSIFY(classify_int, signed long long)
PPL_SPECIALIZE_CLASSIFY(classify_int, unsigned char)
PPL_SPECIALIZE_CLASSIFY(classify_int, unsigned short)
PPL_SPECIALIZE_CLASSIFY(classify_int, unsigned int)
PPL_SPECIALIZE_CLASSIFY(classify_int, unsigned long)
PPL_SPECIALIZE_CLASSIFY(classify_int, unsigned long long)

template <typename Policy, typename Type>
inline bool
is_nan_int(const Type v) {
  return Policy::has_nan && v == Extended_Int<Policy, Type>::not_a_number;
}

PPL_SPECIALIZE_IS_NAN(is_nan_int, char)
PPL_SPECIALIZE_IS_NAN(is_nan_int, signed char)
PPL_SPECIALIZE_IS_NAN(is_nan_int, signed short)
PPL_SPECIALIZE_IS_NAN(is_nan_int, signed int)
PPL_SPECIALIZE_IS_NAN(is_nan_int, signed long)
PPL_SPECIALIZE_IS_NAN(is_nan_int, signed long long)
PPL_SPECIALIZE_IS_NAN(is_nan_int, unsigned char)
PPL_SPECIALIZE_IS_NAN(is_nan_int, unsigned short)
PPL_SPECIALIZE_IS_NAN(is_nan_int, unsigned int)
PPL_SPECIALIZE_IS_NAN(is_nan_int, unsigned long)
PPL_SPECIALIZE_IS_NAN(is_nan_int, unsigned long long)

template <typename Policy, typename Type>
inline bool
is_minf_int(const Type v) {
  return Policy::has_infinity
    && v == Extended_Int<Policy, Type>::minus_infinity;
}

PPL_SPECIALIZE_IS_MINF(is_minf_int, char)
PPL_SPECIALIZE_IS_MINF(is_minf_int, signed char)
PPL_SPECIALIZE_IS_MINF(is_minf_int, signed short)
PPL_SPECIALIZE_IS_MINF(is_minf_int, signed int)
PPL_SPECIALIZE_IS_MINF(is_minf_int, signed long)
PPL_SPECIALIZE_IS_MINF(is_minf_int, signed long long)
PPL_SPECIALIZE_IS_MINF(is_minf_int, unsigned char)
PPL_SPECIALIZE_IS_MINF(is_minf_int, unsigned short)
PPL_SPECIALIZE_IS_MINF(is_minf_int, unsigned int)
PPL_SPECIALIZE_IS_MINF(is_minf_int, unsigned long)
PPL_SPECIALIZE_IS_MINF(is_minf_int, unsigned long long)

template <typename Policy, typename Type>
inline bool
is_pinf_int(const Type v) {
  return Policy::has_infinity
    && v == Extended_Int<Policy, Type>::plus_infinity;
}

PPL_SPECIALIZE_IS_PINF(is_pinf_int, char)
PPL_SPECIALIZE_IS_PINF(is_pinf_int, signed char)
PPL_SPECIALIZE_IS_PINF(is_pinf_int, signed short)
PPL_SPECIALIZE_IS_PINF(is_pinf_int, signed int)
PPL_SPECIALIZE_IS_PINF(is_pinf_int, signed long)
PPL_SPECIALIZE_IS_PINF(is_pinf_int, signed long long)
PPL_SPECIALIZE_IS_PINF(is_pinf_int, unsigned char)
PPL_SPECIALIZE_IS_PINF(is_pinf_int, unsigned short)
PPL_SPECIALIZE_IS_PINF(is_pinf_int, unsigned int)
PPL_SPECIALIZE_IS_PINF(is_pinf_int, unsigned long)
PPL_SPECIALIZE_IS_PINF(is_pinf_int, unsigned long long)

template <typename Policy, typename Type>
inline bool
is_int_int(const Type v) {
  return !is_nan<Policy>(v);
}

PPL_SPECIALIZE_IS_INT(is_int_int, char)
PPL_SPECIALIZE_IS_INT(is_int_int, signed char)
PPL_SPECIALIZE_IS_INT(is_int_int, signed short)
PPL_SPECIALIZE_IS_INT(is_int_int, signed int)
PPL_SPECIALIZE_IS_INT(is_int_int, signed long)
PPL_SPECIALIZE_IS_INT(is_int_int, signed long long)
PPL_SPECIALIZE_IS_INT(is_int_int, unsigned char)
PPL_SPECIALIZE_IS_INT(is_int_int, unsigned short)
PPL_SPECIALIZE_IS_INT(is_int_int, unsigned int)
PPL_SPECIALIZE_IS_INT(is_int_int, unsigned long)
PPL_SPECIALIZE_IS_INT(is_int_int, unsigned long long)

template <typename Policy, typename Type>
inline Result
assign_special_int(Type& v, Result_Class c, Rounding_Dir dir) {
  PPL_ASSERT(c == VC_MINUS_INFINITY || c == VC_PLUS_INFINITY || c == VC_NAN);
  switch (c) {
  case VC_NAN:
    if (Policy::has_nan) {
      v = Extended_Int<Policy, Type>::not_a_number;
      return V_NAN;
    }
    return V_NAN | V_UNREPRESENTABLE;
  case VC_MINUS_INFINITY:
    if (Policy::has_infinity) {
      v = Extended_Int<Policy, Type>::minus_infinity;
      return V_EQ_MINUS_INFINITY;
    }
    if (round_up(dir)) {
      v = Extended_Int<Policy, Type>::min;
      return V_LT_INF;
    }
    return V_EQ_MINUS_INFINITY | V_UNREPRESENTABLE;
  case VC_PLUS_INFINITY:
    if (Policy::has_infinity) {
      v = Extended_Int<Policy, Type>::plus_infinity;
      return V_EQ_PLUS_INFINITY;
    }
    if (round_down(dir)) {
      v = Extended_Int<Policy, Type>::max;
      return V_GT_SUP;
    }
    return V_EQ_PLUS_INFINITY | V_UNREPRESENTABLE;
  default:
    PPL_UNREACHABLE;
    return V_NAN | V_UNREPRESENTABLE;
  }
}

PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_int, char)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_int, signed char)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_int, signed short)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_int, signed int)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_int, signed long)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_int, signed long long)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_int, unsigned char)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_int, unsigned short)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_int, unsigned int)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_int, unsigned long)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_int, unsigned long long)

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
assign_signed_int_signed_int(To& to, const From from, Rounding_Dir dir) {
  if (sizeof(To) < sizeof(From)
      || (sizeof(To) == sizeof(From)
      && (Extended_Int<To_Policy, To>::min > Extended_Int<From_Policy, From>::min
      || Extended_Int<To_Policy, To>::max < Extended_Int<From_Policy, From>::max))) {
    if (CHECK_P(To_Policy::check_overflow,
                PPL_LT_SILENT(from,
                              static_cast<From>(Extended_Int<To_Policy, To>::min)))) {
      return set_neg_overflow_int<To_Policy>(to, dir);
    }
    if (CHECK_P(To_Policy::check_overflow,
                PPL_GT_SILENT(from,
                              static_cast<From>(Extended_Int<To_Policy, To>::max)))) {
      return set_pos_overflow_int<To_Policy>(to, dir);
    }
  }
  to = static_cast<To>(from);
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
assign_signed_int_unsigned_int(To& to, const From from, Rounding_Dir dir) {
  if (sizeof(To) <= sizeof(From)) {
    if (CHECK_P(To_Policy::check_overflow,
                from > static_cast<From>(Extended_Int<To_Policy, To>::max))) {
      return set_pos_overflow_int<To_Policy>(to, dir);
    }
  }
  to = static_cast<To>(from);
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
assign_unsigned_int_signed_int(To& to, const From from, Rounding_Dir dir) {
  if (CHECK_P(To_Policy::check_overflow, from < 0)) {
    return set_neg_overflow_int<To_Policy>(to, dir);
  }
  if (sizeof(To) < sizeof(From)) {
    if (CHECK_P(To_Policy::check_overflow,
                from > static_cast<From>(Extended_Int<To_Policy, To>::max))) {
      return set_pos_overflow_int<To_Policy>(to, dir);
    }
  }
  to = static_cast<To>(from);
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
assign_unsigned_int_unsigned_int(To& to, const From from, Rounding_Dir dir) {
  if (sizeof(To) < sizeof(From)
      || (sizeof(To) == sizeof(From)
      && Extended_Int<To_Policy, To>::max < Extended_Int<From_Policy, From>::max)) {
    if (CHECK_P(To_Policy::check_overflow,
                PPL_GT_SILENT(from,
                              static_cast<From>(Extended_Int<To_Policy, To>::max)))) {
      return set_pos_overflow_int<To_Policy>(to, dir);
    }
  }
  to = static_cast<To>(from);
  return V_EQ;
}


#define PPL_ASSIGN2_SIGNED_SIGNED(Smaller, Larger) \
PPL_SPECIALIZE_ASSIGN(assign_signed_int_signed_int, Smaller, Larger) \
PPL_SPECIALIZE_ASSIGN(assign_signed_int_signed_int, Larger, Smaller)

#define PPL_ASSIGN2_UNSIGNED_UNSIGNED(Smaller, Larger) \
PPL_SPECIALIZE_ASSIGN(assign_unsigned_int_unsigned_int, Smaller, Larger) \
PPL_SPECIALIZE_ASSIGN(assign_unsigned_int_unsigned_int, Larger, Smaller)

#define PPL_ASSIGN2_UNSIGNED_SIGNED(Smaller, Larger) \
PPL_SPECIALIZE_ASSIGN(assign_unsigned_int_signed_int, Smaller, Larger) \
PPL_SPECIALIZE_ASSIGN(assign_signed_int_unsigned_int, Larger, Smaller)

#define PPL_ASSIGN2_SIGNED_UNSIGNED(Smaller, Larger) \
PPL_SPECIALIZE_ASSIGN(assign_signed_int_unsigned_int, Smaller, Larger) \
PPL_SPECIALIZE_ASSIGN(assign_unsigned_int_signed_int, Larger, Smaller)

#define PPL_ASSIGN_SIGNED(Type) \
PPL_SPECIALIZE_ASSIGN(assign_signed_int_signed_int, Type, Type)
#define PPL_ASSIGN_UNSIGNED(Type) \
PPL_SPECIALIZE_ASSIGN(assign_unsigned_int_unsigned_int, Type, Type)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_ASSIGN_SIGNED(char)
#endif
PPL_ASSIGN_SIGNED(signed char)
PPL_ASSIGN_SIGNED(signed short)
PPL_ASSIGN_SIGNED(signed int)
PPL_ASSIGN_SIGNED(signed long)
PPL_ASSIGN_SIGNED(signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_ASSIGN_UNSIGNED(char)
#endif
PPL_ASSIGN_UNSIGNED(unsigned char)
PPL_ASSIGN_UNSIGNED(unsigned short)
PPL_ASSIGN_UNSIGNED(unsigned int)
PPL_ASSIGN_UNSIGNED(unsigned long)
PPL_ASSIGN_UNSIGNED(unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_ASSIGN2_SIGNED_SIGNED(char, signed short)
PPL_ASSIGN2_SIGNED_SIGNED(char, signed int)
PPL_ASSIGN2_SIGNED_SIGNED(char, signed long)
PPL_ASSIGN2_SIGNED_SIGNED(char, signed long long)
#endif
PPL_ASSIGN2_SIGNED_SIGNED(signed char, signed short)
PPL_ASSIGN2_SIGNED_SIGNED(signed char, signed int)
PPL_ASSIGN2_SIGNED_SIGNED(signed char, signed long)
PPL_ASSIGN2_SIGNED_SIGNED(signed char, signed long long)
PPL_ASSIGN2_SIGNED_SIGNED(signed short, signed int)
PPL_ASSIGN2_SIGNED_SIGNED(signed short, signed long)
PPL_ASSIGN2_SIGNED_SIGNED(signed short, signed long long)
PPL_ASSIGN2_SIGNED_SIGNED(signed int, signed long)
PPL_ASSIGN2_SIGNED_SIGNED(signed int, signed long long)
PPL_ASSIGN2_SIGNED_SIGNED(signed long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_ASSIGN2_UNSIGNED_UNSIGNED(char, unsigned short)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(char, unsigned int)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(char, unsigned long)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(char, unsigned long long)
#endif
PPL_ASSIGN2_UNSIGNED_UNSIGNED(unsigned char, unsigned short)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(unsigned char, unsigned int)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(unsigned char, unsigned long)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(unsigned char, unsigned long long)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(unsigned short, unsigned int)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(unsigned short, unsigned long)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(unsigned short, unsigned long long)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(unsigned int, unsigned long)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(unsigned int, unsigned long long)
PPL_ASSIGN2_UNSIGNED_UNSIGNED(unsigned long, unsigned long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_ASSIGN2_UNSIGNED_SIGNED(char, signed short)
PPL_ASSIGN2_UNSIGNED_SIGNED(char, signed int)
PPL_ASSIGN2_UNSIGNED_SIGNED(char, signed long)
PPL_ASSIGN2_UNSIGNED_SIGNED(char, signed long long)
#endif
PPL_ASSIGN2_UNSIGNED_SIGNED(unsigned char, signed short)
PPL_ASSIGN2_UNSIGNED_SIGNED(unsigned char, signed int)
PPL_ASSIGN2_UNSIGNED_SIGNED(unsigned char, signed long)
PPL_ASSIGN2_UNSIGNED_SIGNED(unsigned char, signed long long)
PPL_ASSIGN2_UNSIGNED_SIGNED(unsigned short, signed int)
PPL_ASSIGN2_UNSIGNED_SIGNED(unsigned short, signed long)
PPL_ASSIGN2_UNSIGNED_SIGNED(unsigned short, signed long long)
PPL_ASSIGN2_UNSIGNED_SIGNED(unsigned int, signed long)
PPL_ASSIGN2_UNSIGNED_SIGNED(unsigned int, signed long long)
PPL_ASSIGN2_UNSIGNED_SIGNED(unsigned long, signed long long)
#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_ASSIGN2_SIGNED_UNSIGNED(char, unsigned char)
PPL_ASSIGN2_SIGNED_UNSIGNED(char, unsigned short)
PPL_ASSIGN2_SIGNED_UNSIGNED(char, unsigned int)
PPL_ASSIGN2_SIGNED_UNSIGNED(char, unsigned long)
PPL_ASSIGN2_SIGNED_UNSIGNED(char, unsigned long long)
#else
PPL_ASSIGN2_SIGNED_UNSIGNED(signed char, char)
#endif
PPL_ASSIGN2_SIGNED_UNSIGNED(signed char, unsigned char)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed char, unsigned short)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed char, unsigned int)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed char, unsigned long)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed char, unsigned long long)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed short, unsigned short)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed short, unsigned int)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed short, unsigned long)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed short, unsigned long long)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed int, unsigned int)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed int, unsigned long)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed int, unsigned long long)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed long, unsigned long)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed long, unsigned long long)
PPL_ASSIGN2_SIGNED_UNSIGNED(signed long long, unsigned long long)

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
assign_int_float(To& to, const From from, Rounding_Dir dir) {
  if (is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
#if 0
  // FIXME: this is correct but it is inefficient and breaks the build
  // for the missing definition of static const members (a problem present
  // also in other areas of the PPL).
  if (CHECK_P(To_Policy::check_overflow,
              lt(from, Extended_Int<To_Policy, To>::min))) {
    return set_neg_overflow_int<To_Policy>(to, dir);
  }
  if (CHECK_P(To_Policy::check_overflow,
              !le(from, Extended_Int<To_Policy, To>::max))) {
    return set_pos_overflow_int<To_Policy>(to, dir);
  }
#else
  if (CHECK_P(To_Policy::check_overflow,
             (from < Extended_Int<To_Policy, To>::min))) {
    return set_neg_overflow_int<To_Policy>(to, dir);
  }
  if (CHECK_P(To_Policy::check_overflow,
             (from > Extended_Int<To_Policy, To>::max))) {
    return set_pos_overflow_int<To_Policy>(to, dir);
  }
#endif
  if (round_not_requested(dir)) {
    to = from;
    return V_LGE;
  }
  From i_from = rint(from);
  to = i_from;
  if (from == i_from) {
    return V_EQ;
  }
  if (round_direct(ROUND_UP)) {
    return round_lt_int<To_Policy>(to, dir);
  }
  if (round_direct(ROUND_DOWN)) {
    return round_gt_int<To_Policy>(to, dir);
  }
  if (from < i_from) {
    return round_lt_int<To_Policy>(to, dir);
  }
  PPL_ASSERT(from > i_from);
  return round_gt_int<To_Policy>(to, dir);
}

PPL_SPECIALIZE_ASSIGN(assign_int_float, char, float)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed char, float)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed short, float)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed int, float)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed long, float)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed long long, float)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned char, float)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned short, float)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned int, float)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned long, float)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned long long, float)

PPL_SPECIALIZE_ASSIGN(assign_int_float, char, double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed char, double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed short, double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed int, double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed long, double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed long long, double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned char, double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned short, double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned int, double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned long, double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned long long, double)

PPL_SPECIALIZE_ASSIGN(assign_int_float, char, long double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed char, long double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed short, long double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed int, long double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed long, long double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, signed long long, long double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned char, long double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned short, long double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned int, long double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned long, long double)
PPL_SPECIALIZE_ASSIGN(assign_int_float, unsigned long long, long double)

#undef PPL_ASSIGN_SIGNED
#undef PPL_ASSIGN_UNSIGNED
#undef PPL_ASSIGN2_SIGNED_SIGNED
#undef PPL_ASSIGN2_UNSIGNED_UNSIGNED
#undef PPL_ASSIGN2_UNSIGNED_SIGNED
#undef PPL_ASSIGN2_SIGNED_UNSIGNED

template <typename To_Policy, typename From_Policy, typename To>
inline Result
assign_signed_int_mpz(To& to, const mpz_class& from, Rounding_Dir dir) {
  if (sizeof(To) <= sizeof(signed long)) {
    if (!To_Policy::check_overflow) {
      to = from.get_si();
      return V_EQ;
    }
    if (from.fits_slong_p()) {
      signed long v = from.get_si();
      if (PPL_LT_SILENT(v, (Extended_Int<To_Policy, To>::min))) {
        return set_neg_overflow_int<To_Policy>(to, dir);
      }
      if (PPL_GT_SILENT(v, (Extended_Int<To_Policy, To>::max))) {
        return set_pos_overflow_int<To_Policy>(to, dir);
      }
      to = v;
      return V_EQ;
    }
  }
  else {
    mpz_srcptr m = from.get_mpz_t();
    size_t sz = mpz_size(m);
    if (sz <= sizeof(To) / sizeof(mp_limb_t)) {
      if (sz == 0) {
        to = 0;
        return V_EQ;
      }
      To v;
      mpz_export(&v, 0, -1, sizeof(To), 0, 0, m);
      if (v >= 0) {
        if (::sgn(from) < 0) {
          return neg<To_Policy, To_Policy>(to, v, dir);
        }
        to = v;
        return V_EQ;
      }
    }
  }
  return (::sgn(from) < 0)
    ? set_neg_overflow_int<To_Policy>(to, dir)
    : set_pos_overflow_int<To_Policy>(to, dir);
}

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_ASSIGN(assign_signed_int_mpz, char, mpz_class)
#endif
PPL_SPECIALIZE_ASSIGN(assign_signed_int_mpz, signed char, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_signed_int_mpz, signed short, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_signed_int_mpz, signed int, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_signed_int_mpz, signed long, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_signed_int_mpz, signed long long, mpz_class)

template <typename To_Policy, typename From_Policy, typename To>
inline Result
assign_unsigned_int_mpz(To& to, const mpz_class& from, Rounding_Dir dir) {
  if (CHECK_P(To_Policy::check_overflow, ::sgn(from) < 0)) {
    return set_neg_overflow_int<To_Policy>(to, dir);
  }
  if (sizeof(To) <= sizeof(unsigned long)) {
    if (!To_Policy::check_overflow) {
      to = static_cast<To>(from.get_ui());
      return V_EQ;
    }
    if (from.fits_ulong_p()) {
      const unsigned long v = from.get_ui();
      if (PPL_GT_SILENT(v, (Extended_Int<To_Policy, To>::max))) {
        return set_pos_overflow_int<To_Policy>(to, dir);
      }
      to = static_cast<To>(v);
      return V_EQ;
    }
  }
  else {
    const mpz_srcptr m = from.get_mpz_t();
    const size_t sz = mpz_size(m);
    if (sz <= sizeof(To) / sizeof(mp_limb_t)) {
      if (sz == 0) {
        to = 0;
      }
      else {
        mpz_export(&to, 0, -1, sizeof(To), 0, 0, m);
      }
      return V_EQ;
    }
  }
  return set_pos_overflow_int<To_Policy>(to, dir);
}

#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_ASSIGN(assign_unsigned_int_mpz, char, mpz_class)
#endif
PPL_SPECIALIZE_ASSIGN(assign_unsigned_int_mpz, unsigned char, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_unsigned_int_mpz, unsigned short, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_unsigned_int_mpz, unsigned int, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_unsigned_int_mpz, unsigned long, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_unsigned_int_mpz, unsigned long long, mpz_class)

template <typename To_Policy, typename From_Policy, typename To>
inline Result
assign_int_mpq(To& to, const mpq_class& from, Rounding_Dir dir) {
  mpz_srcptr n = from.get_num().get_mpz_t();
  mpz_srcptr d = from.get_den().get_mpz_t();
  PPL_DIRTY_TEMP(mpz_class, q);
  mpz_ptr q_z = q.get_mpz_t();
  if (round_not_requested(dir)) {
    mpz_tdiv_q(q_z, n, d);
    Result r = assign<To_Policy, void>(to, q, dir);
    if (r != V_EQ) {
      return r;
    }
    return V_LGE;
  }
  mpz_t rem;
  int sign;
  mpz_init(rem);
  mpz_tdiv_qr(q_z, rem, n, d);
  sign = mpz_sgn(rem);
  mpz_clear(rem);
  Result r = assign<To_Policy, void>(to, q, dir);
  if (r != V_EQ) {
    return r;
  }
  switch (sign) {
  case -1:
    return round_lt_int<To_Policy>(to, dir);
  case 1:
    return round_gt_int<To_Policy>(to, dir);
  default:
    return V_EQ;
  }
}

PPL_SPECIALIZE_ASSIGN(assign_int_mpq, char, mpq_class)
PPL_SPECIALIZE_ASSIGN(assign_int_mpq, signed char, mpq_class)
PPL_SPECIALIZE_ASSIGN(assign_int_mpq, signed short, mpq_class)
PPL_SPECIALIZE_ASSIGN(assign_int_mpq, signed int, mpq_class)
PPL_SPECIALIZE_ASSIGN(assign_int_mpq, signed long, mpq_class)
PPL_SPECIALIZE_ASSIGN(assign_int_mpq, signed long long, mpq_class)
PPL_SPECIALIZE_ASSIGN(assign_int_mpq, unsigned char, mpq_class)
PPL_SPECIALIZE_ASSIGN(assign_int_mpq, unsigned short, mpq_class)
PPL_SPECIALIZE_ASSIGN(assign_int_mpq, unsigned int, mpq_class)
PPL_SPECIALIZE_ASSIGN(assign_int_mpq, unsigned long, mpq_class)
PPL_SPECIALIZE_ASSIGN(assign_int_mpq, unsigned long long, mpq_class)

#if ~0 != -1
#error "Only two's complement is supported"
#endif

#if UCHAR_MAX == 0xff
#define CHAR_BITS 8
#else
#error "Unexpected max for unsigned char"
#endif

#if USHRT_MAX == 0xffff
#define SHRT_BITS 16
#else
#error "Unexpected max for unsigned short"
#endif

#if UINT_MAX == 0xffffffff
#define INT_BITS 32
#else
#error "Unexpected max for unsigned int"
#endif

#if ULONG_MAX == 0xffffffffUL
#define LONG_BITS 32
#elif ULONG_MAX == 0xffffffffffffffffULL
#define LONG_BITS 64
#else
#error "Unexpected max for unsigned long"
#endif

#if ULLONG_MAX == 0xffffffffffffffffULL
#define LONG_LONG_BITS 64
#else
#error "Unexpected max for unsigned long long"
#endif


template <typename T>
struct Larger;

// The following may be tuned for performance on specific architectures.
//
// Current guidelines:
//   - avoid division where possible (larger type variant for mul)
//   - use larger type variant for types smaller than architecture bit size

template <>
struct Larger<char> {
  const_bool_nodef(use_for_neg, true);
  const_bool_nodef(use_for_add, true);
  const_bool_nodef(use_for_sub, true);
  const_bool_nodef(use_for_mul, true);
  typedef int_fast16_t type_for_neg;
  typedef int_fast16_t type_for_add;
  typedef int_fast16_t type_for_sub;
  typedef int_fast16_t type_for_mul;
};

template <>
struct Larger<signed char> {
  const_bool_nodef(use_for_neg, true);
  const_bool_nodef(use_for_add, true);
  const_bool_nodef(use_for_sub, true);
  const_bool_nodef(use_for_mul, true);
  typedef int_fast16_t type_for_neg;
  typedef int_fast16_t type_for_add;
  typedef int_fast16_t type_for_sub;
  typedef int_fast16_t type_for_mul;
};

template <>
struct Larger<unsigned char> {
  const_bool_nodef(use_for_neg, true);
  const_bool_nodef(use_for_add, true);
  const_bool_nodef(use_for_sub, true);
  const_bool_nodef(use_for_mul, true);
  typedef int_fast16_t type_for_neg;
  typedef uint_fast16_t type_for_add;
  typedef int_fast16_t type_for_sub;
  typedef uint_fast16_t type_for_mul;
};

template <>
struct Larger<signed short> {
  const_bool_nodef(use_for_neg, true);
  const_bool_nodef(use_for_add, true);
  const_bool_nodef(use_for_sub, true);
  const_bool_nodef(use_for_mul, true);
  typedef int_fast32_t type_for_neg;
  typedef int_fast32_t type_for_add;
  typedef int_fast32_t type_for_sub;
  typedef int_fast32_t type_for_mul;
};

template <>
struct Larger<unsigned short> {
  const_bool_nodef(use_for_neg, true);
  const_bool_nodef(use_for_add, true);
  const_bool_nodef(use_for_sub, true);
  const_bool_nodef(use_for_mul, true);
  typedef int_fast32_t type_for_neg;
  typedef uint_fast32_t type_for_add;
  typedef int_fast32_t type_for_sub;
  typedef uint_fast32_t type_for_mul;
};

template <>
struct Larger<signed int> {
  const_bool_nodef(use_for_neg, (LONG_BITS == 64));
  const_bool_nodef(use_for_add, (LONG_BITS == 64));
  const_bool_nodef(use_for_sub, (LONG_BITS == 64));
  const_bool_nodef(use_for_mul, true);
  typedef int_fast64_t type_for_neg;
  typedef int_fast64_t type_for_add;
  typedef int_fast64_t type_for_sub;
  typedef int_fast64_t type_for_mul;
};

template <>
struct Larger<unsigned int> {
  const_bool_nodef(use_for_neg, (LONG_BITS == 64));
  const_bool_nodef(use_for_add, (LONG_BITS == 64));
  const_bool_nodef(use_for_sub, (LONG_BITS == 64));
  const_bool_nodef(use_for_mul, true);
  typedef int_fast64_t type_for_neg;
  typedef uint_fast64_t type_for_add;
  typedef int_fast64_t type_for_sub;
  typedef uint_fast64_t type_for_mul;
};

template <>
struct Larger<signed long> {
  const_bool_nodef(use_for_neg, false);
  const_bool_nodef(use_for_add, false);
  const_bool_nodef(use_for_sub, false);
  const_bool_nodef(use_for_mul, (LONG_BITS == 32));
  typedef int_fast64_t type_for_neg;
  typedef int_fast64_t type_for_add;
  typedef int_fast64_t type_for_sub;
  typedef int_fast64_t type_for_mul;
};

template <>
struct Larger<unsigned long> {
  const_bool_nodef(use_for_neg, false);
  const_bool_nodef(use_for_add, false);
  const_bool_nodef(use_for_sub, false);
  const_bool_nodef(use_for_mul, (LONG_BITS == 32));
  typedef int_fast64_t type_for_neg;
  typedef uint_fast64_t type_for_add;
  typedef int_fast64_t type_for_sub;
  typedef uint_fast64_t type_for_mul;
};

template <>
struct Larger<signed long long> {
  const_bool_nodef(use_for_neg, false);
  const_bool_nodef(use_for_add, false);
  const_bool_nodef(use_for_sub, false);
  const_bool_nodef(use_for_mul, false);
  typedef int_fast64_t type_for_neg;
  typedef int_fast64_t type_for_add;
  typedef int_fast64_t type_for_sub;
  typedef int_fast64_t type_for_mul;
};

template <>
struct Larger<unsigned long long> {
  const_bool_nodef(use_for_neg, false);
  const_bool_nodef(use_for_add, false);
  const_bool_nodef(use_for_sub, false);
  const_bool_nodef(use_for_mul, false);
  typedef int_fast64_t type_for_neg;
  typedef uint_fast64_t type_for_add;
  typedef int_fast64_t type_for_sub;
  typedef uint_fast64_t type_for_mul;
};

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
neg_int_larger(Type& to, const Type x, Rounding_Dir dir) {
  typename Larger<Type>::type_for_neg l = x;
  l = -l;
  return assign<To_Policy, To_Policy>(to, l, dir);
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
add_int_larger(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  typename Larger<Type>::type_for_add l = x;
  l += y;
  return assign<To_Policy, To_Policy>(to, l, dir);
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
sub_int_larger(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  typename Larger<Type>::type_for_sub l = x;
  l -= y;
  return assign<To_Policy, To_Policy>(to, l, dir);
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
mul_int_larger(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  typename Larger<Type>::type_for_mul l = x;
  l *= y;
  return assign<To_Policy, To_Policy>(to, l, dir);
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
neg_signed_int(Type& to, const Type from, Rounding_Dir dir) {
  if (To_Policy::check_overflow && Larger<Type>::use_for_neg) {
    return neg_int_larger<To_Policy, From_Policy>(to, from, dir);
  }
  if (CHECK_P(To_Policy::check_overflow,
              (from < -Extended_Int<To_Policy, Type>::max))) {
    return set_pos_overflow_int<To_Policy>(to, dir);
  }
  to = -from;
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
neg_unsigned_int(Type& to, const Type from, Rounding_Dir dir) {
  if (To_Policy::check_overflow && Larger<Type>::use_for_neg) {
    return neg_int_larger<To_Policy, From_Policy>(to, from, dir);
  }
  if (CHECK_P(To_Policy::check_overflow, from != 0)) {
    return set_neg_overflow_int<To_Policy>(to, dir);
  }
  to = from;
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
add_signed_int(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_overflow && Larger<Type>::use_for_add) {
    return add_int_larger<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
  }
  if (To_Policy::check_overflow) {
    if (y >= 0) {
      if (x > Extended_Int<To_Policy, Type>::max - y) {
        return set_pos_overflow_int<To_Policy>(to, dir);
      }
    }
    else if (x < Extended_Int<To_Policy, Type>::min - y) {
      return set_neg_overflow_int<To_Policy>(to, dir);
    }
  }
  to = x + y;
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
add_unsigned_int(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_overflow && Larger<Type>::use_for_add) {
    return add_int_larger<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
  }
  if (CHECK_P(To_Policy::check_overflow,
              (x > Extended_Int<To_Policy, Type>::max - y))) {
    return set_pos_overflow_int<To_Policy>(to, dir);
  }
  to = x + y;
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
sub_signed_int(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_overflow && Larger<Type>::use_for_sub) {
    return sub_int_larger<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
  }
  if (To_Policy::check_overflow) {
    if (y >= 0) {
      if (x < Extended_Int<To_Policy, Type>::min + y) {
        return set_neg_overflow_int<To_Policy>(to, dir);
      }
    }
    else if (x > Extended_Int<To_Policy, Type>::max + y) {
      return set_pos_overflow_int<To_Policy>(to, dir);
    }
  }
  to = x - y;
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
sub_unsigned_int(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_overflow && Larger<Type>::use_for_sub) {
    return sub_int_larger<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
  }
  if (CHECK_P(To_Policy::check_overflow,
              (x < Extended_Int<To_Policy, Type>::min + y))) {
    return set_neg_overflow_int<To_Policy>(to, dir);
  }
  to = x - y;
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
mul_signed_int(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_overflow && Larger<Type>::use_for_mul) {
    return mul_int_larger<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
  }
  if (!To_Policy::check_overflow) {
    to = x * y;
    return V_EQ;
  }
  if (y == 0) {
    to = 0;
    return V_EQ;
  }
  if (y == -1) {
    return neg_signed_int<To_Policy, From1_Policy>(to, x, dir);
  }
  if (x >= 0) {
    if (y > 0) {
      if (x > Extended_Int<To_Policy, Type>::max / y) {
        return set_pos_overflow_int<To_Policy>(to, dir);
      }
    }
    else {
      if (x > Extended_Int<To_Policy, Type>::min / y) {
        return set_neg_overflow_int<To_Policy>(to, dir);
      }
    }
  }
  else {
    if (y < 0) {
      if (x < Extended_Int<To_Policy, Type>::max / y) {
        return set_pos_overflow_int<To_Policy>(to, dir);
      }
    }
    else {
      if (x < Extended_Int<To_Policy, Type>::min / y) {
        return set_neg_overflow_int<To_Policy>(to, dir);
      }
    }
  }
  to = x * y;
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
mul_unsigned_int(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_overflow && Larger<Type>::use_for_mul) {
    return mul_int_larger<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
  }
  if (!To_Policy::check_overflow) {
    to = x * y;
    return V_EQ;
  }
  if (y == 0) {
    to = 0;
    return V_EQ;
  }
  if (x > Extended_Int<To_Policy, Type>::max / y) {
    return set_pos_overflow_int<To_Policy>(to, dir);
  }
  to = x * y;
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
div_signed_int(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (CHECK_P(To_Policy::check_div_zero, y == 0)) {
    return assign_nan<To_Policy>(to, V_DIV_ZERO);
  }
  if (To_Policy::check_overflow && y == -1) {
    return neg_signed_int<To_Policy, From1_Policy>(to, x, dir);
  }
  to = x / y;
  if (round_not_requested(dir)) {
    return V_LGE;
  }
  if (y == -1) {
    return V_EQ;
  }
  Type m = x % y;
  if (m < 0) {
    return round_lt_int_no_overflow<To_Policy>(to, dir);
  }
  else if (m > 0) {
    return round_gt_int_no_overflow<To_Policy>(to, dir);
  }
  else {
    return V_EQ;
  }
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
div_unsigned_int(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (CHECK_P(To_Policy::check_div_zero, y == 0)) {
    return assign_nan<To_Policy>(to, V_DIV_ZERO);
  }
  to = x / y;
  if (round_not_requested(dir)) {
    return V_GE;
  }
  Type m = x % y;
  if (m == 0) {
    return V_EQ;
  }
  return round_gt_int<To_Policy>(to, dir);
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
idiv_signed_int(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (CHECK_P(To_Policy::check_div_zero, y == 0)) {
    return assign_nan<To_Policy>(to, V_DIV_ZERO);
  }
  if (To_Policy::check_overflow && y == -1) {
    return neg_signed_int<To_Policy, From1_Policy>(to, x, dir);
  }
  to = x / y;
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
idiv_unsigned_int(Type& to, const Type x, const Type y, Rounding_Dir) {
  if (CHECK_P(To_Policy::check_div_zero, y == 0)) {
    return assign_nan<To_Policy>(to, V_DIV_ZERO);
  }
  to = x / y;
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
rem_signed_int(Type& to, const Type x, const Type y, Rounding_Dir) {
  if (CHECK_P(To_Policy::check_div_zero, y == 0)) {
    return assign_nan<To_Policy>(to, V_MOD_ZERO);
  }
  to = (y == -1) ? 0 : (x % y);
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
rem_unsigned_int(Type& to, const Type x, const Type y, Rounding_Dir) {
  if (CHECK_P(To_Policy::check_div_zero, y == 0)) {
    return assign_nan<To_Policy>(to, V_MOD_ZERO);
  }
  to = x % y;
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
div_2exp_unsigned_int(Type& to, const Type x, unsigned int exp,
                      Rounding_Dir dir) {
  if (exp >= sizeof_to_bits(sizeof(Type))) {
    to = 0;
    if (round_not_requested(dir)) {
      return V_GE;
    }
    if (x == 0) {
      return V_EQ;
    }
    return round_gt_int_no_overflow<To_Policy>(to, dir);
  }
  to = x >> exp;
  if (round_not_requested(dir)) {
    return V_GE;
  }
  if (x & ((Type(1) << exp) - 1)) {
    return round_gt_int_no_overflow<To_Policy>(to, dir);
  }
  else {
    return V_EQ;
  }
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
div_2exp_signed_int(Type& to, const Type x, unsigned int exp,
                    Rounding_Dir dir) {
  if (x < 0) {
    if (exp >= sizeof_to_bits(sizeof(Type))) {
      to = 0;
      if (round_not_requested(dir)) {
        return V_LE;
      }
      return round_lt_int_no_overflow<To_Policy>(to, dir);
    }
    typedef typename C_Integer<Type>::other_type UType;
    UType ux = x;
    ux = -ux;
    to = ~Type(~-(ux >> exp));
    if (round_not_requested(dir)) {
      return V_LE;
    }
    if (ux & ((UType(1) << exp) -1)) {
      return round_lt_int_no_overflow<To_Policy>(to, dir);
    }
    return V_EQ;
  }
  else {
    if (exp >= sizeof_to_bits(sizeof(Type)) - 1) {
      to = 0;
      if (round_not_requested(dir)) {
        return V_GE;
      }
      if (x == 0) {
        return V_EQ;
      }
      return round_gt_int_no_overflow<To_Policy>(to, dir);
    }
    to = x >> exp;
    if (round_not_requested(dir)) {
      return V_GE;
    }
    if (x & ((Type(1) << exp) - 1)) {
      return round_gt_int_no_overflow<To_Policy>(to, dir);
    }
    else {
      return V_EQ;
    }
  }
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
add_2exp_unsigned_int(Type& to, const Type x, unsigned int exp,
                      Rounding_Dir dir) {
  if (!To_Policy::check_overflow) {
    to = x + (Type(1) << exp);
    return V_EQ;
  }
  if (exp >= sizeof_to_bits(sizeof(Type))) {
    return set_pos_overflow_int<To_Policy>(to, dir);
  }
  Type n = Type(1) << exp;
  return add_unsigned_int<To_Policy, From_Policy, void>(to, x, n, dir);
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
add_2exp_signed_int(Type& to, const Type x, unsigned int exp,
                    Rounding_Dir dir) {
  if (!To_Policy::check_overflow) {
    to = x + (Type(1) << exp);
    return V_EQ;
  }
  if (exp >= sizeof_to_bits(sizeof(Type))) {
    return set_pos_overflow_int<To_Policy>(to, dir);
  }
  if (exp == sizeof_to_bits(sizeof(Type)) - 1) {
    Type n = -2 * (Type(1) << (exp - 1));
    return sub_signed_int<To_Policy, From_Policy, void>(to, x, n, dir);
  }
  else {
    Type n = Type(1) << exp;
    return add_signed_int<To_Policy, From_Policy, void>(to, x, n, dir);
  }
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
sub_2exp_unsigned_int(Type& to, const Type x, unsigned int exp,
                      Rounding_Dir dir) {
  if (!To_Policy::check_overflow) {
    to = x - (Type(1) << exp);
    return V_EQ;
  }
  if (exp >= sizeof_to_bits(sizeof(Type))) {
    return set_neg_overflow_int<To_Policy>(to, dir);
  }
  Type n = Type(1) << exp;
  return sub_unsigned_int<To_Policy, From_Policy, void>(to, x, n, dir);
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
sub_2exp_signed_int(Type& to, const Type x, unsigned int exp,
                    Rounding_Dir dir) {
  if (!To_Policy::check_overflow) {
    to = x - (Type(1) << exp);
    return V_EQ;
  }
  if (exp >= sizeof_to_bits(sizeof(Type))) {
    return set_neg_overflow_int<To_Policy>(to, dir);
  }
  if (exp == sizeof_to_bits(sizeof(Type)) - 1) {
    Type n = -2 * (Type(1) << (exp - 1));
    return add_signed_int<To_Policy, From_Policy, void>(to, x, n, dir);
  }
  else {
    Type n = Type(1) << exp;
    return sub_signed_int<To_Policy, From_Policy, void>(to, x, n, dir);
  }
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
mul_2exp_unsigned_int(Type& to, const Type x, unsigned int exp,
                      Rounding_Dir dir) {
  if (!To_Policy::check_overflow) {
    to = x << exp;
    return V_EQ;
  }
  if (exp >= sizeof_to_bits(sizeof(Type))) {
    if (x == 0) {
      to = 0;
      return V_EQ;
    }
    return set_pos_overflow_int<To_Policy>(to, dir);
  }
  if (x > Extended_Int<To_Policy, Type>::max >> exp) {
    return set_pos_overflow_int<To_Policy>(to, dir);
  }
  to = x << exp;
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
mul_2exp_signed_int(Type& to, const Type x, unsigned int exp,
                    Rounding_Dir dir) {
  if (x < 0) {
    if (!To_Policy::check_overflow) {
      to = x * (Type(1) << exp);
      return V_EQ;
    }
    if (exp >= sizeof_to_bits(sizeof(Type))) {
      return set_neg_overflow_int<To_Policy>(to, dir);
    }
    typedef typename C_Integer<Type>::other_type UType;
    UType mask = UType(-1) << (sizeof_to_bits(sizeof(Type)) - exp - 1);
    UType ux = x;
    if ((ux & mask) != mask) {
      return set_neg_overflow_int<To_Policy>(to, dir);
    }
    ux <<= exp;
    Type n = ~(Type(~ux));
    if (PPL_LT_SILENT(n, (Extended_Int<To_Policy, Type>::min))) {
      return set_neg_overflow_int<To_Policy>(to, dir);
    }
    to = n;
  }
  else {
    if (!To_Policy::check_overflow) {
      to = x << exp;
      return V_EQ;
    }
    if (exp >= sizeof_to_bits(sizeof(Type)) - 1) {
      if (x == 0) {
        to = 0;
        return V_EQ;
      }
      return set_pos_overflow_int<To_Policy>(to, dir);
    }
    if (x > Extended_Int<To_Policy, Type>::max >> exp) {
      return set_pos_overflow_int<To_Policy>(to, dir);
    }
    to = x << exp;
  }
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
smod_2exp_unsigned_int(Type& to, const Type x, unsigned int exp,
                       Rounding_Dir dir) {
  if (exp > sizeof_to_bits(sizeof(Type))) {
    to = x;
  }
  else {
    Type v = (exp == sizeof_to_bits(sizeof(Type)) ? x : (x & ((Type(1) << exp) - 1)));
    if (v >= (Type(1) << (exp - 1))) {
      return set_neg_overflow_int<To_Policy>(to, dir);
    }
    else {
      to = v;
    }
  }
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
smod_2exp_signed_int(Type& to, const Type x, unsigned int exp,
                     Rounding_Dir) {
  if (exp >= sizeof_to_bits(sizeof(Type))) {
    to = x;
  }
  else {
    Type m = Type(1) << (exp - 1);
    to = (x & (m - 1)) - (x & m);
  }
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
umod_2exp_unsigned_int(Type& to, const Type x, unsigned int exp,
                       Rounding_Dir) {
  if (exp >= sizeof_to_bits(sizeof(Type))) {
    to = x;
  }
  else {
    to = x & ((Type(1) << exp) - 1);
  }
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
umod_2exp_signed_int(Type& to, const Type x, unsigned int exp,
                     Rounding_Dir dir) {
  if (exp >= sizeof_to_bits(sizeof(Type))) {
    if (x < 0) {
      return set_pos_overflow_int<To_Policy>(to, dir);
    }
    to = x;
  }
  else {
    to = x & ((Type(1) << exp) - 1);
  }
  return V_EQ;
}

template <typename Type>
inline void
isqrt_rem(Type& q, Type& r, const Type from) {
  q = 0;
  r = from;
  Type t(1);
  for (t <<= sizeof_to_bits(sizeof(Type)) - 2; t != 0; t >>= 2) {
    Type s = q + t;
    if (s <= r) {
      r -= s;
      q = s + t;
    }
    q >>= 1;
  }
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
sqrt_unsigned_int(Type& to, const Type from, Rounding_Dir dir) {
  Type rem;
  isqrt_rem(to, rem, from);
  if (round_not_requested(dir)) {
    return V_GE;
  }
  if (rem == 0) {
    return V_EQ;
  }
  return round_gt_int<To_Policy>(to, dir);
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
sqrt_signed_int(Type& to, const Type from, Rounding_Dir dir) {
  if (CHECK_P(To_Policy::check_sqrt_neg, from < 0)) {
    return assign_nan<To_Policy>(to, V_SQRT_NEG);
  }
  return sqrt_unsigned_int<To_Policy, From_Policy>(to, from, dir);
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
add_mul_int(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  Type z;
  Result r = mul<To_Policy, From1_Policy, From2_Policy>(z, x, y, dir);
  switch (result_overflow(r)) {
  case 0:
    return add<To_Policy, To_Policy, To_Policy>(to, to, z, dir);
  case -1:
    if (to <= 0) {
      return set_neg_overflow_int<To_Policy>(to, dir);
    }
    return assign_nan<To_Policy>(to, V_UNKNOWN_NEG_OVERFLOW);
  case 1:
    if (to >= 0) {
      return set_pos_overflow_int<To_Policy>(to, dir);
    }
    return assign_nan<To_Policy>(to, V_UNKNOWN_POS_OVERFLOW);
  default:
    PPL_UNREACHABLE;
    return V_NAN;
  }
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
sub_mul_int(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  Type z;
  Result r = mul<To_Policy, From1_Policy, From2_Policy>(z, x, y, dir);
  switch (result_overflow(r)) {
  case 0:
    return sub<To_Policy, To_Policy, To_Policy>(to, to, z, dir);
  case -1:
    if (to >= 0) {
      return set_pos_overflow_int<To_Policy>(to, dir);
    }
    return assign_nan<To_Policy>(to, V_UNKNOWN_NEG_OVERFLOW);
  case 1:
    if (to <= 0) {
      return set_neg_overflow_int<To_Policy>(to, dir);
    }
    return assign_nan<To_Policy>(to, V_UNKNOWN_POS_OVERFLOW);
  default:
    PPL_UNREACHABLE;
    return V_NAN;
  }
}

template <typename Policy, typename Type>
inline Result
output_char(std::ostream& os, Type& from,
            const Numeric_Format&, Rounding_Dir) {
  os << int(from);
  return V_EQ;
}

template <typename Policy, typename Type>
inline Result
output_int(std::ostream& os, Type& from, const Numeric_Format&, Rounding_Dir) {
  os << from;
  return V_EQ;
}

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_FLOOR(assign_signed_int_signed_int, char, char)
#endif
PPL_SPECIALIZE_FLOOR(assign_signed_int_signed_int, signed char, signed char)
PPL_SPECIALIZE_FLOOR(assign_signed_int_signed_int, signed short, signed short)
PPL_SPECIALIZE_FLOOR(assign_signed_int_signed_int, signed int, signed int)
PPL_SPECIALIZE_FLOOR(assign_signed_int_signed_int, signed long, signed long)
PPL_SPECIALIZE_FLOOR(assign_signed_int_signed_int, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_FLOOR(assign_unsigned_int_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_FLOOR(assign_unsigned_int_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_FLOOR(assign_unsigned_int_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_FLOOR(assign_unsigned_int_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_FLOOR(assign_unsigned_int_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_FLOOR(assign_unsigned_int_unsigned_int, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_CEIL(assign_signed_int_signed_int, char, char)
#endif
PPL_SPECIALIZE_CEIL(assign_signed_int_signed_int, signed char, signed char)
PPL_SPECIALIZE_CEIL(assign_signed_int_signed_int, signed short, signed short)
PPL_SPECIALIZE_CEIL(assign_signed_int_signed_int, signed int, signed int)
PPL_SPECIALIZE_CEIL(assign_signed_int_signed_int, signed long, signed long)
PPL_SPECIALIZE_CEIL(assign_signed_int_signed_int, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_CEIL(assign_unsigned_int_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_CEIL(assign_unsigned_int_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_CEIL(assign_unsigned_int_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_CEIL(assign_unsigned_int_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_CEIL(assign_unsigned_int_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_CEIL(assign_unsigned_int_unsigned_int, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_TRUNC(assign_signed_int_signed_int, char, char)
#endif
PPL_SPECIALIZE_TRUNC(assign_signed_int_signed_int, signed char, signed char)
PPL_SPECIALIZE_TRUNC(assign_signed_int_signed_int, signed short, signed short)
PPL_SPECIALIZE_TRUNC(assign_signed_int_signed_int, signed int, signed int)
PPL_SPECIALIZE_TRUNC(assign_signed_int_signed_int, signed long, signed long)
PPL_SPECIALIZE_TRUNC(assign_signed_int_signed_int, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_TRUNC(assign_unsigned_int_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_TRUNC(assign_unsigned_int_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_TRUNC(assign_unsigned_int_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_TRUNC(assign_unsigned_int_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_TRUNC(assign_unsigned_int_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_TRUNC(assign_unsigned_int_unsigned_int, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_NEG(neg_signed_int, char, char)
#endif
PPL_SPECIALIZE_NEG(neg_signed_int, signed char, signed char)
PPL_SPECIALIZE_NEG(neg_signed_int, signed short, signed short)
PPL_SPECIALIZE_NEG(neg_signed_int, signed int, signed int)
PPL_SPECIALIZE_NEG(neg_signed_int, signed long, signed long)
PPL_SPECIALIZE_NEG(neg_signed_int, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_NEG(neg_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_NEG(neg_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_NEG(neg_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_NEG(neg_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_NEG(neg_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_NEG(neg_unsigned_int, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_ADD(add_signed_int, char, char, char)
#endif
PPL_SPECIALIZE_ADD(add_signed_int, signed char, signed char, signed char)
PPL_SPECIALIZE_ADD(add_signed_int, signed short, signed short, signed short)
PPL_SPECIALIZE_ADD(add_signed_int, signed int, signed int, signed int)
PPL_SPECIALIZE_ADD(add_signed_int, signed long, signed long, signed long)
PPL_SPECIALIZE_ADD(add_signed_int, signed long long, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_ADD(add_unsigned_int, char, char, char)
#endif
PPL_SPECIALIZE_ADD(add_unsigned_int, unsigned char, unsigned char, unsigned char)
PPL_SPECIALIZE_ADD(add_unsigned_int, unsigned short, unsigned short, unsigned short)
PPL_SPECIALIZE_ADD(add_unsigned_int, unsigned int, unsigned int, unsigned int)
PPL_SPECIALIZE_ADD(add_unsigned_int, unsigned long, unsigned long, unsigned long)
PPL_SPECIALIZE_ADD(add_unsigned_int, unsigned long long, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_SUB(sub_signed_int, char, char, char)
#endif
PPL_SPECIALIZE_SUB(sub_signed_int, signed char, signed char, signed char)
PPL_SPECIALIZE_SUB(sub_signed_int, signed short, signed short, signed short)
PPL_SPECIALIZE_SUB(sub_signed_int, signed int, signed int, signed int)
PPL_SPECIALIZE_SUB(sub_signed_int, signed long, signed long, signed long)
PPL_SPECIALIZE_SUB(sub_signed_int, signed long long, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_SUB(sub_unsigned_int, char, char, char)
#endif
PPL_SPECIALIZE_SUB(sub_unsigned_int, unsigned char, unsigned char, unsigned char)
PPL_SPECIALIZE_SUB(sub_unsigned_int, unsigned short, unsigned short, unsigned short)
PPL_SPECIALIZE_SUB(sub_unsigned_int, unsigned int, unsigned int, unsigned int)
PPL_SPECIALIZE_SUB(sub_unsigned_int, unsigned long, unsigned long, unsigned long)
PPL_SPECIALIZE_SUB(sub_unsigned_int, unsigned long long, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_MUL(mul_signed_int, char, char, char)
#endif
PPL_SPECIALIZE_MUL(mul_signed_int, signed char, signed char, signed char)
PPL_SPECIALIZE_MUL(mul_signed_int, signed short, signed short, signed short)
PPL_SPECIALIZE_MUL(mul_signed_int, signed int, signed int, signed int)
PPL_SPECIALIZE_MUL(mul_signed_int, signed long, signed long, signed long)
PPL_SPECIALIZE_MUL(mul_signed_int, signed long long, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_MUL(mul_unsigned_int, char, char, char)
#endif
PPL_SPECIALIZE_MUL(mul_unsigned_int, unsigned char, unsigned char, unsigned char)
PPL_SPECIALIZE_MUL(mul_unsigned_int, unsigned short, unsigned short, unsigned short)
PPL_SPECIALIZE_MUL(mul_unsigned_int, unsigned int, unsigned int, unsigned int)
PPL_SPECIALIZE_MUL(mul_unsigned_int, unsigned long, unsigned long, unsigned long)
PPL_SPECIALIZE_MUL(mul_unsigned_int, unsigned long long, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_DIV(div_signed_int, char, char, char)
#endif
PPL_SPECIALIZE_DIV(div_signed_int, signed char, signed char, signed char)
PPL_SPECIALIZE_DIV(div_signed_int, signed short, signed short, signed short)
PPL_SPECIALIZE_DIV(div_signed_int, signed int, signed int, signed int)
PPL_SPECIALIZE_DIV(div_signed_int, signed long, signed long, signed long)
PPL_SPECIALIZE_DIV(div_signed_int, signed long long, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_DIV(div_unsigned_int, char, char, char)
#endif
PPL_SPECIALIZE_DIV(div_unsigned_int, unsigned char, unsigned char, unsigned char)
PPL_SPECIALIZE_DIV(div_unsigned_int, unsigned short, unsigned short, unsigned short)
PPL_SPECIALIZE_DIV(div_unsigned_int, unsigned int, unsigned int, unsigned int)
PPL_SPECIALIZE_DIV(div_unsigned_int, unsigned long, unsigned long, unsigned long)
PPL_SPECIALIZE_DIV(div_unsigned_int, unsigned long long, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_IDIV(idiv_signed_int, char, char, char)
#endif
PPL_SPECIALIZE_IDIV(idiv_signed_int, signed char, signed char, signed char)
PPL_SPECIALIZE_IDIV(idiv_signed_int, signed short, signed short, signed short)
PPL_SPECIALIZE_IDIV(idiv_signed_int, signed int, signed int, signed int)
PPL_SPECIALIZE_IDIV(idiv_signed_int, signed long, signed long, signed long)
PPL_SPECIALIZE_IDIV(idiv_signed_int, signed long long, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_IDIV(idiv_unsigned_int, char, char, char)
#endif
PPL_SPECIALIZE_IDIV(idiv_unsigned_int, unsigned char, unsigned char, unsigned char)
PPL_SPECIALIZE_IDIV(idiv_unsigned_int, unsigned short, unsigned short, unsigned short)
PPL_SPECIALIZE_IDIV(idiv_unsigned_int, unsigned int, unsigned int, unsigned int)
PPL_SPECIALIZE_IDIV(idiv_unsigned_int, unsigned long, unsigned long, unsigned long)
PPL_SPECIALIZE_IDIV(idiv_unsigned_int, unsigned long long, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_REM(rem_signed_int, char, char, char)
#endif
PPL_SPECIALIZE_REM(rem_signed_int, signed char, signed char, signed char)
PPL_SPECIALIZE_REM(rem_signed_int, signed short, signed short, signed short)
PPL_SPECIALIZE_REM(rem_signed_int, signed int, signed int, signed int)
PPL_SPECIALIZE_REM(rem_signed_int, signed long, signed long, signed long)
PPL_SPECIALIZE_REM(rem_signed_int, signed long long, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_REM(rem_unsigned_int, char, char, char)
#endif
PPL_SPECIALIZE_REM(rem_unsigned_int, unsigned char, unsigned char, unsigned char)
PPL_SPECIALIZE_REM(rem_unsigned_int, unsigned short, unsigned short, unsigned short)
PPL_SPECIALIZE_REM(rem_unsigned_int, unsigned int, unsigned int, unsigned int)
PPL_SPECIALIZE_REM(rem_unsigned_int, unsigned long, unsigned long, unsigned long)
PPL_SPECIALIZE_REM(rem_unsigned_int, unsigned long long, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_ADD_2EXP(add_2exp_signed_int, char, char)
#endif
PPL_SPECIALIZE_ADD_2EXP(add_2exp_signed_int, signed char, signed char)
PPL_SPECIALIZE_ADD_2EXP(add_2exp_signed_int, signed short, signed short)
PPL_SPECIALIZE_ADD_2EXP(add_2exp_signed_int, signed int, signed int)
PPL_SPECIALIZE_ADD_2EXP(add_2exp_signed_int, signed long, signed long)
PPL_SPECIALIZE_ADD_2EXP(add_2exp_signed_int, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_ADD_2EXP(add_2exp_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_ADD_2EXP(add_2exp_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_ADD_2EXP(add_2exp_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_ADD_2EXP(add_2exp_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_ADD_2EXP(add_2exp_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_ADD_2EXP(add_2exp_unsigned_int, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_signed_int, char, char)
#endif
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_signed_int, signed char, signed char)
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_signed_int, signed short, signed short)
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_signed_int, signed int, signed int)
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_signed_int, signed long, signed long)
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_signed_int, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_unsigned_int, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_signed_int, char, char)
#endif
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_signed_int, signed char, signed char)
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_signed_int, signed short, signed short)
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_signed_int, signed int, signed int)
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_signed_int, signed long, signed long)
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_signed_int, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_unsigned_int, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_DIV_2EXP(div_2exp_signed_int, char, char)
#endif
PPL_SPECIALIZE_DIV_2EXP(div_2exp_signed_int, signed char, signed char)
PPL_SPECIALIZE_DIV_2EXP(div_2exp_signed_int, signed short, signed short)
PPL_SPECIALIZE_DIV_2EXP(div_2exp_signed_int, signed int, signed int)
PPL_SPECIALIZE_DIV_2EXP(div_2exp_signed_int, signed long, signed long)
PPL_SPECIALIZE_DIV_2EXP(div_2exp_signed_int, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_DIV_2EXP(div_2exp_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_DIV_2EXP(div_2exp_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_DIV_2EXP(div_2exp_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_DIV_2EXP(div_2exp_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_DIV_2EXP(div_2exp_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_DIV_2EXP(div_2exp_unsigned_int, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_signed_int, char, char)
#endif
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_signed_int, signed char, signed char)
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_signed_int, signed short, signed short)
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_signed_int, signed int, signed int)
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_signed_int, signed long, signed long)
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_signed_int, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_unsigned_int, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_signed_int, char, char)
#endif
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_signed_int, signed char, signed char)
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_signed_int, signed short, signed short)
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_signed_int, signed int, signed int)
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_signed_int, signed long, signed long)
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_signed_int, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_unsigned_int, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_SQRT(sqrt_signed_int, char, char)
#endif
PPL_SPECIALIZE_SQRT(sqrt_signed_int, signed char, signed char)
PPL_SPECIALIZE_SQRT(sqrt_signed_int, signed short, signed short)
PPL_SPECIALIZE_SQRT(sqrt_signed_int, signed int, signed int)
PPL_SPECIALIZE_SQRT(sqrt_signed_int, signed long, signed long)
PPL_SPECIALIZE_SQRT(sqrt_signed_int, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_SQRT(sqrt_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_SQRT(sqrt_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_SQRT(sqrt_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_SQRT(sqrt_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_SQRT(sqrt_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_SQRT(sqrt_unsigned_int, unsigned long long, unsigned long long)

#if PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_ABS(abs_generic, char, char)
#endif
PPL_SPECIALIZE_ABS(abs_generic, signed char, signed char)
PPL_SPECIALIZE_ABS(abs_generic, signed short, signed short)
PPL_SPECIALIZE_ABS(abs_generic, signed int, signed int)
PPL_SPECIALIZE_ABS(abs_generic, signed long, signed long)
PPL_SPECIALIZE_ABS(abs_generic, signed long long, signed long long)
#if !PPL_CXX_PLAIN_CHAR_IS_SIGNED
PPL_SPECIALIZE_ABS(assign_unsigned_int_unsigned_int, char, char)
#endif
PPL_SPECIALIZE_ABS(assign_unsigned_int_unsigned_int, unsigned char, unsigned char)
PPL_SPECIALIZE_ABS(assign_unsigned_int_unsigned_int, unsigned short, unsigned short)
PPL_SPECIALIZE_ABS(assign_unsigned_int_unsigned_int, unsigned int, unsigned int)
PPL_SPECIALIZE_ABS(assign_unsigned_int_unsigned_int, unsigned long, unsigned long)
PPL_SPECIALIZE_ABS(assign_unsigned_int_unsigned_int, unsigned long long, unsigned long long)

PPL_SPECIALIZE_GCD(gcd_exact, char, char, char)
PPL_SPECIALIZE_GCD(gcd_exact, signed char, signed char, signed char)
PPL_SPECIALIZE_GCD(gcd_exact, signed short, signed short, signed short)
PPL_SPECIALIZE_GCD(gcd_exact, signed int, signed int, signed int)
PPL_SPECIALIZE_GCD(gcd_exact, signed long, signed long, signed long)
PPL_SPECIALIZE_GCD(gcd_exact, signed long long, signed long long, signed long long)
PPL_SPECIALIZE_GCD(gcd_exact, unsigned char, unsigned char, unsigned char)
PPL_SPECIALIZE_GCD(gcd_exact, unsigned short, unsigned short, unsigned short)
PPL_SPECIALIZE_GCD(gcd_exact, unsigned int, unsigned int, unsigned int)
PPL_SPECIALIZE_GCD(gcd_exact, unsigned long, unsigned long, unsigned long)
PPL_SPECIALIZE_GCD(gcd_exact, unsigned long long, unsigned long long, unsigned long long)

PPL_SPECIALIZE_GCDEXT(gcdext_exact,
                      char, char, char, char, char)
PPL_SPECIALIZE_GCDEXT(gcdext_exact,
                      signed char, signed char, signed char,
                      signed char, signed char)
PPL_SPECIALIZE_GCDEXT(gcdext_exact,
                      signed short, signed short, signed short,
                      signed short, signed short)
PPL_SPECIALIZE_GCDEXT(gcdext_exact,
                      signed int, signed int, signed int,
                      signed int, signed int)
PPL_SPECIALIZE_GCDEXT(gcdext_exact,
                      signed long, signed long, signed long,
                      signed long, signed long)
PPL_SPECIALIZE_GCDEXT(gcdext_exact,
                      signed long long, signed long long, signed long long,
                      signed long long, signed long long)
PPL_SPECIALIZE_GCDEXT(gcdext_exact,
                      unsigned char, unsigned char, unsigned char,
                      unsigned char, unsigned char)
PPL_SPECIALIZE_GCDEXT(gcdext_exact,
                      unsigned short, unsigned short, unsigned short,
                      unsigned short, unsigned short)
PPL_SPECIALIZE_GCDEXT(gcdext_exact,
                      unsigned int, unsigned int, unsigned int,
                      unsigned int, unsigned int)
PPL_SPECIALIZE_GCDEXT(gcdext_exact,
                      unsigned long, unsigned long, unsigned long,
                      unsigned long, unsigned long)
PPL_SPECIALIZE_GCDEXT(gcdext_exact,
                      unsigned long long, unsigned long long,
                      unsigned long long, unsigned long long,
                      unsigned long long)

PPL_SPECIALIZE_LCM(lcm_gcd_exact, char, char, char)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, signed char, signed char, signed char)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, signed short, signed short, signed short)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, signed int, signed int, signed int)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, signed long, signed long, signed long)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, signed long long, signed long long, signed long long)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, unsigned char, unsigned char, unsigned char)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, unsigned short, unsigned short, unsigned short)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, unsigned int, unsigned int, unsigned int)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, unsigned long, unsigned long, unsigned long)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, unsigned long long, unsigned long long, unsigned long long)

PPL_SPECIALIZE_SGN(sgn_generic, char)
PPL_SPECIALIZE_SGN(sgn_generic, signed char)
PPL_SPECIALIZE_SGN(sgn_generic, signed short)
PPL_SPECIALIZE_SGN(sgn_generic, signed int)
PPL_SPECIALIZE_SGN(sgn_generic, signed long)
PPL_SPECIALIZE_SGN(sgn_generic, signed long long)
PPL_SPECIALIZE_SGN(sgn_generic, unsigned char)
PPL_SPECIALIZE_SGN(sgn_generic, unsigned short)
PPL_SPECIALIZE_SGN(sgn_generic, unsigned int)
PPL_SPECIALIZE_SGN(sgn_generic, unsigned long)
PPL_SPECIALIZE_SGN(sgn_generic, unsigned long long)

PPL_SPECIALIZE_CMP(cmp_generic, char, char)
PPL_SPECIALIZE_CMP(cmp_generic, signed char, signed char)
PPL_SPECIALIZE_CMP(cmp_generic, signed short, signed short)
PPL_SPECIALIZE_CMP(cmp_generic, signed int, signed int)
PPL_SPECIALIZE_CMP(cmp_generic, signed long, signed long)
PPL_SPECIALIZE_CMP(cmp_generic, signed long long, signed long long)
PPL_SPECIALIZE_CMP(cmp_generic, unsigned char, unsigned char)
PPL_SPECIALIZE_CMP(cmp_generic, unsigned short, unsigned short)
PPL_SPECIALIZE_CMP(cmp_generic, unsigned int, unsigned int)
PPL_SPECIALIZE_CMP(cmp_generic, unsigned long, unsigned long)
PPL_SPECIALIZE_CMP(cmp_generic, unsigned long long, unsigned long long)

PPL_SPECIALIZE_ADD_MUL(add_mul_int, char, char, char)
PPL_SPECIALIZE_ADD_MUL(add_mul_int, signed char, signed char, signed char)
PPL_SPECIALIZE_ADD_MUL(add_mul_int, signed short, signed short, signed short)
PPL_SPECIALIZE_ADD_MUL(add_mul_int, signed int, signed int, signed int)
PPL_SPECIALIZE_ADD_MUL(add_mul_int, signed long, signed long, signed long)
PPL_SPECIALIZE_ADD_MUL(add_mul_int, signed long long, signed long long, signed long long)
PPL_SPECIALIZE_ADD_MUL(add_mul_int, unsigned char, unsigned char, unsigned char)
PPL_SPECIALIZE_ADD_MUL(add_mul_int, unsigned short, unsigned short, unsigned short)
PPL_SPECIALIZE_ADD_MUL(add_mul_int, unsigned int, unsigned int, unsigned int)
PPL_SPECIALIZE_ADD_MUL(add_mul_int, unsigned long, unsigned long, unsigned long)
PPL_SPECIALIZE_ADD_MUL(add_mul_int, unsigned long long, unsigned long long, unsigned long long)

PPL_SPECIALIZE_SUB_MUL(sub_mul_int, char, char, char)
PPL_SPECIALIZE_SUB_MUL(sub_mul_int, signed char, signed char, signed char)
PPL_SPECIALIZE_SUB_MUL(sub_mul_int, signed short, signed short, signed short)
PPL_SPECIALIZE_SUB_MUL(sub_mul_int, signed int, signed int, signed int)
PPL_SPECIALIZE_SUB_MUL(sub_mul_int, signed long, signed long, signed long)
PPL_SPECIALIZE_SUB_MUL(sub_mul_int, signed long long, signed long long, signed long long)
PPL_SPECIALIZE_SUB_MUL(sub_mul_int, unsigned char, unsigned char, unsigned char)
PPL_SPECIALIZE_SUB_MUL(sub_mul_int, unsigned short, unsigned short, unsigned short)
PPL_SPECIALIZE_SUB_MUL(sub_mul_int, unsigned int, unsigned int, unsigned int)
PPL_SPECIALIZE_SUB_MUL(sub_mul_int, unsigned long, unsigned long, unsigned long)
PPL_SPECIALIZE_SUB_MUL(sub_mul_int, unsigned long long, unsigned long long, unsigned long long)

PPL_SPECIALIZE_INPUT(input_generic, char)
PPL_SPECIALIZE_INPUT(input_generic, signed char)
PPL_SPECIALIZE_INPUT(input_generic, signed short)
PPL_SPECIALIZE_INPUT(input_generic, signed int)
PPL_SPECIALIZE_INPUT(input_generic, signed long)
PPL_SPECIALIZE_INPUT(input_generic, signed long long)
PPL_SPECIALIZE_INPUT(input_generic, unsigned char)
PPL_SPECIALIZE_INPUT(input_generic, unsigned short)
PPL_SPECIALIZE_INPUT(input_generic, unsigned int)
PPL_SPECIALIZE_INPUT(input_generic, unsigned long)
PPL_SPECIALIZE_INPUT(input_generic, unsigned long long)

PPL_SPECIALIZE_OUTPUT(output_char, char)
PPL_SPECIALIZE_OUTPUT(output_char, signed char)
PPL_SPECIALIZE_OUTPUT(output_int, signed short)
PPL_SPECIALIZE_OUTPUT(output_int, signed int)
PPL_SPECIALIZE_OUTPUT(output_int, signed long)
PPL_SPECIALIZE_OUTPUT(output_int, signed long long)
PPL_SPECIALIZE_OUTPUT(output_char, unsigned char)
PPL_SPECIALIZE_OUTPUT(output_int, unsigned short)
PPL_SPECIALIZE_OUTPUT(output_int, unsigned int)
PPL_SPECIALIZE_OUTPUT(output_int, unsigned long)
PPL_SPECIALIZE_OUTPUT(output_int, unsigned long long)

} // namespace Checked

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/checked_float_inlines.hh line 1. */
/* Specialized "checked" functions for native floating-point numbers.
*/


/* Automatically generated from PPL source file ../src/checked_float_inlines.hh line 28. */
#include <cmath>

namespace Parma_Polyhedra_Library {

namespace Checked {

inline float
multiply_add(float x, float y, float z) {
#if PPL_HAVE_DECL_FMAF && defined(FP_FAST_FMAF) \
  && !defined(__alpha) && !defined(__FreeBSD__)
  return fmaf(x, y, z);
#else
  return x*y + z;
#endif
}

inline double
multiply_add(double x, double y, double z) {
#if PPL_HAVE_DECL_FMA && defined(FP_FAST_FMA) \
  && !defined(__alpha) && !defined(__FreeBSD__)
  return fma(x, y, z);
#else
  return x*y + z;
#endif
}

inline long double
multiply_add(long double x, long double y, long double z) {
#if PPL_HAVE_DECL_FMAL && defined(FP_FAST_FMAL) \
  && !defined(__alpha) && !defined(__FreeBSD__)
  return fmal(x, y, z);
#else
  return x*y + z;
#endif
}

#if PPL_HAVE_DECL_RINTF
inline float
round_to_integer(float x) {
  return rintf(x);
}
#endif

inline double
round_to_integer(double x) {
  return rint(x);
}

#if PPL_HAVE_DECL_RINTL
inline long double
round_to_integer(long double x) {
  return rintl(x);
}
#elif !PPL_CXX_PROVIDES_PROPER_LONG_DOUBLE
// If proper long doubles are not provided, this is most likely
// because long double and double are the same type: use rint().
inline long double
round_to_integer(long double x) {
  return rint(x);
}
#elif defined(__i386__) && (defined(__GNUC__) || defined(__INTEL_COMPILER))
// On Cygwin, we have proper long doubles but rintl() is not defined:
// luckily, one machine instruction is enough to save the day.
inline long double
round_to_integer(long double x) {
  long double i;
  __asm__ ("frndint" : "=t" (i) : "0" (x));
  return i;
}
#endif

inline bool
fpu_direct_rounding(Rounding_Dir dir) {
  return round_direct(dir) || round_not_requested(dir);
}

inline bool
fpu_inverse_rounding(Rounding_Dir dir) {
  return round_inverse(dir);
}

// The FPU mode is "round down".
//
// The result of the rounded down multiplication is thus computed directly.
//
//   a = 0.3
//   b = 0.1
//   c_i = a * b = 0.03
//   c = c_i = 0.0
//
// To obtain the result of the rounded up multiplication
// we do -(-a * b).
//
//   a = 0.3
//   b = 0.1
//   c_i = -a * b = -0.03
//
// Here c_i should be forced to lose excess precision, otherwise the
// FPU will truncate using the rounding mode in force, which is "round down".
//
//   c_i = -c_i = 0.03
//   c = c_i = 0.0
//
// Wrong result: we should have obtained c = 0.1.

inline void
limit_precision(const float& v) {
  PPL_CC_FLUSH(v);
}

inline void
limit_precision(const double& v) {
  PPL_CC_FLUSH(v);
}

inline void
limit_precision(const long double&) {
}

template <typename Policy, typename T>
inline Result
classify_float(const T v, bool nan, bool inf, bool sign) {
  Float<T> f(v);
  if ((nan || sign) && CHECK_P(Policy::has_nan, f.u.binary.is_nan())) {
    return V_NAN;
  }
  if (inf) {
    if (Policy::has_infinity) {
      int sign_inf = f.u.binary.inf_sign();
      if (sign_inf < 0) {
        return V_EQ_MINUS_INFINITY;
      }
      if (sign_inf > 0) {
        return V_EQ_PLUS_INFINITY;
      }
    }
    else {
      PPL_ASSERT(f.u.binary.inf_sign() == 0);
    }
  }
  if (sign) {
    if (v < 0) {
      return V_LT;
    }
    if (v > 0) {
      return V_GT;
    }
    return V_EQ;
  }
  return V_LGE;
}

template <typename Policy, typename T>
inline bool
is_nan_float(const T v) {
  Float<T> f(v);
  return CHECK_P(Policy::has_nan, f.u.binary.is_nan());
}

template <typename Policy, typename T>
inline bool
is_inf_float(const T v) {
  Float<T> f(v);
  return CHECK_P(Policy::has_infinity, (f.u.binary.inf_sign() != 0));
}
template <typename Policy, typename T>
inline bool
is_minf_float(const T v) {
  Float<T> f(v);
  return CHECK_P(Policy::has_infinity, (f.u.binary.inf_sign() < 0));
}

template <typename Policy, typename T>
inline bool
is_pinf_float(const T v) {
  Float<T> f(v);
  return CHECK_P(Policy::has_infinity, (f.u.binary.inf_sign() > 0));
}


template <typename Policy, typename T>
inline bool
is_int_float(const T v) {
  return round_to_integer(v) == v;
}

template <typename Policy, typename T>
inline Result
assign_special_float(T& v, Result_Class c, Rounding_Dir) {
  PPL_ASSERT(c == VC_MINUS_INFINITY || c == VC_PLUS_INFINITY || c == VC_NAN);
  switch (c) {
  case VC_MINUS_INFINITY:
    v = -HUGE_VAL;
    return V_EQ_MINUS_INFINITY;
  case VC_PLUS_INFINITY:
    v = HUGE_VAL;
    return V_EQ_PLUS_INFINITY;
  case VC_NAN:
    v = PPL_NAN;
    return V_NAN;
  default:
    PPL_UNREACHABLE;
    return V_NAN | V_UNREPRESENTABLE;
  }
}

template <typename T>
inline void
pred_float(T& v) {
  Float<T> f(v);
  PPL_ASSERT(!f.u.binary.is_nan());
  PPL_ASSERT(f.u.binary.inf_sign() >= 0);
  if (f.u.binary.zero_sign() > 0) {
    f.u.binary.negate();
    f.u.binary.inc();
  }
  else if (f.u.binary.sign_bit()) {
    f.u.binary.inc();
  }
  else {
    f.u.binary.dec();
  }
  v = f.value();
}

template <typename T>
inline void
succ_float(T& v) {
  Float<T> f(v);
  PPL_ASSERT(!f.u.binary.is_nan());
  PPL_ASSERT(f.u.binary.inf_sign() <= 0);
  if (f.u.binary.zero_sign() < 0) {
    f.u.binary.negate();
    f.u.binary.inc();
  }
  else if (!f.u.binary.sign_bit()) {
    f.u.binary.inc();
  }
  else {
    f.u.binary.dec();
  }
  v = f.value();
}

template <typename Policy, typename To>
inline Result
round_lt_float(To& to, Rounding_Dir dir) {
  if (round_down(dir)) {
    pred_float(to);
    return V_GT;
  }
  return V_LT;
}

template <typename Policy, typename To>
inline Result
round_gt_float(To& to, Rounding_Dir dir) {
  if (round_up(dir)) {
    succ_float(to);
    return V_LT;
  }
  return V_GT;
}


template <typename Policy>
inline void
prepare_inexact(Rounding_Dir dir) {
  if (Policy::fpu_check_inexact
      && !round_not_needed(dir) && round_strict_relation(dir)) {
    fpu_reset_inexact();
  }
}

template <typename Policy>
inline Result
result_relation(Rounding_Dir dir) {
  if (Policy::fpu_check_inexact
      && !round_not_needed(dir) && round_strict_relation(dir)) {
    switch (fpu_check_inexact()) {
    case 0:
      return V_EQ;
    case -1:
      goto unknown;
    case 1:
      break;
    }
    switch (round_dir(dir)) {
    case ROUND_DOWN:
      return V_GT;
    case ROUND_UP:
      return V_LT;
    default:
      return V_NE;
    }
  }
  else {
  unknown:
    switch (round_dir(dir)) {
    case ROUND_DOWN:
      return V_GE;
    case ROUND_UP:
      return V_LE;
    default:
      return V_LGE;
    }
  }
}

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
assign_float_float_exact(To& to, const From from, Rounding_Dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  to = from;
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
assign_float_float_inexact(To& to, const From from, Rounding_Dir dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  prepare_inexact<To_Policy>(dir);
  if (fpu_direct_rounding(dir)) {
    to = from;
  }
  else if (fpu_inverse_rounding(dir)) {
    From tmp = -from;
    to = tmp;
    limit_precision(to);
    to = -to;
  }
  else {
    fpu_rounding_control_word_type old
      = fpu_save_rounding_direction(round_fpu_dir(dir));
    limit_precision(from);
    to = from;
    limit_precision(to);
    fpu_restore_rounding_direction(old);
  }
  return result_relation<To_Policy>(dir);
}

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
assign_float_float(To& to, const From from, Rounding_Dir dir) {
  if (sizeof(From) > sizeof(To)) {
    return assign_float_float_inexact<To_Policy, From_Policy>(to, from, dir);
  }
  else {
    return assign_float_float_exact<To_Policy, From_Policy>(to, from, dir);
  }
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
floor_float(Type& to, const Type from, Rounding_Dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  if (fpu_direct_rounding(ROUND_DOWN)) {
    to = round_to_integer(from);
  }
  else if (fpu_inverse_rounding(ROUND_DOWN)) {
    to = round_to_integer(-from);
    limit_precision(to);
    to = -to;
  }
  else {
    fpu_rounding_control_word_type old
      = fpu_save_rounding_direction(round_fpu_dir(ROUND_DOWN));
    limit_precision(from);
    to = round_to_integer(from);
    limit_precision(to);
    fpu_restore_rounding_direction(old);
  }
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
ceil_float(Type& to, const Type from, Rounding_Dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  if (fpu_direct_rounding(ROUND_UP)) {
    to = round_to_integer(from);
  }
  else if (fpu_inverse_rounding(ROUND_UP)) {
    to = round_to_integer(-from);
    limit_precision(to);
    to = -to;
  }
  else {
    fpu_rounding_control_word_type old
      = fpu_save_rounding_direction(round_fpu_dir(ROUND_UP));
    limit_precision(from);
    to = round_to_integer(from);
    limit_precision(to);
    fpu_restore_rounding_direction(old);
  }
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
trunc_float(Type& to, const Type from, Rounding_Dir dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  if (from >= 0) {
    return floor<To_Policy, From_Policy>(to, from, dir);
  }
  else {
    return ceil<To_Policy, From_Policy>(to, from, dir);
  }
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
neg_float(Type& to, const Type from, Rounding_Dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  to = -from;
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
add_float(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_inf_add_inf
      && is_inf_float<From1_Policy>(x) && x == -y) {
    return assign_nan<To_Policy>(to, V_INF_ADD_INF);
  }
  prepare_inexact<To_Policy>(dir);
  if (fpu_direct_rounding(dir)) {
    to = x + y;
  }
  else if (fpu_inverse_rounding(dir)) {
    to = -x - y;
    limit_precision(to);
    to = -to;
  }
  else {
    fpu_rounding_control_word_type old
      = fpu_save_rounding_direction(round_fpu_dir(dir));
    limit_precision(x);
    limit_precision(y);
    to = x + y;
    limit_precision(to);
    fpu_restore_rounding_direction(old);
  }
  if (To_Policy::fpu_check_nan_result && is_nan<To_Policy>(to)) {
    return V_NAN;
  }
  return result_relation<To_Policy>(dir);
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
sub_float(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_inf_sub_inf
      && is_inf_float<From1_Policy>(x) && x == y) {
    return assign_nan<To_Policy>(to, V_INF_SUB_INF);
  }
  prepare_inexact<To_Policy>(dir);
  if (fpu_direct_rounding(dir)) {
    to = x - y;
  }
  else if (fpu_inverse_rounding(dir)) {
    to = y - x;
    limit_precision(to);
    to = -to;
  }
  else {
    fpu_rounding_control_word_type old
      = fpu_save_rounding_direction(round_fpu_dir(dir));
    limit_precision(x);
    limit_precision(y);
    to = x - y;
    limit_precision(to);
    fpu_restore_rounding_direction(old);
  }
  if (To_Policy::fpu_check_nan_result && is_nan<To_Policy>(to)) {
    return V_NAN;
  }
  return result_relation<To_Policy>(dir);
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
mul_float(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_inf_mul_zero
      && ((x == 0 && is_inf_float<From2_Policy>(y))
          ||
          (y == 0 && is_inf_float<From1_Policy>(x)))) {
    return assign_nan<To_Policy>(to, V_INF_MUL_ZERO);
  }
  prepare_inexact<To_Policy>(dir);
  if (fpu_direct_rounding(dir)) {
    to = x * y;
  }
  else if (fpu_inverse_rounding(dir)) {
    to = x * -y;
    limit_precision(to);
    to = -to;
  }
  else {
    fpu_rounding_control_word_type old
      = fpu_save_rounding_direction(round_fpu_dir(dir));
    limit_precision(x);
    limit_precision(y);
    to = x * y;
    limit_precision(to);
    fpu_restore_rounding_direction(old);
  }
  if (To_Policy::fpu_check_nan_result && is_nan<To_Policy>(to)) {
    return V_NAN;
  }
  return result_relation<To_Policy>(dir);
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
div_float(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_inf_div_inf
      && is_inf_float<From1_Policy>(x) && is_inf_float<From2_Policy>(y)) {
    return assign_nan<To_Policy>(to, V_INF_DIV_INF);
  }
  if (To_Policy::check_div_zero && y == 0) {
    return assign_nan<To_Policy>(to, V_DIV_ZERO);
  }
  prepare_inexact<To_Policy>(dir);
  if (fpu_direct_rounding(dir)) {
    to = x / y;
  }
  else if (fpu_inverse_rounding(dir)) {
    to = x / -y;
    limit_precision(to);
    to = -to;
  }
  else {
    fpu_rounding_control_word_type old
      = fpu_save_rounding_direction(round_fpu_dir(dir));
    limit_precision(x);
    limit_precision(y);
    to = x / y;
    limit_precision(to);
    fpu_restore_rounding_direction(old);
  }
  if (To_Policy::fpu_check_nan_result && is_nan<To_Policy>(to)) {
    return V_NAN;
  }
  return result_relation<To_Policy>(dir);
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
idiv_float(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  Type temp;
  // The inexact check is useless
  dir = round_dir(dir);
  Result r = div<To_Policy, From1_Policy, From2_Policy>(temp, x, y, dir);
  if (result_class(r) != VC_NORMAL) {
    to = temp;
    return r;
  }
  Result r1 = trunc<To_Policy, To_Policy>(to, temp, ROUND_NOT_NEEDED);
  PPL_ASSERT(r1 == V_EQ);
  if (r == V_EQ || to != temp) {
    return r1;
  }
  // FIXME: Prove that it is impossible to return a strict relation
  return (dir == ROUND_UP) ? V_LE : V_GE;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
rem_float(Type& to, const Type x, const Type y, Rounding_Dir) {
  if (To_Policy::check_inf_mod && is_inf_float<From1_Policy>(x)) {
    return assign_nan<To_Policy>(to, V_INF_MOD);
  }
  if (To_Policy::check_div_zero && y == 0) {
    return assign_nan<To_Policy>(to, V_MOD_ZERO);
  }
  to = std::fmod(x, y);
  if (To_Policy::fpu_check_nan_result && is_nan<To_Policy>(to)) {
    return V_NAN;
  }
  return V_EQ;
}

struct Float_2exp {
  const_bool_nodef(has_nan, false);
  const_bool_nodef(has_infinity, false);
};

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
add_2exp_float(Type& to, const Type x, unsigned int exp, Rounding_Dir dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  PPL_ASSERT(exp < sizeof_to_bits(sizeof(unsigned long long)));
  return
    add<To_Policy, From_Policy, Float_2exp>(to,
                                            x,
                                            Type(1ULL << exp),
                                            dir);
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
sub_2exp_float(Type& to, const Type x, unsigned int exp, Rounding_Dir dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  PPL_ASSERT(exp < sizeof_to_bits(sizeof(unsigned long long)));
  return
    sub<To_Policy, From_Policy, Float_2exp>(to,
                                            x,
                                            Type(1ULL << exp),
                                            dir);
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
mul_2exp_float(Type& to, const Type x, unsigned int exp, Rounding_Dir dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  PPL_ASSERT(exp < sizeof_to_bits(sizeof(unsigned long long)));
  return
    mul<To_Policy, From_Policy, Float_2exp>(to,
                                            x,
                                            Type(1ULL << exp),
                                            dir);
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
div_2exp_float(Type& to, const Type x, unsigned int exp, Rounding_Dir dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  PPL_ASSERT(exp < sizeof_to_bits(sizeof(unsigned long long)));
  return
    div<To_Policy, From_Policy, Float_2exp>(to,
                                            x,
                                            Type(1ULL << exp),
                                            dir);
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
smod_2exp_float(Type& to, const Type x, unsigned int exp, Rounding_Dir dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  if (To_Policy::check_inf_mod && is_inf_float<From_Policy>(x)) {
    return assign_nan<To_Policy>(to, V_INF_MOD);
  }
  PPL_ASSERT(exp < sizeof_to_bits(sizeof(unsigned long long)));
  Type m = 1ULL << exp;
  rem_float<To_Policy, From_Policy, Float_2exp>(to, x, m, ROUND_IGNORE);
  Type m2 = m / 2;
  if (to < -m2) {
    return add_float<To_Policy, From_Policy, Float_2exp>(to, to, m, dir);
  }
  else if (to >= m2) {
    return sub_float<To_Policy, From_Policy, Float_2exp>(to, to, m, dir);
  }
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
umod_2exp_float(Type& to, const Type x, unsigned int exp, Rounding_Dir dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  if (To_Policy::check_inf_mod && is_inf_float<From_Policy>(x)) {
    return assign_nan<To_Policy>(to, V_INF_MOD);
  }
  PPL_ASSERT(exp < sizeof_to_bits(sizeof(unsigned long long)));
  Type m = 1ULL << exp;
  rem_float<To_Policy, From_Policy, Float_2exp>(to, x, m, ROUND_IGNORE);
  if (to < 0) {
    return add_float<To_Policy, From_Policy, Float_2exp>(to, to, m, dir);
  }
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
abs_float(Type& to, const Type from, Rounding_Dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  to = std::abs(from);
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename Type>
inline Result
sqrt_float(Type& to, const Type from, Rounding_Dir dir) {
  if (To_Policy::fpu_check_nan_result && is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  if (To_Policy::check_sqrt_neg && from < 0) {
    return assign_nan<To_Policy>(to, V_SQRT_NEG);
  }
  prepare_inexact<To_Policy>(dir);
  if (fpu_direct_rounding(dir)) {
    to = std::sqrt(from);
  }
  else {
    fpu_rounding_control_word_type old
      = fpu_save_rounding_direction(round_fpu_dir(dir));
    limit_precision(from);
    to = std::sqrt(from);
    limit_precision(to);
    fpu_restore_rounding_direction(old);
  }
  return result_relation<To_Policy>(dir);
}

template <typename Policy, typename Type>
inline Result_Relation
sgn_float(const Type x) {
  if (x > 0) {
    return VR_GT;
  }
  if (x < 0) {
    return VR_LT;
  }
  if (x == 0) {
    return VR_EQ;
  }
  return VR_EMPTY;
}

template <typename Policy1, typename Policy2, typename Type>
inline Result_Relation
cmp_float(const Type x, const Type y) {
  if (x > y) {
    return VR_GT;
  }
  if (x < y) {
    return VR_LT;
  }
  if (x == y) {
    return VR_EQ;
  }
  return VR_EMPTY;
}

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
assign_float_int_inexact(To& to, const From from, Rounding_Dir dir) {
  prepare_inexact<To_Policy>(dir);
  if (fpu_direct_rounding(dir)) {
    to = from;
  }
  else {
    fpu_rounding_control_word_type old
      = fpu_save_rounding_direction(round_fpu_dir(dir));
    to = from;
    limit_precision(to);
    fpu_restore_rounding_direction(old);
  }
  return result_relation<To_Policy>(dir);
}

template <typename To_Policy, typename From_Policy, typename To, typename From>
inline Result
assign_float_int(To& to, const From from, Rounding_Dir dir) {
  if (sizeof_to_bits(sizeof(From)) > Float<To>::Binary::MANTISSA_BITS) {
    return assign_float_int_inexact<To_Policy, From_Policy>(to, from, dir);
  }
  else {
    return assign_exact<To_Policy, From_Policy>(to, from, dir);
  }
}

template <typename Policy, typename T>
inline Result
set_neg_overflow_float(T& to, Rounding_Dir dir) {
  switch (round_dir(dir)) {
  case ROUND_UP:
    {
      Float<T> f;
      f.u.binary.set_max(true);
      to = f.value();
      return V_LT_INF;
    }
  case ROUND_DOWN: // Fall through.
  case ROUND_IGNORE:
    to = -HUGE_VAL;
    return V_GT_MINUS_INFINITY;
  default:
    PPL_UNREACHABLE;
    return V_GT_MINUS_INFINITY;
  }
}

template <typename Policy, typename T>
inline Result
set_pos_overflow_float(T& to, Rounding_Dir dir) {
  switch (round_dir(dir)) {
  case ROUND_DOWN:
    {
      Float<T> f;
      f.u.binary.set_max(false);
      to = f.value();
      return V_GT_SUP;
    }
  case ROUND_UP: // Fall through.
  case ROUND_IGNORE:
    to = HUGE_VAL;
    return V_LT_PLUS_INFINITY;
  default:
    PPL_UNREACHABLE;
    return V_LT_PLUS_INFINITY;
  }
}

template <typename To_Policy, typename From_Policy, typename T>
inline Result
assign_float_mpz(T& to, const mpz_class& from, Rounding_Dir dir) {
  int sign = sgn(from);
  if (sign == 0) {
    to = 0;
    return V_EQ;
  }
  mpz_srcptr from_z = from.get_mpz_t();
  size_t exponent = mpz_sizeinbase(from_z, 2) - 1;
  if (exponent > size_t(Float<T>::Binary::EXPONENT_MAX)) {
    if (sign < 0) {
      return set_neg_overflow_float<To_Policy>(to, dir);
    }
    else {
      return set_pos_overflow_float<To_Policy>(to, dir);
    }
  }
  unsigned long zeroes = mpn_scan1(from_z->_mp_d, 0);
  size_t meaningful_bits = exponent - zeroes;
  mpz_t mantissa;
  mpz_init(mantissa);
  if (exponent > Float<T>::Binary::MANTISSA_BITS) {
    mpz_tdiv_q_2exp(mantissa,
                    from_z,
                    exponent - Float<T>::Binary::MANTISSA_BITS);
  }
  else {
    mpz_mul_2exp(mantissa, from_z, Float<T>::Binary::MANTISSA_BITS - exponent);
  }
  Float<T> f;
  f.u.binary.build(sign < 0, mantissa, static_cast<long>(exponent));
  mpz_clear(mantissa);
  to = f.value();
  if (meaningful_bits > Float<T>::Binary::MANTISSA_BITS) {
    if (sign < 0) {
      return round_lt_float<To_Policy>(to, dir);
    }
    else {
      return round_gt_float<To_Policy>(to, dir);
    }
  }
  return V_EQ;
}

template <typename To_Policy, typename From_Policy, typename T>
inline Result
assign_float_mpq(T& to, const mpq_class& from, Rounding_Dir dir) {
  const mpz_class& numer = from.get_num();
  const mpz_class& denom = from.get_den();
  if (denom == 1) {
    return assign_float_mpz<To_Policy, From_Policy>(to, numer, dir);
  }
  mpz_srcptr numer_z = numer.get_mpz_t();
  mpz_srcptr denom_z = denom.get_mpz_t();
  int sign = sgn(numer);
  long exponent = static_cast<long>(mpz_sizeinbase(numer_z, 2))
    - static_cast<long>(mpz_sizeinbase(denom_z, 2));
  if (exponent < Float<T>::Binary::EXPONENT_MIN_DENORM) {
    to = 0;
  inexact:
    if (sign < 0) {
      return round_lt_float<To_Policy>(to, dir);
    }
    else {
      return round_gt_float<To_Policy>(to, dir);
    }
  }
  if (exponent > Float<T>::Binary::EXPONENT_MAX + 1) {
  overflow:
    if (sign < 0) {
      return set_neg_overflow_float<To_Policy>(to, dir);
    }
    else {
      return set_pos_overflow_float<To_Policy>(to, dir);
    }
  }
  unsigned int needed_bits = Float<T>::Binary::MANTISSA_BITS + 1;
  if (exponent < Float<T>::Binary::EXPONENT_MIN) {
    long diff = Float<T>::Binary::EXPONENT_MIN - exponent;
    needed_bits -= static_cast<unsigned int>(diff);
  }
  mpz_t mantissa;
  mpz_init(mantissa);
  {
    long shift = static_cast<long>(needed_bits) - exponent;
    if (shift > 0) {
      mpz_mul_2exp(mantissa, numer_z, static_cast<unsigned long>(shift));
      numer_z = mantissa;
    }
    else if (shift < 0) {
      shift = -shift;
      mpz_mul_2exp(mantissa, denom_z, static_cast<unsigned long>(shift));
      denom_z = mantissa;
    }
  }
  mpz_t r;
  mpz_init(r);
  mpz_tdiv_qr(mantissa, r, numer_z, denom_z);
  size_t bits = mpz_sizeinbase(mantissa, 2);
  bool inexact = (mpz_sgn(r) != 0);
  mpz_clear(r);
  if (bits == needed_bits + 1) {
    inexact = (inexact || mpz_odd_p(mantissa));
    mpz_tdiv_q_2exp(mantissa, mantissa, 1);
  }
  else {
    --exponent;
  }
  if (exponent > Float<T>::Binary::EXPONENT_MAX) {
    mpz_clear(mantissa);
    goto overflow;
  }
  else if (exponent < Float<T>::Binary::EXPONENT_MIN - 1) {
    // Denormalized.
    exponent = Float<T>::Binary::EXPONENT_MIN - 1;
  }
  Float<T> f;
  f.u.binary.build(sign < 0, mantissa, exponent);
  mpz_clear(mantissa);
  to = f.value();
  if (inexact) {
    goto inexact;
  }
  return V_EQ;
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename Type>
inline Result
add_mul_float(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_inf_mul_zero
      && ((x == 0 && is_inf_float<From2_Policy>(y))
          ||
          (y == 0 && is_inf_float<From1_Policy>(x)))) {
    return assign_nan<To_Policy>(to, V_INF_MUL_ZERO);
  }
  // FIXME: missing check_inf_add_inf
  prepare_inexact<To_Policy>(dir);
  if (fpu_direct_rounding(dir)) {
    to = multiply_add(x, y, to);
  }
  else if (fpu_inverse_rounding(dir)) {
    to = multiply_add(-x, y, -to);
    limit_precision(to);
    to = -to;
  }
  else {
    fpu_rounding_control_word_type old
      = fpu_save_rounding_direction(round_fpu_dir(dir));
    limit_precision(x);
    limit_precision(y);
    limit_precision(to);
    to = multiply_add(x, y, to);
    limit_precision(to);
    fpu_restore_rounding_direction(old);
  }
  if (To_Policy::fpu_check_nan_result && is_nan<To_Policy>(to)) {
    return V_NAN;
  }
  return result_relation<To_Policy>(dir);
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy, typename Type>
inline Result
sub_mul_float(Type& to, const Type x, const Type y, Rounding_Dir dir) {
  if (To_Policy::check_inf_mul_zero
      && ((x == 0 && is_inf_float<From2_Policy>(y))
          ||
          (y == 0 && is_inf_float<From1_Policy>(x)))) {
    return assign_nan<To_Policy>(to, V_INF_MUL_ZERO);
  }
  // FIXME: missing check_inf_add_inf
  prepare_inexact<To_Policy>(dir);
  if (fpu_direct_rounding(dir)) {
    to = multiply_add(x, -y, to);
  }
  else if (fpu_inverse_rounding(dir)) {
    to = multiply_add(x, y, -to);
    limit_precision(to);
    to = -to;
  }
  else {
    fpu_rounding_control_word_type old
      = fpu_save_rounding_direction(round_fpu_dir(dir));
    limit_precision(x);
    limit_precision(y);
    limit_precision(to);
    to = multiply_add(x, -y, to);
    limit_precision(to);
    fpu_restore_rounding_direction(old);
  }
  if (To_Policy::fpu_check_nan_result && is_nan<To_Policy>(to)) {
    return V_NAN;
  }
  return result_relation<To_Policy>(dir);
}

template <typename From>
inline void
assign_mpq_numeric_float(mpq_class& to, const From from) {
  to = from;
}

template <>
inline void
assign_mpq_numeric_float(mpq_class& to, const long double from) {
  to = 0;
  if (from == 0.0L) {
    return;
  }
  mpz_class& num = to.get_num();
  mpz_class& den = to.get_den();
  int exp;
  long double n = std::frexp(from, &exp);
  bool neg = false;
  if (n < 0.0L) {
    neg = true;
    n = -n;
  }
  const long double mult = static_cast<long double>(ULONG_MAX) + 1.0L;
  const unsigned int bits = sizeof(unsigned long) * CHAR_BIT;
  while (true) {
    n *= mult;
    exp -= bits;
    const long double intpart = std::floor(n);
    num += static_cast<unsigned long>(intpart);
    n -= intpart;
    if (n == 0.0L) {
      break;
    }
    num <<= bits;
  }
  if (exp < 0) {
    den <<= -exp;
  }
  else {
    num <<= exp;
  }
  if (neg) {
    to = -to;
  }
  to.canonicalize();
}

template <typename Policy, typename Type>
inline Result
output_float(std::ostream& os, const Type from, const Numeric_Format&,
             Rounding_Dir) {
  if (from == 0) {
    os << "0";
  }
  else if (is_minf<Policy>(from)) {
    os << "-inf";
  }
  else if (is_pinf<Policy>(from)) {
    os << "+inf";
  }
  else if (is_nan<Policy>(from)) {
    os << "nan";
  }
  else {
    mpq_class q;
    assign_mpq_numeric_float(q, from);
    std::string s = float_mpq_to_string(q);
    os << s;
  }
  return V_EQ;
}

#if PPL_SUPPORTED_FLOAT
PPL_SPECIALIZE_ASSIGN(assign_float_float_exact, float, float)
#if PPL_SUPPORTED_DOUBLE
PPL_SPECIALIZE_ASSIGN(assign_float_float, float, double)
PPL_SPECIALIZE_ASSIGN(assign_float_float_exact, double, float)
#endif
#if PPL_SUPPORTED_LONG_DOUBLE
PPL_SPECIALIZE_ASSIGN(assign_float_float, float, long double)
PPL_SPECIALIZE_ASSIGN(assign_float_float_exact, long double, float)
#endif
#endif

#if PPL_SUPPORTED_DOUBLE
PPL_SPECIALIZE_ASSIGN(assign_float_float_exact, double, double)
#if PPL_SUPPORTED_LONG_DOUBLE
PPL_SPECIALIZE_ASSIGN(assign_float_float, double, long double)
PPL_SPECIALIZE_ASSIGN(assign_float_float_exact, long double, double)
#endif
#endif

#if PPL_SUPPORTED_LONG_DOUBLE
PPL_SPECIALIZE_ASSIGN(assign_float_float_exact, long double, long double)
#endif

#if PPL_SUPPORTED_FLOAT
PPL_SPECIALIZE_CLASSIFY(classify_float, float)
PPL_SPECIALIZE_IS_NAN(is_nan_float, float)
PPL_SPECIALIZE_IS_MINF(is_minf_float, float)
PPL_SPECIALIZE_IS_PINF(is_pinf_float, float)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_float, float)
PPL_SPECIALIZE_ASSIGN(assign_float_int, float, char)
PPL_SPECIALIZE_ASSIGN(assign_float_int, float, signed char)
PPL_SPECIALIZE_ASSIGN(assign_float_int, float, signed short)
PPL_SPECIALIZE_ASSIGN(assign_float_int, float, signed int)
PPL_SPECIALIZE_ASSIGN(assign_float_int, float, signed long)
PPL_SPECIALIZE_ASSIGN(assign_float_int, float, signed long long)
PPL_SPECIALIZE_ASSIGN(assign_float_int, float, unsigned char)
PPL_SPECIALIZE_ASSIGN(assign_float_int, float, unsigned short)
PPL_SPECIALIZE_ASSIGN(assign_float_int, float, unsigned int)
PPL_SPECIALIZE_ASSIGN(assign_float_int, float, unsigned long)
PPL_SPECIALIZE_ASSIGN(assign_float_int, float, unsigned long long)
PPL_SPECIALIZE_ASSIGN(assign_float_mpz, float, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_float_mpq, float, mpq_class)
PPL_SPECIALIZE_COPY(copy_generic, float)
PPL_SPECIALIZE_IS_INT(is_int_float, float)
PPL_SPECIALIZE_FLOOR(floor_float, float, float)
PPL_SPECIALIZE_CEIL(ceil_float, float, float)
PPL_SPECIALIZE_TRUNC(trunc_float, float, float)
PPL_SPECIALIZE_NEG(neg_float, float, float)
PPL_SPECIALIZE_ABS(abs_float, float, float)
PPL_SPECIALIZE_ADD(add_float, float, float, float)
PPL_SPECIALIZE_SUB(sub_float, float, float, float)
PPL_SPECIALIZE_MUL(mul_float, float, float, float)
PPL_SPECIALIZE_DIV(div_float, float, float, float)
PPL_SPECIALIZE_REM(rem_float, float, float, float)
PPL_SPECIALIZE_ADD_2EXP(add_2exp_float, float, float)
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_float, float, float)
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_float, float, float)
PPL_SPECIALIZE_DIV_2EXP(div_2exp_float, float, float)
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_float, float, float)
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_float, float, float)
PPL_SPECIALIZE_SQRT(sqrt_float, float, float)
PPL_SPECIALIZE_GCD(gcd_exact, float, float, float)
PPL_SPECIALIZE_GCDEXT(gcdext_exact, float, float, float, float, float)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, float, float, float)
PPL_SPECIALIZE_SGN(sgn_float, float)
PPL_SPECIALIZE_CMP(cmp_float, float, float)
PPL_SPECIALIZE_ADD_MUL(add_mul_float, float, float, float)
PPL_SPECIALIZE_SUB_MUL(sub_mul_float, float, float, float)
PPL_SPECIALIZE_INPUT(input_generic, float)
PPL_SPECIALIZE_OUTPUT(output_float, float)
#endif

#if PPL_SUPPORTED_DOUBLE
PPL_SPECIALIZE_CLASSIFY(classify_float, double)
PPL_SPECIALIZE_IS_NAN(is_nan_float, double)
PPL_SPECIALIZE_IS_MINF(is_minf_float, double)
PPL_SPECIALIZE_IS_PINF(is_pinf_float, double)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_float, double)
PPL_SPECIALIZE_ASSIGN(assign_float_int, double, char)
PPL_SPECIALIZE_ASSIGN(assign_float_int, double, signed char)
PPL_SPECIALIZE_ASSIGN(assign_float_int, double, signed short)
PPL_SPECIALIZE_ASSIGN(assign_float_int, double, signed int)
PPL_SPECIALIZE_ASSIGN(assign_float_int, double, signed long)
PPL_SPECIALIZE_ASSIGN(assign_float_int, double, signed long long)
PPL_SPECIALIZE_ASSIGN(assign_float_int, double, unsigned char)
PPL_SPECIALIZE_ASSIGN(assign_float_int, double, unsigned short)
PPL_SPECIALIZE_ASSIGN(assign_float_int, double, unsigned int)
PPL_SPECIALIZE_ASSIGN(assign_float_int, double, unsigned long)
PPL_SPECIALIZE_ASSIGN(assign_float_int, double, unsigned long long)
PPL_SPECIALIZE_ASSIGN(assign_float_mpz, double, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_float_mpq, double, mpq_class)
PPL_SPECIALIZE_COPY(copy_generic, double)
PPL_SPECIALIZE_IS_INT(is_int_float, double)
PPL_SPECIALIZE_FLOOR(floor_float, double, double)
PPL_SPECIALIZE_CEIL(ceil_float, double, double)
PPL_SPECIALIZE_TRUNC(trunc_float, double, double)
PPL_SPECIALIZE_NEG(neg_float, double, double)
PPL_SPECIALIZE_ABS(abs_float, double, double)
PPL_SPECIALIZE_ADD(add_float, double, double, double)
PPL_SPECIALIZE_SUB(sub_float, double, double, double)
PPL_SPECIALIZE_MUL(mul_float, double, double, double)
PPL_SPECIALIZE_DIV(div_float, double, double, double)
PPL_SPECIALIZE_REM(rem_float, double, double, double)
PPL_SPECIALIZE_ADD_2EXP(add_2exp_float, double, double)
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_float, double, double)
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_float, double, double)
PPL_SPECIALIZE_DIV_2EXP(div_2exp_float, double, double)
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_float, double, double)
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_float, double, double)
PPL_SPECIALIZE_SQRT(sqrt_float, double, double)
PPL_SPECIALIZE_GCD(gcd_exact, double, double, double)
PPL_SPECIALIZE_GCDEXT(gcdext_exact, double, double, double, double, double)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, double, double, double)
PPL_SPECIALIZE_SGN(sgn_float, double)
PPL_SPECIALIZE_CMP(cmp_float, double, double)
PPL_SPECIALIZE_ADD_MUL(add_mul_float, double, double, double)
PPL_SPECIALIZE_SUB_MUL(sub_mul_float, double, double, double)
PPL_SPECIALIZE_INPUT(input_generic, double)
PPL_SPECIALIZE_OUTPUT(output_float, double)
#endif

#if PPL_SUPPORTED_LONG_DOUBLE
PPL_SPECIALIZE_CLASSIFY(classify_float, long double)
PPL_SPECIALIZE_IS_NAN(is_nan_float, long double)
PPL_SPECIALIZE_IS_MINF(is_minf_float, long double)
PPL_SPECIALIZE_IS_PINF(is_pinf_float, long double)
PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_float, long double)
PPL_SPECIALIZE_ASSIGN(assign_float_int, long double, char)
PPL_SPECIALIZE_ASSIGN(assign_float_int, long double, signed char)
PPL_SPECIALIZE_ASSIGN(assign_float_int, long double, signed short)
PPL_SPECIALIZE_ASSIGN(assign_float_int, long double, signed int)
PPL_SPECIALIZE_ASSIGN(assign_float_int, long double, signed long)
PPL_SPECIALIZE_ASSIGN(assign_float_int, long double, signed long long)
PPL_SPECIALIZE_ASSIGN(assign_float_int, long double, unsigned char)
PPL_SPECIALIZE_ASSIGN(assign_float_int, long double, unsigned short)
PPL_SPECIALIZE_ASSIGN(assign_float_int, long double, unsigned int)
PPL_SPECIALIZE_ASSIGN(assign_float_int, long double, unsigned long)
PPL_SPECIALIZE_ASSIGN(assign_float_int, long double, unsigned long long)
PPL_SPECIALIZE_ASSIGN(assign_float_mpz, long double, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_float_mpq, long double, mpq_class)
PPL_SPECIALIZE_COPY(copy_generic, long double)
PPL_SPECIALIZE_IS_INT(is_int_float, long double)
PPL_SPECIALIZE_FLOOR(floor_float, long double, long double)
PPL_SPECIALIZE_CEIL(ceil_float, long double, long double)
PPL_SPECIALIZE_TRUNC(trunc_float, long double, long double)
PPL_SPECIALIZE_NEG(neg_float, long double, long double)
PPL_SPECIALIZE_ABS(abs_float, long double, long double)
PPL_SPECIALIZE_ADD(add_float, long double, long double, long double)
PPL_SPECIALIZE_SUB(sub_float, long double, long double, long double)
PPL_SPECIALIZE_MUL(mul_float, long double, long double, long double)
PPL_SPECIALIZE_DIV(div_float, long double, long double, long double)
PPL_SPECIALIZE_REM(rem_float, long double, long double, long double)
PPL_SPECIALIZE_ADD_2EXP(add_2exp_float, long double, long double)
PPL_SPECIALIZE_SUB_2EXP(sub_2exp_float, long double, long double)
PPL_SPECIALIZE_MUL_2EXP(mul_2exp_float, long double, long double)
PPL_SPECIALIZE_DIV_2EXP(div_2exp_float, long double, long double)
PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_float, long double, long double)
PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_float, long double, long double)
PPL_SPECIALIZE_SQRT(sqrt_float, long double, long double)
PPL_SPECIALIZE_GCD(gcd_exact, long double, long double, long double)
PPL_SPECIALIZE_GCDEXT(gcdext_exact, long double, long double, long double,
                  long double, long double)
PPL_SPECIALIZE_LCM(lcm_gcd_exact, long double, long double, long double)
PPL_SPECIALIZE_SGN(sgn_float, long double)
PPL_SPECIALIZE_CMP(cmp_float, long double, long double)
PPL_SPECIALIZE_ADD_MUL(add_mul_float, long double, long double, long double)
PPL_SPECIALIZE_SUB_MUL(sub_mul_float, long double, long double, long double)
PPL_SPECIALIZE_INPUT(input_generic, long double)
PPL_SPECIALIZE_OUTPUT(output_float, long double)
#endif

} // namespace Checked

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/checked_mpz_inlines.hh line 1. */
/* Specialized "checked" functions for GMP's mpz_class numbers.
*/


#include <sstream>

namespace Parma_Polyhedra_Library {

namespace Checked {

template <typename Policy>
inline Result
round_lt_mpz(mpz_class& to, Rounding_Dir dir) {
  if (round_down(dir)) {
    --to;
    return V_GT;
  }
  return V_LT;
}

template <typename Policy>
inline Result
round_gt_mpz(mpz_class& to, Rounding_Dir dir) {
  if (round_up(dir)) {
    ++to;
    return V_LT;
  }
  return V_GT;
}

#if __cplusplus >= 201103L
//! Type of the _mp_size field of GMP's __mpz_struct.
typedef decltype(__mpz_struct()._mp_size) mp_size_field_t;
#elif PPL_HAVE_TYPEOF
//! Type of the _mp_size field of GMP's __mpz_struct.
typedef typeof(__mpz_struct()._mp_size) mp_size_field_t;
#else
//! This is assumed to be the type of the _mp_size field of GMP's __mpz_struct.
typedef int mp_size_field_t;
#endif

inline mp_size_field_t
get_mp_size(const mpz_class &v) {
  return v.get_mpz_t()->_mp_size;
}

inline void
set_mp_size(mpz_class &v, mp_size_field_t size) {
  v.get_mpz_t()->_mp_size = size;
}

template <typename Policy>
inline Result
classify_mpz(const mpz_class& v, bool nan, bool inf, bool sign) {
  if (Policy::has_nan || Policy::has_infinity) {
    mp_size_field_t s = get_mp_size(v);
    if (Policy::has_nan
        && (nan || sign)
        && s == C_Integer<mp_size_field_t>::min + 1) {
      return V_NAN;
    }
    if (!inf && !sign) {
      return V_LGE;
    }
    if (Policy::has_infinity) {
      if (s == C_Integer<mp_size_field_t>::min) {
        return inf ? V_EQ_MINUS_INFINITY : V_LT;
      }
      if (s == C_Integer<mp_size_field_t>::max) {
        return inf ? V_EQ_PLUS_INFINITY : V_GT;
      }
    }
  }
  if (sign) {
    return static_cast<Result>(sgn<Policy>(v));
  }
  return V_LGE;
}

PPL_SPECIALIZE_CLASSIFY(classify_mpz, mpz_class)

template <typename Policy>
inline bool
is_nan_mpz(const mpz_class& v) {
  return Policy::has_nan
    && get_mp_size(v) == C_Integer<mp_size_field_t>::min + 1;
}

PPL_SPECIALIZE_IS_NAN(is_nan_mpz, mpz_class)

template <typename Policy>
inline bool
is_minf_mpz(const mpz_class& v) {
  return Policy::has_infinity
    && get_mp_size(v) == C_Integer<mp_size_field_t>::min;
}

PPL_SPECIALIZE_IS_MINF(is_minf_mpz, mpz_class)

template <typename Policy>
inline bool
is_pinf_mpz(const mpz_class& v) {
  return Policy::has_infinity
    && get_mp_size(v) == C_Integer<mp_size_field_t>::max;
}

PPL_SPECIALIZE_IS_PINF(is_pinf_mpz, mpz_class)

template <typename Policy>
inline bool
is_int_mpz(const mpz_class& v) {
  return !is_nan<Policy>(v);
}

PPL_SPECIALIZE_IS_INT(is_int_mpz, mpz_class)

template <typename Policy>
inline Result
assign_special_mpz(mpz_class& v, Result_Class c, Rounding_Dir) {
  switch (c) {
  case VC_NAN:
    if (Policy::has_nan) {
      set_mp_size(v, C_Integer<mp_size_field_t>::min + 1);
    }
    return V_NAN;
  case VC_MINUS_INFINITY:
    if (Policy::has_infinity) {
      set_mp_size(v, C_Integer<mp_size_field_t>::min);
      return V_EQ_MINUS_INFINITY;
    }
    return V_EQ_MINUS_INFINITY | V_UNREPRESENTABLE;
  case VC_PLUS_INFINITY:
    if (Policy::has_infinity) {
      set_mp_size(v, C_Integer<mp_size_field_t>::max);
      return V_EQ_PLUS_INFINITY;
    }
    return V_EQ_PLUS_INFINITY | V_UNREPRESENTABLE;
  default:
    PPL_UNREACHABLE;
    return V_NAN;
  }
}

PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_mpz, mpz_class)

template <typename To_Policy, typename From_Policy>
inline void
copy_mpz(mpz_class& to, const mpz_class& from) {
  if (is_nan_mpz<From_Policy>(from)) {
    PPL_ASSERT(To_Policy::has_nan);
  }
  else if (is_minf_mpz<From_Policy>(from) || is_pinf_mpz<From_Policy>(from)) {
    PPL_ASSERT(To_Policy::has_infinity);
  }
  else {
    to = from;
    return;
  }
  set_mp_size(to, get_mp_size(from));
}

PPL_SPECIALIZE_COPY(copy_mpz, mpz_class)

template <typename To_Policy, typename From_Policy, typename From>
inline Result
construct_mpz_base(mpz_class& to, const From from, Rounding_Dir) {
    new(&to) mpz_class(from);
    return V_EQ;
}

PPL_SPECIALIZE_CONSTRUCT(construct_mpz_base, mpz_class, char)
PPL_SPECIALIZE_CONSTRUCT(construct_mpz_base, mpz_class, signed char)
PPL_SPECIALIZE_CONSTRUCT(construct_mpz_base, mpz_class, signed short)
PPL_SPECIALIZE_CONSTRUCT(construct_mpz_base, mpz_class, signed int)
PPL_SPECIALIZE_CONSTRUCT(construct_mpz_base, mpz_class, signed long)
PPL_SPECIALIZE_CONSTRUCT(construct_mpz_base, mpz_class, unsigned char)
PPL_SPECIALIZE_CONSTRUCT(construct_mpz_base, mpz_class, unsigned short)
PPL_SPECIALIZE_CONSTRUCT(construct_mpz_base, mpz_class, unsigned int)
PPL_SPECIALIZE_CONSTRUCT(construct_mpz_base, mpz_class, unsigned long)

template <typename To_Policy, typename From_Policy, typename From>
inline Result
construct_mpz_float(mpz_class& to, const From& from, Rounding_Dir dir) {
  if (is_nan<From_Policy>(from)) {
    return construct_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(from)) {
    return construct_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(from)) {
    return construct_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  if (round_not_requested(dir)) {
    new(&to) mpz_class(from);
    return V_LGE;
  }
  From n = rint(from);
  new(&to) mpz_class(n);
  if (from == n) {
    return V_EQ;
  }
  if (from < 0) {
    return round_lt_mpz<To_Policy>(to, dir);
  }
  else {
    return round_gt_mpz<To_Policy>(to, dir);
  }
}

PPL_SPECIALIZE_CONSTRUCT(construct_mpz_float, mpz_class, float)
PPL_SPECIALIZE_CONSTRUCT(construct_mpz_float, mpz_class, double)

PPL_SPECIALIZE_ASSIGN(assign_exact, mpz_class, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpz_class, char)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpz_class, signed char)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpz_class, signed short)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpz_class, signed int)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpz_class, signed long)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpz_class, unsigned char)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpz_class, unsigned short)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpz_class, unsigned int)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpz_class, unsigned long)

template <typename To_Policy, typename From_Policy, typename From>
inline Result
assign_mpz_signed_int(mpz_class& to, const From from, Rounding_Dir) {
  if (sizeof(From) <= sizeof(signed long)) {
    to = static_cast<signed long>(from);
  }
  else {
    mpz_ptr m = to.get_mpz_t();
    if (from >= 0) {
      mpz_import(m, 1, 1, sizeof(From), 0, 0, &from);
    }
    else {
      From n = -from;
      mpz_import(m, 1, 1, sizeof(From), 0, 0, &n);
      mpz_neg(m, m);
    }
  }
  return V_EQ;
}

PPL_SPECIALIZE_ASSIGN(assign_mpz_signed_int, mpz_class, signed long long)

template <typename To_Policy, typename From_Policy, typename From>
inline Result
assign_mpz_unsigned_int(mpz_class& to, const From from, Rounding_Dir) {
  if (sizeof(From) <= sizeof(unsigned long)) {
    to = static_cast<unsigned long>(from);
  }
  else {
    mpz_import(to.get_mpz_t(), 1, 1, sizeof(From), 0, 0, &from);
  }
  return V_EQ;
}

PPL_SPECIALIZE_ASSIGN(assign_mpz_unsigned_int, mpz_class, unsigned long long)

template <typename To_Policy, typename From_Policy, typename From>
inline Result
assign_mpz_float(mpz_class& to, const From from, Rounding_Dir dir) {
  if (is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  if (round_not_requested(dir)) {
    to = from;
    return V_LGE;
  }
  From i_from = rint(from);
  to = i_from;
  if (from == i_from) {
    return V_EQ;
  }
  if (round_direct(ROUND_UP)) {
    return round_lt_mpz<To_Policy>(to, dir);
  }
  if (round_direct(ROUND_DOWN)) {
    return round_gt_mpz<To_Policy>(to, dir);
  }
  if (from < i_from) {
    return round_lt_mpz<To_Policy>(to, dir);
  }
  if (from > i_from) {
    return round_gt_mpz<To_Policy>(to, dir);
  }
  PPL_UNREACHABLE;
  return V_NAN;
}

PPL_SPECIALIZE_ASSIGN(assign_mpz_float, mpz_class, float)
PPL_SPECIALIZE_ASSIGN(assign_mpz_float, mpz_class, double)

template <typename To_Policy, typename From_Policy, typename From>
inline Result
assign_mpz_long_double(mpz_class& to, const From& from, Rounding_Dir dir) {
  if (is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  // FIXME: this is an incredibly inefficient implementation!
  std::stringstream ss;
  output<From_Policy>(ss, from, Numeric_Format(), dir);
  PPL_DIRTY_TEMP(mpq_class, tmp);
#ifndef NDEBUG
  Result r =
#endif
    input_mpq(tmp, ss);
  PPL_ASSERT(r == V_EQ);
  return assign<To_Policy, From_Policy>(to, tmp, dir);
}

PPL_SPECIALIZE_ASSIGN(assign_mpz_long_double, mpz_class, long double)

template <typename To_Policy, typename From_Policy>
inline Result
assign_mpz_mpq(mpz_class& to, const mpq_class& from, Rounding_Dir dir) {
  if (round_not_needed(dir)) {
    to = from.get_num();
    return V_LGE;
  }
  if (round_ignore(dir)) {
    to = from;
    return V_LGE;
  }
  const mpz_srcptr n = from.get_num().get_mpz_t();
  const mpz_srcptr d = from.get_den().get_mpz_t();
  if (round_down(dir)) {
    mpz_fdiv_q(to.get_mpz_t(), n, d);
    if (round_strict_relation(dir)) {
      return (mpz_divisible_p(n, d) != 0) ? V_EQ : V_GT;
    }
    return V_GE;
  }
  else {
    PPL_ASSERT(round_up(dir));
    mpz_cdiv_q(to.get_mpz_t(), n, d);
    if (round_strict_relation(dir)) {
      return (mpz_divisible_p(n, d) != 0) ? V_EQ : V_LT;
    }
    return V_LE;
  }
}

PPL_SPECIALIZE_ASSIGN(assign_mpz_mpq, mpz_class, mpq_class)

PPL_SPECIALIZE_FLOOR(assign_exact, mpz_class, mpz_class)
PPL_SPECIALIZE_CEIL(assign_exact, mpz_class, mpz_class)
PPL_SPECIALIZE_TRUNC(assign_exact, mpz_class, mpz_class)

template <typename To_Policy, typename From_Policy>
inline Result
neg_mpz(mpz_class& to, const mpz_class& from, Rounding_Dir) {
  mpz_neg(to.get_mpz_t(), from.get_mpz_t());
  return V_EQ;
}

PPL_SPECIALIZE_NEG(neg_mpz, mpz_class, mpz_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
add_mpz(mpz_class& to, const mpz_class& x, const mpz_class& y, Rounding_Dir) {
  to = x + y;
  return V_EQ;
}

PPL_SPECIALIZE_ADD(add_mpz, mpz_class, mpz_class, mpz_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
sub_mpz(mpz_class& to, const mpz_class& x, const mpz_class& y, Rounding_Dir) {
  to = x - y;
  return V_EQ;
}

PPL_SPECIALIZE_SUB(sub_mpz, mpz_class, mpz_class, mpz_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
mul_mpz(mpz_class& to, const mpz_class& x, const mpz_class& y, Rounding_Dir) {
  to = x * y;
  return V_EQ;
}

PPL_SPECIALIZE_MUL(mul_mpz, mpz_class, mpz_class, mpz_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
div_mpz(mpz_class& to, const mpz_class& x, const mpz_class& y,
        Rounding_Dir dir) {
  if (CHECK_P(To_Policy::check_div_zero, ::sgn(y) == 0)) {
    return assign_nan<To_Policy>(to, V_DIV_ZERO);
  }
  const mpz_srcptr n = x.get_mpz_t();
  const mpz_srcptr d = y.get_mpz_t();
  if (round_not_needed(dir)) {
    mpz_divexact(to.get_mpz_t(), n, d);
    return V_LGE;
  }
  if (round_ignore(dir)) {
    mpz_cdiv_q(to.get_mpz_t(), n, d);
    return V_LE;
  }
  if (round_down(dir)) {
    mpz_fdiv_q(to.get_mpz_t(), n, d);
    if (round_strict_relation(dir)) {
      return (mpz_divisible_p(n, d) != 0) ? V_EQ : V_GT;
    }
    return V_GE;
  }
  else {
    PPL_ASSERT(round_up(dir));
    mpz_cdiv_q(to.get_mpz_t(), n, d);
    if (round_strict_relation(dir)) {
      return (mpz_divisible_p(n, d) != 0) ? V_EQ : V_LT;
    }
    return V_LE;
  }
}

PPL_SPECIALIZE_DIV(div_mpz, mpz_class, mpz_class, mpz_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
idiv_mpz(mpz_class& to, const mpz_class& x, const mpz_class& y,
        Rounding_Dir) {
  if (CHECK_P(To_Policy::check_div_zero, ::sgn(y) == 0)) {
    return assign_nan<To_Policy>(to, V_DIV_ZERO);
  }
  mpz_srcptr n = x.get_mpz_t();
  mpz_srcptr d = y.get_mpz_t();
  mpz_tdiv_q(to.get_mpz_t(), n, d);
  return V_EQ;
}

PPL_SPECIALIZE_IDIV(idiv_mpz, mpz_class, mpz_class, mpz_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
rem_mpz(mpz_class& to, const mpz_class& x, const mpz_class& y, Rounding_Dir) {
  if (CHECK_P(To_Policy::check_div_zero, ::sgn(y) == 0)) {
    return assign_nan<To_Policy>(to, V_MOD_ZERO);
  }
  to = x % y;
  return V_EQ;
}

PPL_SPECIALIZE_REM(rem_mpz, mpz_class, mpz_class, mpz_class)

template <typename To_Policy, typename From_Policy>
inline Result
add_2exp_mpz(mpz_class& to, const mpz_class& x, unsigned int exp,
             Rounding_Dir) {
  PPL_DIRTY_TEMP(mpz_class, v);
  v = 1;
  mpz_mul_2exp(v.get_mpz_t(), v.get_mpz_t(), exp);
  to = x + v;
  return V_EQ;
}

PPL_SPECIALIZE_ADD_2EXP(add_2exp_mpz, mpz_class, mpz_class)

template <typename To_Policy, typename From_Policy>
inline Result
sub_2exp_mpz(mpz_class& to, const mpz_class& x, unsigned int exp,
             Rounding_Dir) {
  PPL_DIRTY_TEMP(mpz_class, v);
  v = 1;
  mpz_mul_2exp(v.get_mpz_t(), v.get_mpz_t(), exp);
  to = x - v;
  return V_EQ;
}

PPL_SPECIALIZE_SUB_2EXP(sub_2exp_mpz, mpz_class, mpz_class)

template <typename To_Policy, typename From_Policy>
inline Result
mul_2exp_mpz(mpz_class& to, const mpz_class& x, unsigned int exp,
             Rounding_Dir) {
  mpz_mul_2exp(to.get_mpz_t(), x.get_mpz_t(), exp);
  return V_EQ;
}

PPL_SPECIALIZE_MUL_2EXP(mul_2exp_mpz, mpz_class, mpz_class)

template <typename To_Policy, typename From_Policy>
inline Result
div_2exp_mpz(mpz_class& to, const mpz_class& x, unsigned int exp,
             Rounding_Dir dir) {
  const mpz_srcptr n = x.get_mpz_t();
  if (round_not_requested(dir)) {
    mpz_tdiv_q_2exp(to.get_mpz_t(), x.get_mpz_t(), exp);
    return V_LGE;
  }
  if (round_down(dir)) {
    mpz_fdiv_q_2exp(to.get_mpz_t(), n, exp);
    if (round_strict_relation(dir)) {
      return (mpz_divisible_2exp_p(n, exp) != 0) ? V_EQ : V_GT;
    }
    return V_GE;
  }
  else {
    PPL_ASSERT(round_up(dir));
    mpz_cdiv_q_2exp(to.get_mpz_t(), n, exp);
    if (round_strict_relation(dir)) {
      return (mpz_divisible_2exp_p(n, exp) != 0) ? V_EQ : V_LT;
    }
    return V_LE;
  }
}

PPL_SPECIALIZE_DIV_2EXP(div_2exp_mpz, mpz_class, mpz_class)

template <typename To_Policy, typename From_Policy>
inline Result
smod_2exp_mpz(mpz_class& to, const mpz_class& x, unsigned int exp,
              Rounding_Dir) {
  if (mpz_tstbit(x.get_mpz_t(), exp - 1) != 0) {
    mpz_cdiv_r_2exp(to.get_mpz_t(), x.get_mpz_t(), exp);
  }
  else {
    mpz_fdiv_r_2exp(to.get_mpz_t(), x.get_mpz_t(), exp);
  }
  return V_EQ;
}

PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_mpz, mpz_class, mpz_class)

template <typename To_Policy, typename From_Policy>
inline Result
umod_2exp_mpz(mpz_class& to, const mpz_class& x, unsigned int exp,
              Rounding_Dir) {
  mpz_fdiv_r_2exp(to.get_mpz_t(), x.get_mpz_t(), exp);
  return V_EQ;
}

PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_mpz, mpz_class, mpz_class)

template <typename To_Policy, typename From_Policy>
inline Result
abs_mpz(mpz_class& to, const mpz_class& from, Rounding_Dir) {
  to = abs(from);
  return V_EQ;
}

PPL_SPECIALIZE_ABS(abs_mpz, mpz_class, mpz_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
add_mul_mpz(mpz_class& to, const mpz_class& x, const mpz_class& y,
            Rounding_Dir) {
  mpz_addmul(to.get_mpz_t(), x.get_mpz_t(), y.get_mpz_t());
  return V_EQ;
}

PPL_SPECIALIZE_ADD_MUL(add_mul_mpz, mpz_class, mpz_class, mpz_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
sub_mul_mpz(mpz_class& to, const mpz_class& x, const mpz_class& y,
            Rounding_Dir) {
  mpz_submul(to.get_mpz_t(), x.get_mpz_t(), y.get_mpz_t());
  return V_EQ;
}

PPL_SPECIALIZE_SUB_MUL(sub_mul_mpz, mpz_class, mpz_class, mpz_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
gcd_mpz(mpz_class& to, const mpz_class& x, const mpz_class& y, Rounding_Dir) {
  mpz_gcd(to.get_mpz_t(), x.get_mpz_t(), y.get_mpz_t());
  return V_EQ;
}

PPL_SPECIALIZE_GCD(gcd_mpz, mpz_class, mpz_class, mpz_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
gcdext_mpz(mpz_class& to, mpz_class& s, mpz_class& t,
           const mpz_class& x, const mpz_class& y,
           Rounding_Dir) {
  mpz_gcdext(to.get_mpz_t(), s.get_mpz_t(), t.get_mpz_t(),
             x.get_mpz_t(), y.get_mpz_t());
  return V_EQ;
}

PPL_SPECIALIZE_GCDEXT(gcdext_mpz, mpz_class, mpz_class, mpz_class, mpz_class, mpz_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
lcm_mpz(mpz_class& to, const mpz_class& x, const mpz_class& y, Rounding_Dir) {
  mpz_lcm(to.get_mpz_t(), x.get_mpz_t(), y.get_mpz_t());
  return V_EQ;
}

PPL_SPECIALIZE_LCM(lcm_mpz, mpz_class, mpz_class, mpz_class)

template <typename To_Policy, typename From_Policy>
inline Result
sqrt_mpz(mpz_class& to, const mpz_class& from, Rounding_Dir dir) {
  if (CHECK_P(To_Policy::check_sqrt_neg, from < 0)) {
    return assign_nan<To_Policy>(to, V_SQRT_NEG);
  }
  if (round_not_requested(dir)) {
    to = sqrt(from);
    return V_GE;
  }
  PPL_DIRTY_TEMP(mpz_class, r);
  mpz_sqrtrem(to.get_mpz_t(), r.get_mpz_t(), from.get_mpz_t());
  if (r == 0) {
    return V_EQ;
  }
  return round_gt_mpz<To_Policy>(to, dir);
}

PPL_SPECIALIZE_SQRT(sqrt_mpz, mpz_class, mpz_class)

template <typename Policy, typename Type>
inline Result_Relation
sgn_mp(const Type& x) {
  const int sign = ::sgn(x);
  return (sign > 0) ? VR_GT : ((sign < 0) ? VR_LT : VR_EQ);
}

PPL_SPECIALIZE_SGN(sgn_mp, mpz_class)
PPL_SPECIALIZE_SGN(sgn_mp, mpq_class)

template <typename Policy1, typename Policy2, typename Type>
inline Result_Relation
cmp_mp(const Type& x, const Type& y) {
  int i = ::cmp(x, y);
  return (i > 0) ? VR_GT : ((i < 0) ? VR_LT : VR_EQ);
}

PPL_SPECIALIZE_CMP(cmp_mp, mpz_class, mpz_class)
PPL_SPECIALIZE_CMP(cmp_mp, mpq_class, mpq_class)

template <typename Policy>
inline Result
output_mpz(std::ostream& os, const mpz_class& from, const Numeric_Format&,
           Rounding_Dir) {
  os << from;
  return V_EQ;
}

PPL_SPECIALIZE_INPUT(input_generic, mpz_class)
PPL_SPECIALIZE_OUTPUT(output_mpz, mpz_class)

} // namespace Checked

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/checked_mpq_inlines.hh line 1. */
/* Specialized "checked" functions for GMP's mpq_class numbers.
*/


#include <sstream>
#include <climits>
#include <stdexcept>

namespace Parma_Polyhedra_Library {

namespace Checked {

template <typename Policy>
inline Result
classify_mpq(const mpq_class& v, bool nan, bool inf, bool sign) {
  if ((Policy::has_nan || Policy::has_infinity)
      && ::sgn(v.get_den()) == 0) {
    int s = ::sgn(v.get_num());
    if (Policy::has_nan && (nan || sign) && s == 0) {
      return V_NAN;
    }
    if (!inf && !sign) {
      return V_LGE;
    }
    if (Policy::has_infinity) {
      if (s < 0) {
        return inf ? V_EQ_MINUS_INFINITY : V_LT;
      }
      if (s > 0) {
        return inf ? V_EQ_PLUS_INFINITY : V_GT;
      }
    }
  }
  if (sign) {
    return static_cast<Result>(sgn<Policy>(v));
  }
  return V_LGE;
}

PPL_SPECIALIZE_CLASSIFY(classify_mpq, mpq_class)

template <typename Policy>
inline bool
is_nan_mpq(const mpq_class& v) {
  return Policy::has_nan
    && ::sgn(v.get_den()) == 0
    && ::sgn(v.get_num()) == 0;
}

PPL_SPECIALIZE_IS_NAN(is_nan_mpq, mpq_class)

template <typename Policy>
inline bool
is_minf_mpq(const mpq_class& v) {
  return Policy::has_infinity
    && ::sgn(v.get_den()) == 0
    && ::sgn(v.get_num()) < 0;
}

PPL_SPECIALIZE_IS_MINF(is_minf_mpq, mpq_class)

template <typename Policy>
inline bool
is_pinf_mpq(const mpq_class& v) {
  return Policy::has_infinity
    && ::sgn(v.get_den()) == 0
    && ::sgn(v.get_num()) > 0;
}

PPL_SPECIALIZE_IS_PINF(is_pinf_mpq, mpq_class)

template <typename Policy>
inline bool
is_int_mpq(const mpq_class& v) {
  if ((Policy::has_infinity || Policy::has_nan)
      && ::sgn(v.get_den()) == 0) {
    return !(Policy::has_nan && ::sgn(v.get_num()) == 0);
  }
  else {
    return v.get_den() == 1;
  }
}

PPL_SPECIALIZE_IS_INT(is_int_mpq, mpq_class)

template <typename Policy>
inline Result
assign_special_mpq(mpq_class& v, Result_Class c, Rounding_Dir) {
  switch (c) {
  case VC_NAN:
    if (Policy::has_nan) {
      v.get_num() = 0;
      v.get_den() = 0;
      return V_NAN | V_UNREPRESENTABLE;
    }
    return V_NAN;
  case VC_MINUS_INFINITY:
    if (Policy::has_infinity) {
      v.get_num() = -1;
      v.get_den() = 0;
      return V_EQ_MINUS_INFINITY;
    }
    return V_EQ_MINUS_INFINITY | V_UNREPRESENTABLE;
  case VC_PLUS_INFINITY:
    if (Policy::has_infinity) {
      v.get_num() = 1;
      v.get_den() = 0;
      return V_EQ_PLUS_INFINITY;
    }
    return V_EQ_PLUS_INFINITY | V_UNREPRESENTABLE;
  default:
    PPL_UNREACHABLE;
    return V_NAN | V_UNREPRESENTABLE;
  }
}

PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_mpq, mpq_class)

PPL_SPECIALIZE_COPY(copy_generic, mpq_class)

template <typename To_Policy, typename From_Policy, typename From>
inline Result
construct_mpq_base(mpq_class& to, const From& from, Rounding_Dir) {
  new(&to) mpq_class(from);
  return V_EQ;
}

PPL_SPECIALIZE_CONSTRUCT(construct_mpq_base, mpq_class, mpz_class)
PPL_SPECIALIZE_CONSTRUCT(construct_mpq_base, mpq_class, char)
PPL_SPECIALIZE_CONSTRUCT(construct_mpq_base, mpq_class, signed char)
PPL_SPECIALIZE_CONSTRUCT(construct_mpq_base, mpq_class, signed short)
PPL_SPECIALIZE_CONSTRUCT(construct_mpq_base, mpq_class, signed int)
PPL_SPECIALIZE_CONSTRUCT(construct_mpq_base, mpq_class, signed long)
PPL_SPECIALIZE_CONSTRUCT(construct_mpq_base, mpq_class, unsigned char)
PPL_SPECIALIZE_CONSTRUCT(construct_mpq_base, mpq_class, unsigned short)
PPL_SPECIALIZE_CONSTRUCT(construct_mpq_base, mpq_class, unsigned int)
PPL_SPECIALIZE_CONSTRUCT(construct_mpq_base, mpq_class, unsigned long)

template <typename To_Policy, typename From_Policy, typename From>
inline Result
construct_mpq_float(mpq_class& to, const From& from, Rounding_Dir dir) {
  if (is_nan<From_Policy>(from)) {
    return construct_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(from)) {
    return construct_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(from)) {
    return construct_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  new(&to) mpq_class(from);
  return V_EQ;
}

PPL_SPECIALIZE_CONSTRUCT(construct_mpq_float, mpq_class, float)
PPL_SPECIALIZE_CONSTRUCT(construct_mpq_float, mpq_class, double)

PPL_SPECIALIZE_ASSIGN(assign_exact, mpq_class, mpq_class)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpq_class, mpz_class)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpq_class, char)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpq_class, signed char)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpq_class, signed short)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpq_class, signed int)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpq_class, signed long)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpq_class, unsigned char)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpq_class, unsigned short)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpq_class, unsigned int)
PPL_SPECIALIZE_ASSIGN(assign_exact, mpq_class, unsigned long)

template <typename To_Policy, typename From_Policy, typename From>
inline Result
assign_mpq_float(mpq_class& to, const From& from, Rounding_Dir dir) {
  if (is_nan<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(from)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  assign_mpq_numeric_float(to, from);
  return V_EQ;
}

PPL_SPECIALIZE_ASSIGN(assign_mpq_float, mpq_class, float)
PPL_SPECIALIZE_ASSIGN(assign_mpq_float, mpq_class, double)
PPL_SPECIALIZE_ASSIGN(assign_mpq_float, mpq_class, long double)

template <typename To_Policy, typename From_Policy, typename From>
inline Result
assign_mpq_signed_int(mpq_class& to, const From from, Rounding_Dir) {
  if (sizeof(From) <= sizeof(signed long)) {
    to = static_cast<signed long>(from);
  }
  else {
    mpz_ptr m = to.get_num().get_mpz_t();
    if (from >= 0) {
      mpz_import(m, 1, 1, sizeof(From), 0, 0, &from);
    }
    else {
      From n = -from;
      mpz_import(m, 1, 1, sizeof(From), 0, 0, &n);
      mpz_neg(m, m);
    }
    to.get_den() = 1;
  }
  return V_EQ;
}

PPL_SPECIALIZE_ASSIGN(assign_mpq_signed_int, mpq_class, signed long long)

template <typename To_Policy, typename From_Policy, typename From>
inline Result
assign_mpq_unsigned_int(mpq_class& to, const From from, Rounding_Dir) {
  if (sizeof(From) <= sizeof(unsigned long)) {
    to = static_cast<unsigned long>(from);
  }
  else {
    mpz_import(to.get_num().get_mpz_t(), 1, 1, sizeof(From), 0, 0, &from);
    to.get_den() = 1;
  }
  return V_EQ;
}

PPL_SPECIALIZE_ASSIGN(assign_mpq_unsigned_int, mpq_class, unsigned long long)

template <typename To_Policy, typename From_Policy>
inline Result
floor_mpq(mpq_class& to, const mpq_class& from, Rounding_Dir) {
  mpz_fdiv_q(to.get_num().get_mpz_t(),
             from.get_num().get_mpz_t(), from.get_den().get_mpz_t());
  to.get_den() = 1;
  return V_EQ;
}

PPL_SPECIALIZE_FLOOR(floor_mpq, mpq_class, mpq_class)

template <typename To_Policy, typename From_Policy>
inline Result
ceil_mpq(mpq_class& to, const mpq_class& from, Rounding_Dir) {
  mpz_cdiv_q(to.get_num().get_mpz_t(),
             from.get_num().get_mpz_t(), from.get_den().get_mpz_t());
  to.get_den() = 1;
  return V_EQ;
}

PPL_SPECIALIZE_CEIL(ceil_mpq, mpq_class, mpq_class)

template <typename To_Policy, typename From_Policy>
inline Result
trunc_mpq(mpq_class& to, const mpq_class& from, Rounding_Dir) {
  mpz_tdiv_q(to.get_num().get_mpz_t(),
             from.get_num().get_mpz_t(), from.get_den().get_mpz_t());
  to.get_den() = 1;
  return V_EQ;
}

PPL_SPECIALIZE_TRUNC(trunc_mpq, mpq_class, mpq_class)

template <typename To_Policy, typename From_Policy>
inline Result
neg_mpq(mpq_class& to, const mpq_class& from, Rounding_Dir) {
  mpq_neg(to.get_mpq_t(), from.get_mpq_t());
  return V_EQ;
}

PPL_SPECIALIZE_NEG(neg_mpq, mpq_class, mpq_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
add_mpq(mpq_class& to, const mpq_class& x, const mpq_class& y, Rounding_Dir) {
  to = x + y;
  return V_EQ;
}

PPL_SPECIALIZE_ADD(add_mpq, mpq_class, mpq_class, mpq_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
sub_mpq(mpq_class& to, const mpq_class& x, const mpq_class& y, Rounding_Dir) {
  to = x - y;
  return V_EQ;
}

PPL_SPECIALIZE_SUB(sub_mpq, mpq_class, mpq_class, mpq_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
mul_mpq(mpq_class& to, const mpq_class& x, const mpq_class& y, Rounding_Dir) {
  to = x * y;
  return V_EQ;
}

PPL_SPECIALIZE_MUL(mul_mpq, mpq_class, mpq_class, mpq_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
div_mpq(mpq_class& to, const mpq_class& x, const mpq_class& y, Rounding_Dir) {
  if (CHECK_P(To_Policy::check_div_zero, sgn(y) == 0)) {
    return assign_nan<To_Policy>(to, V_DIV_ZERO);
  }
  to = x / y;
  return V_EQ;
}

PPL_SPECIALIZE_DIV(div_mpq, mpq_class, mpq_class, mpq_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
idiv_mpq(mpq_class& to, const mpq_class& x, const mpq_class& y, Rounding_Dir dir) {
  if (CHECK_P(To_Policy::check_div_zero, sgn(y) == 0)) {
    return assign_nan<To_Policy>(to, V_DIV_ZERO);
  }
  to = x / y;
  return trunc<To_Policy, To_Policy>(to, to, dir);
}

PPL_SPECIALIZE_IDIV(idiv_mpq, mpq_class, mpq_class, mpq_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
rem_mpq(mpq_class& to, const mpq_class& x, const mpq_class& y, Rounding_Dir) {
  if (CHECK_P(To_Policy::check_div_zero, sgn(y) == 0)) {
    return assign_nan<To_Policy>(to, V_MOD_ZERO);
  }
  PPL_DIRTY_TEMP(mpq_class, tmp);
  tmp = x / y;
  tmp.get_num() %= tmp.get_den();
  to = tmp * y;
  return V_EQ;
}

PPL_SPECIALIZE_REM(rem_mpq, mpq_class, mpq_class, mpq_class)

template <typename To_Policy, typename From_Policy>
inline Result
add_2exp_mpq(mpq_class& to, const mpq_class& x, unsigned int exp,
             Rounding_Dir) {
  PPL_DIRTY_TEMP(mpz_class, v);
  v = 1;
  mpz_mul_2exp(v.get_mpz_t(), v.get_mpz_t(), exp);
  to = x + v;
  return V_EQ;
}

PPL_SPECIALIZE_ADD_2EXP(add_2exp_mpq, mpq_class, mpq_class)

template <typename To_Policy, typename From_Policy>
inline Result
sub_2exp_mpq(mpq_class& to, const mpq_class& x, unsigned int exp,
             Rounding_Dir) {
  PPL_DIRTY_TEMP(mpz_class, v);
  v = 1;
  mpz_mul_2exp(v.get_mpz_t(), v.get_mpz_t(), exp);
  to = x - v;
  return V_EQ;
}

PPL_SPECIALIZE_SUB_2EXP(sub_2exp_mpq, mpq_class, mpq_class)

template <typename To_Policy, typename From_Policy>
inline Result
mul_2exp_mpq(mpq_class& to, const mpq_class& x, unsigned int exp,
             Rounding_Dir) {
  mpz_mul_2exp(to.get_num().get_mpz_t(), x.get_num().get_mpz_t(), exp);
  to.get_den() = x.get_den();
  to.canonicalize();
  return V_EQ;
}

PPL_SPECIALIZE_MUL_2EXP(mul_2exp_mpq, mpq_class, mpq_class)

template <typename To_Policy, typename From_Policy>
inline Result
div_2exp_mpq(mpq_class& to, const mpq_class& x, unsigned int exp,
             Rounding_Dir) {
  to.get_num() = x.get_num();
  mpz_mul_2exp(to.get_den().get_mpz_t(), x.get_den().get_mpz_t(), exp);
  to.canonicalize();
  return V_EQ;
}

PPL_SPECIALIZE_DIV_2EXP(div_2exp_mpq, mpq_class, mpq_class)

template <typename To_Policy, typename From_Policy>
inline Result
smod_2exp_mpq(mpq_class& to, const mpq_class& x, unsigned int exp,
              Rounding_Dir) {
  mpz_mul_2exp(to.get_den().get_mpz_t(), x.get_den().get_mpz_t(), exp);
  mpz_fdiv_r(to.get_num().get_mpz_t(), x.get_num().get_mpz_t(), to.get_den().get_mpz_t());
  mpz_fdiv_q_2exp(to.get_den().get_mpz_t(), to.get_den().get_mpz_t(), 1);
  bool neg = to.get_num() >= to.get_den();
  mpz_mul_2exp(to.get_den().get_mpz_t(), to.get_den().get_mpz_t(), 1);
  if (neg) {
    to.get_num() -= to.get_den();
  }
  mpz_mul_2exp(to.get_num().get_mpz_t(), to.get_num().get_mpz_t(), exp);
  to.canonicalize();
  return V_EQ;
}

PPL_SPECIALIZE_SMOD_2EXP(smod_2exp_mpq, mpq_class, mpq_class)

template <typename To_Policy, typename From_Policy>
inline Result
umod_2exp_mpq(mpq_class& to, const mpq_class& x, unsigned int exp,
              Rounding_Dir) {
  mpz_mul_2exp(to.get_den().get_mpz_t(), x.get_den().get_mpz_t(), exp);
  mpz_fdiv_r(to.get_num().get_mpz_t(), x.get_num().get_mpz_t(), to.get_den().get_mpz_t());
  mpz_mul_2exp(to.get_num().get_mpz_t(), to.get_num().get_mpz_t(), exp);
  to.canonicalize();
  return V_EQ;
}

PPL_SPECIALIZE_UMOD_2EXP(umod_2exp_mpq, mpq_class, mpq_class)

template <typename To_Policy, typename From_Policy>
inline Result
abs_mpq(mpq_class& to, const mpq_class& from, Rounding_Dir) {
  to = abs(from);
  return V_EQ;
}

PPL_SPECIALIZE_ABS(abs_mpq, mpq_class, mpq_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
add_mul_mpq(mpq_class& to, const mpq_class& x, const mpq_class& y,
            Rounding_Dir) {
  to += x * y;
  return V_EQ;
}

PPL_SPECIALIZE_ADD_MUL(add_mul_mpq, mpq_class, mpq_class, mpq_class)

template <typename To_Policy, typename From1_Policy, typename From2_Policy>
inline Result
sub_mul_mpq(mpq_class& to, const mpq_class& x, const mpq_class& y,
            Rounding_Dir) {
  to -= x * y;
  return V_EQ;
}

PPL_SPECIALIZE_SUB_MUL(sub_mul_mpq, mpq_class, mpq_class, mpq_class)

extern unsigned irrational_precision;

template <typename To_Policy, typename From_Policy>
inline Result
sqrt_mpq(mpq_class& to, const mpq_class& from, Rounding_Dir dir) {
  if (CHECK_P(To_Policy::check_sqrt_neg, from < 0)) {
    return assign_nan<To_Policy>(to, V_SQRT_NEG);
  }
  if (from == 0) {
    to = 0;
    return V_EQ;
  }
  bool gt1 = from.get_num() > from.get_den();
  const mpz_class& from_a = gt1 ? from.get_num() : from.get_den();
  const mpz_class& from_b = gt1 ? from.get_den() : from.get_num();
  mpz_class& to_a = gt1 ? to.get_num() : to.get_den();
  mpz_class& to_b = gt1 ? to.get_den() : to.get_num();
  Rounding_Dir rdir = gt1 ? dir : inverse(dir);
  mul_2exp<To_Policy, From_Policy>(to_a, from_a,
                                   2*irrational_precision, ROUND_IGNORE);
  Result r_div
    = div<To_Policy, To_Policy, To_Policy>(to_a, to_a, from_b, rdir);
  Result r_sqrt = sqrt<To_Policy, To_Policy>(to_a, to_a, rdir);
  to_b = 1;
  mul_2exp<To_Policy, To_Policy>(to_b, to_b,
                                 irrational_precision, ROUND_IGNORE);
  to.canonicalize();
  return (r_div != V_EQ) ? r_div : r_sqrt;
}

PPL_SPECIALIZE_SQRT(sqrt_mpq, mpq_class, mpq_class)

template <typename Policy>
inline Result
input_mpq(mpq_class& to, std::istream& is, Rounding_Dir dir) {
  Result r = input_mpq(to, is);
  Result_Class c = result_class(r);
  switch (c) {
  case VC_MINUS_INFINITY:
  case VC_PLUS_INFINITY:
    return assign_special<Policy>(to, c, dir);
  case VC_NAN:
    return assign_nan<Policy>(to, r);
  default:
    return r;
  }
}

PPL_SPECIALIZE_INPUT(input_mpq, mpq_class)

template <typename Policy>
inline Result
output_mpq(std::ostream& os,
           const mpq_class& from,
           const Numeric_Format&,
           Rounding_Dir) {
  os << from;
  return V_EQ;
}

PPL_SPECIALIZE_OUTPUT(output_mpq, mpq_class)

} // namespace Checked

//! Returns the precision parameter used for irrational calculations.
inline unsigned
irrational_precision() {
  return Checked::irrational_precision;
}

//! Sets the precision parameter used for irrational calculations.
/*! The lesser between numerator and denominator is limited to 2**\p p.

  If \p p is less than or equal to <CODE>INT_MAX</CODE>, sets the
  precision parameter used for irrational calculations to \p p.

  \exception std::invalid_argument
  Thrown if \p p is greater than <CODE>INT_MAX</CODE>.
*/
inline void
set_irrational_precision(const unsigned p) {
  if (p <= INT_MAX) {
    Checked::irrational_precision = p;
  }
  else {
    throw std::invalid_argument("PPL::set_irrational_precision(p)"
                                " with p > INT_MAX");
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/checked_ext_inlines.hh line 1. */
/* Checked extended arithmetic functions.
*/


namespace Parma_Polyhedra_Library {

template <typename T> struct FPU_Related : public False {};
template <> struct FPU_Related<float> : public True {};
template <> struct FPU_Related<double> : public True {};
template <> struct FPU_Related<long double> : public True {};

namespace Checked {

template <typename T>
inline bool
handle_ext_natively(const T&) {
  return FPU_Related<T>::value;
}

template <typename Policy, typename Type>
inline bool
ext_to_handle(const Type& x) {
  return !handle_ext_natively(x)
    && (Policy::has_infinity || Policy::has_nan);
}

template <typename Policy, typename Type>
inline Result_Relation
sgn_ext(const Type& x) {
  if (!ext_to_handle<Policy>(x)) {
    goto native;
  }
  if (is_nan<Policy>(x)) {
    return VR_EMPTY;
  }
  else if (is_minf<Policy>(x)) {
    return VR_LT;
  }
  else if (is_pinf<Policy>(x)) {
    return VR_GT;
  }
  else {
  native:
    return sgn<Policy>(x);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
construct_ext(To& to, const From& x, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return construct_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x)) {
    return construct_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(x)) {
    return construct_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
  native:
    return construct<To_Policy, From_Policy>(to, x, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
assign_ext(To& to, const From& x, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
  native:
    return assign<To_Policy, From_Policy>(to, x, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
neg_ext(To& to, const From& x, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else {
  native:
    return neg<To_Policy, From_Policy>(to, x, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
floor_ext(To& to, const From& x, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
  native:
    return floor<To_Policy, From_Policy>(to, x, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
ceil_ext(To& to, const From& x, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
  native:
    return ceil<To_Policy, From_Policy>(to, x, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
trunc_ext(To& to, const From& x, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
  native:
    return trunc<To_Policy, From_Policy>(to, x, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
abs_ext(To& to, const From& x, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x) || is_pinf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
  native:
    return abs<To_Policy, From_Policy>(to, x, dir);
  }
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
add_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  if (!ext_to_handle<From1_Policy>(x) && !ext_to_handle<From2_Policy>(y)) {
    goto native;
  }
  if (is_nan<From1_Policy>(x) || is_nan<From2_Policy>(y)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From1_Policy>(x)) {
    if (CHECK_P(To_Policy::check_inf_add_inf, is_pinf<From2_Policy>(y))) {
      goto inf_add_inf;
    }
    else {
      goto minf;
    }
  }
  else if (is_pinf<From1_Policy>(x)) {
    if (CHECK_P(To_Policy::check_inf_add_inf, is_minf<From2_Policy>(y))) {
    inf_add_inf:
      return assign_nan<To_Policy>(to, V_INF_ADD_INF);
    }
    else {
      goto pinf;
    }
  }
  else {
    if (is_minf<From2_Policy>(y)) {
    minf:
      return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
    }
    else if (is_pinf<From2_Policy>(y)) {
    pinf:
      return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
    }
    else {
    native:
      return add<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
    }
  }
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
sub_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  if (!ext_to_handle<From1_Policy>(x) && !ext_to_handle<From2_Policy>(y)) {
    goto native;
  }
  if (is_nan<From1_Policy>(x) || is_nan<From2_Policy>(y)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From1_Policy>(x)) {
    if (CHECK_P(To_Policy::check_inf_sub_inf, is_minf<From2_Policy>(y))) {
      goto inf_sub_inf;
    }
    else {
      goto minf;
    }
  }
  else if (is_pinf<From1_Policy>(x)) {
    if (CHECK_P(To_Policy::check_inf_sub_inf, is_pinf<From2_Policy>(y))) {
    inf_sub_inf:
      return assign_nan<To_Policy>(to, V_INF_SUB_INF);
    }
    else {
      goto pinf;
    }
  }
  else {
    if (is_pinf<From2_Policy>(y)) {
    minf:
      return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
    }
    else if (is_minf<From2_Policy>(y)) {
    pinf:
      return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
    }
    else {
    native:
      return sub<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
    }
  }
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
mul_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  if (!ext_to_handle<From1_Policy>(x) && !ext_to_handle<From2_Policy>(y)) {
    goto native;
  }
  if (is_nan<From1_Policy>(x) || is_nan<From2_Policy>(y)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  if (is_minf<From1_Policy>(x)) {
    switch (sgn_ext<From2_Policy>(y)) {
    case VR_LT:
      goto pinf;
    case VR_GT:
      goto minf;
    default:
      goto inf_mul_zero;
    }
  }
  else if (is_pinf<From1_Policy>(x)) {
    switch (sgn_ext<From2_Policy>(y)) {
    case VR_LT:
      goto minf;
    case VR_GT:
      goto pinf;
    default:
      goto inf_mul_zero;
    }
  }
  else {
    if (is_minf<From2_Policy>(y)) {
      switch (sgn<From1_Policy>(x)) {
      case VR_LT:
        goto pinf;
      case VR_GT:
        goto minf;
      default:
        goto inf_mul_zero;
      }
    }
    else if (is_pinf<From2_Policy>(y)) {
      switch (sgn<From1_Policy>(x)) {
      case VR_LT:
      minf:
        return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
      case VR_GT:
      pinf:
        return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
      default:
      inf_mul_zero:
        PPL_ASSERT(To_Policy::check_inf_mul_zero);
        return assign_nan<To_Policy>(to, V_INF_MUL_ZERO);
      }
    }
    else {
    native:
      return mul<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
    }
  }
}


template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
add_mul_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  if (!ext_to_handle<To_Policy>(to)
      && !ext_to_handle<From1_Policy>(x) && !ext_to_handle<From2_Policy>(y)) {
    goto native;
  }
  if (is_nan<To_Policy>(to)
      || is_nan<From1_Policy>(x) || is_nan<From2_Policy>(y)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  if (is_minf<From1_Policy>(x)) {
    switch (sgn_ext<From2_Policy>(y)) {
    case VR_LT:
      goto a_pinf;
    case VR_GT:
      goto a_minf;
    default:
      goto inf_mul_zero;
    }
  }
  else if (is_pinf<From1_Policy>(x)) {
    switch (sgn_ext<From2_Policy>(y)) {
    case VR_LT:
      goto a_minf;
    case VR_GT:
      goto a_pinf;
    default:
      goto inf_mul_zero;
    }
  }
  else {
    if (is_minf<From2_Policy>(y)) {
      switch (sgn<From1_Policy>(x)) {
      case VR_LT:
        goto a_pinf;
      case VR_GT:
        goto a_minf;
      default:
        goto inf_mul_zero;
      }
    }
    else if (is_pinf<From2_Policy>(y)) {
      switch (sgn<From1_Policy>(x)) {
      case VR_LT:
      a_minf:
        if (CHECK_P(To_Policy::check_inf_add_inf, is_pinf<To_Policy>(to))) {
          goto inf_add_inf;
        }
        else {
          goto minf;
        }
      case VR_GT:
      a_pinf:
        if (CHECK_P(To_Policy::check_inf_add_inf, is_minf<To_Policy>(to))) {
        inf_add_inf:
          return assign_nan<To_Policy>(to, V_INF_ADD_INF);
        }
        else {
          goto pinf;
        }
      default:
      inf_mul_zero:
        PPL_ASSERT(To_Policy::check_inf_mul_zero);
        return assign_nan<To_Policy>(to, V_INF_MUL_ZERO);
      }
    }
    else {
      if (is_minf<To_Policy>(to)) {
      minf:
        return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
      }
      if (is_pinf<To_Policy>(to)) {
      pinf:
        return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
      }
    native:
      return add_mul<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
    }
  }
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
sub_mul_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  if (!ext_to_handle<To_Policy>(to)
      && !ext_to_handle<From1_Policy>(x) && !ext_to_handle<From2_Policy>(y)) {
    goto native;
  }
  if (is_nan<To_Policy>(to)
      || is_nan<From1_Policy>(x) || is_nan<From2_Policy>(y)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  if (is_minf<From1_Policy>(x)) {
    switch (sgn_ext<From2_Policy>(y)) {
    case VR_LT:
      goto a_pinf;
    case VR_GT:
      goto a_minf;
    default:
      goto inf_mul_zero;
    }
  }
  else if (is_pinf<From1_Policy>(x)) {
    switch (sgn_ext<From2_Policy>(y)) {
    case VR_LT:
      goto a_minf;
    case VR_GT:
      goto a_pinf;
    default:
      goto inf_mul_zero;
    }
  }
  else {
    if (is_minf<From2_Policy>(y)) {
      switch (sgn<From1_Policy>(x)) {
      case VR_LT:
        goto a_pinf;
      case VR_GT:
        goto a_minf;
      default:
        goto inf_mul_zero;
      }
    }
    else if (is_pinf<From2_Policy>(y)) {
      switch (sgn<From1_Policy>(x)) {
      case VR_LT:
      a_minf:
        if (CHECK_P(To_Policy::check_inf_sub_inf, is_minf<To_Policy>(to))) {
          goto inf_sub_inf;
        }
        else {
          goto pinf;
        }
      case VR_GT:
      a_pinf:
        if (CHECK_P(To_Policy::check_inf_sub_inf, is_pinf<To_Policy>(to))) {
        inf_sub_inf:
          return assign_nan<To_Policy>(to, V_INF_SUB_INF);
        }
        else {
          goto minf;
        }
      default:
      inf_mul_zero:
        PPL_ASSERT(To_Policy::check_inf_mul_zero);
        return assign_nan<To_Policy>(to, V_INF_MUL_ZERO);
      }
    }
    else {
      if (is_minf<To_Policy>(to)) {
      minf:
        return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
      }
      if (is_pinf<To_Policy>(to)) {
      pinf:
        return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
      }
    native:
      return sub_mul<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
    }
  }
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
div_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  if (!ext_to_handle<From1_Policy>(x) && !ext_to_handle<From2_Policy>(y)) {
    goto native;
  }
  if (is_nan<From1_Policy>(x) || is_nan<From2_Policy>(y)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  if (is_minf<From1_Policy>(x)) {
    if (CHECK_P(To_Policy::check_inf_div_inf, is_minf<From2_Policy>(y)
                || is_pinf<From2_Policy>(y))) {
      goto inf_div_inf;
    }
    else {
      switch (sgn<From2_Policy>(y)) {
      case VR_LT:
        goto pinf;
      case VR_GT:
        goto minf;
      default:
        goto div_zero;
      }
    }
  }
  else if (is_pinf<From1_Policy>(x)) {
    if (CHECK_P(To_Policy::check_inf_div_inf, is_minf<From2_Policy>(y)
                || is_pinf<From2_Policy>(y))) {
    inf_div_inf:
      return assign_nan<To_Policy>(to, V_INF_DIV_INF);
    }
    else {
      switch (sgn<From2_Policy>(y)) {
      case VR_LT:
      minf:
        return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
      case VR_GT:
      pinf:
        return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
      default:
      div_zero:
        PPL_ASSERT(To_Policy::check_div_zero);
        return assign_nan<To_Policy>(to, V_DIV_ZERO);
      }
    }
  }
  else {
    if (is_minf<From2_Policy>(y) || is_pinf<From2_Policy>(y)) {
      to = 0;
      return V_EQ;
    }
    else {
    native:
      return div<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
    }
  }
}


template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
idiv_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  if (!ext_to_handle<From1_Policy>(x) && !ext_to_handle<From2_Policy>(y)) {
    goto native;
  }
  if (is_nan<From1_Policy>(x) || is_nan<From2_Policy>(y)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  if (is_minf<From1_Policy>(x)) {
    if (CHECK_P(To_Policy::check_inf_div_inf, is_minf<From2_Policy>(y)
                || is_pinf<From2_Policy>(y))) {
      goto inf_div_inf;
    }
    else {
      switch (sgn<From2_Policy>(y)) {
      case VR_LT:
        goto pinf;
      case VR_GT:
        goto minf;
      default:
        goto div_zero;
      }
    }
  }
  else if (is_pinf<From1_Policy>(x)) {
    if (CHECK_P(To_Policy::check_inf_div_inf, is_minf<From2_Policy>(y)
                || is_pinf<From2_Policy>(y))) {
    inf_div_inf:
      return assign_nan<To_Policy>(to, V_INF_DIV_INF);
    }
    else {
      switch (sgn<From2_Policy>(y)) {
      case VR_LT:
      minf:
        return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
      case VR_GT:
      pinf:
        return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
      default:
      div_zero:
        PPL_ASSERT(To_Policy::check_div_zero);
        return assign_nan<To_Policy>(to, V_DIV_ZERO);
      }
    }
  }
  else {
    if (is_minf<From2_Policy>(y) || is_pinf<From2_Policy>(y)) {
      to = 0;
      return V_EQ;
    }
    else {
    native:
      return idiv<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
    }
  }
}


template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
rem_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  if (!ext_to_handle<From1_Policy>(x) && !ext_to_handle<From2_Policy>(y)) {
    goto native;
  }
  if (is_nan<From1_Policy>(x) || is_nan<From2_Policy>(y)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (CHECK_P(To_Policy::check_inf_mod, is_minf<From1_Policy>(x)
                   || is_pinf<From1_Policy>(x))) {
    return assign_nan<To_Policy>(to, V_INF_MOD);
  }
  else {
    if (is_minf<From1_Policy>(y) || is_pinf<From2_Policy>(y)) {
      to = x;
      return V_EQ;
    }
    else {
    native:
      return rem<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
    }
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
add_2exp_ext(To& to, const From& x, unsigned int exp, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
  native:
    return add_2exp<To_Policy, From_Policy>(to, x, exp, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
sub_2exp_ext(To& to, const From& x, unsigned int exp, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
  native:
    return sub_2exp<To_Policy, From_Policy>(to, x, exp, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
mul_2exp_ext(To& to, const From& x, unsigned int exp, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
  native:
    return mul_2exp<To_Policy, From_Policy>(to, x, exp, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
div_2exp_ext(To& to, const From& x, unsigned int exp, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_MINUS_INFINITY, dir);
  }
  else if (is_pinf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
  native:
    return div_2exp<To_Policy, From_Policy>(to, x, exp, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
smod_2exp_ext(To& to, const From& x, unsigned int exp, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (CHECK_P(To_Policy::check_inf_mod, is_minf<From_Policy>(x)
                   || is_pinf<From_Policy>(x))) {
    return assign_nan<To_Policy>(to, V_INF_MOD);
  }
  else {
  native:
    return smod_2exp<To_Policy, From_Policy>(to, x, exp, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
umod_2exp_ext(To& to, const From& x, unsigned int exp, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (CHECK_P(To_Policy::check_inf_mod, is_minf<From_Policy>(x)
                   || is_pinf<From_Policy>(x))) {
    return assign_nan<To_Policy>(to, V_INF_MOD);
  }
  else {
  native:
    return umod_2exp<To_Policy, From_Policy>(to, x, exp, dir);
  }
}

template <typename To_Policy, typename From_Policy,
          typename To, typename From>
inline Result
sqrt_ext(To& to, const From& x, Rounding_Dir dir) {
  if (!ext_to_handle<From_Policy>(x)) {
    goto native;
  }
  if (is_nan<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From_Policy>(x)) {
    return assign_nan<To_Policy>(to, V_SQRT_NEG);
  }
  else if (is_pinf<From_Policy>(x)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
  native:
    return sqrt<To_Policy, From_Policy>(to, x, dir);
  }
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
gcd_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  if (is_nan<From1_Policy>(x) || is_nan<From2_Policy>(y)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From1_Policy>(x) || is_pinf<From1_Policy>(x)) {
    return abs_ext<To_Policy, From2_Policy>(to, y, dir);
  }
  else if (is_minf<From2_Policy>(y) || is_pinf<From2_Policy>(y)) {
    return abs_ext<To_Policy, From1_Policy>(to, x, dir);
  }
  else {
    return gcd<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
  }
}

template <typename To1_Policy, typename To2_Policy, typename To3_Policy,
          typename From1_Policy, typename From2_Policy,
          typename To1, typename To2, typename To3,
          typename From1, typename From2>
inline Result
gcdext_ext(To1& to, To2& s, To3& t, const From1& x, const From2& y,
           Rounding_Dir dir) {
  if (is_nan<From1_Policy>(x) || is_nan<From2_Policy>(y)) {
    return assign_special<To1_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From1_Policy>(x) || is_pinf<From1_Policy>(x)) {
    s = 0;
    t = y > 0 ? -1 : 1;
    return abs_ext<To1_Policy, From2_Policy>(to, y, dir);
  }
  else if (is_minf<From2_Policy>(y) || is_pinf<From2_Policy>(y)) {
    s = x > 0 ? -1 : 1;
    t = 0;
    return abs_ext<To1_Policy, From1_Policy>(to, x, dir);
  }
  else {
    return gcdext<To1_Policy, To2_Policy, To3_Policy, From1_Policy, From2_Policy>(to, s, t, x, y, dir);
  }
}

template <typename To_Policy, typename From1_Policy, typename From2_Policy,
          typename To, typename From1, typename From2>
inline Result
lcm_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) {
  if (is_nan<From1_Policy>(x) || is_nan<From2_Policy>(y)) {
    return assign_special<To_Policy>(to, VC_NAN, ROUND_IGNORE);
  }
  else if (is_minf<From1_Policy>(x) || is_pinf<From1_Policy>(x)
           || is_minf<From2_Policy>(y) || is_pinf<From2_Policy>(y)) {
    return assign_special<To_Policy>(to, VC_PLUS_INFINITY, dir);
  }
  else {
    return lcm<To_Policy, From1_Policy, From2_Policy>(to, x, y, dir);
  }
}

template <typename Policy1, typename Policy2,
          typename Type1, typename Type2>
inline Result_Relation
cmp_ext(const Type1& x, const Type2& y) {
  if (!ext_to_handle<Policy1>(x) && !ext_to_handle<Policy2>(y)) {
    goto native;
  }
  if (is_nan<Policy1>(x) || is_nan<Policy2>(y)) {
    return VR_EMPTY;
  }
  else if (is_minf<Policy1>(x)) {
    return is_minf<Policy2>(y) ? VR_EQ : VR_LT;
  }
  else if (is_pinf<Policy1>(x)) {
    return is_pinf<Policy2>(y) ? VR_EQ : VR_GT;
  }
  else {
    if (is_minf<Policy2>(y)) {
      return VR_GT;
    }
    if (is_pinf<Policy2>(y)) {
      return VR_LT;
    }
  native:
    return cmp<Policy1, Policy2>(x, y);
  }
}

template <typename Policy1, typename Policy2,
          typename Type1, typename Type2>
inline bool
lt_ext(const Type1& x, const Type2& y) {
  if (!ext_to_handle<Policy1>(x) && !ext_to_handle<Policy2>(y)) {
    goto native;
  }
  if (is_nan<Policy1>(x) || is_nan<Policy2>(y)) {
    return false;
  }
  if (is_pinf<Policy1>(x) || is_minf<Policy2>(y)) {
    return false;
  }
  if (is_minf<Policy1>(x) || is_pinf<Policy2>(y)) {
    return true;
  }
 native:
  return lt_p<Policy1, Policy2>(x, y);
}

template <typename Policy1, typename Policy2,
          typename Type1, typename Type2>
inline bool
gt_ext(const Type1& x, const Type2& y) {
  return lt_ext<Policy1, Policy2>(y, x);
}

template <typename Policy1, typename Policy2,
          typename Type1, typename Type2>
inline bool
le_ext(const Type1& x, const Type2& y) {
  if (!ext_to_handle<Policy1>(x) && !ext_to_handle<Policy2>(y)) {
    goto native;
  }
  if (is_nan<Policy1>(x) || is_nan<Policy2>(y)) {
    return false;
  }
  if (is_minf<Policy1>(x) || is_pinf<Policy2>(y)) {
    return true;
  }
  if (is_pinf<Policy1>(x) || is_minf<Policy2>(y)) {
    return false;
  }
 native:
  return le_p<Policy1, Policy2>(x, y);
}

template <typename Policy1, typename Policy2,
          typename Type1, typename Type2>
inline bool
ge_ext(const Type1& x, const Type2& y) {
  return le_ext<Policy1, Policy2>(y, x);
}

template <typename Policy1, typename Policy2,
          typename Type1, typename Type2>
inline bool
eq_ext(const Type1& x, const Type2& y) {
  if (!ext_to_handle<Policy1>(x) && !ext_to_handle<Policy2>(y)) {
    goto native;
  }
  if (is_nan<Policy1>(x) || is_nan<Policy2>(y)) {
    return false;
  }
  if (is_minf<Policy1>(x)) {
    return is_minf<Policy2>(y);
  }
  if (is_pinf<Policy1>(x)) {
    return is_pinf<Policy2>(y);
  }
  else if (is_minf<Policy2>(y) || is_pinf<Policy2>(y)) {
    return false;
  }
 native:
  return eq_p<Policy1, Policy2>(x, y);
}

template <typename Policy1, typename Policy2,
          typename Type1, typename Type2>
inline bool
ne_ext(const Type1& x, const Type2& y) {
  return !eq_ext<Policy1, Policy2>(x, y);
}

template <typename Policy, typename Type>
inline Result
output_ext(std::ostream& os, const Type& x,
           const Numeric_Format& format, Rounding_Dir dir) {
  if (!ext_to_handle<Policy>(x)) {
    goto native;
  }
  if (is_nan<Policy>(x)) {
    os << "nan";
    return V_NAN;
  }
  if (is_minf<Policy>(x)) {
    os << "-inf";
    return V_EQ;
  }
  if (is_pinf<Policy>(x)) {
    os << "+inf";
    return V_EQ;
  }
 native:
  return output<Policy>(os, x, format, dir);
}

template <typename To_Policy, typename To>
inline Result
input_ext(To& to, std::istream& is, Rounding_Dir dir) {
  return input<To_Policy>(to, is, dir);
}

} // namespace Checked

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/checked_defs.hh line 706. */

#undef nonconst
#ifdef PPL_SAVED_nonconst
#define nonconst PPL_SAVED_nonconst
#undef PPL_SAVED_nonconst
#endif

#undef PPL_FUNCTION_CLASS
#undef PPL_NAN

/* Automatically generated from PPL source file ../src/Checked_Number_defs.hh line 31. */
#include <iosfwd>

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct Extended_Number_Policy {
  const_bool_nodef(check_overflow, true);
  const_bool_nodef(check_inf_add_inf, false);
  const_bool_nodef(check_inf_sub_inf, false);
  const_bool_nodef(check_inf_mul_zero, false);
  const_bool_nodef(check_div_zero, false);
  const_bool_nodef(check_inf_div_inf, false);
  const_bool_nodef(check_inf_mod, false);
  const_bool_nodef(check_sqrt_neg, false);
  const_bool_nodef(has_nan, true);
  const_bool_nodef(has_infinity, true);

  // `convertible' is intentionally not defined: the compile time
  // error on conversions is the expected behavior.

  const_bool_nodef(fpu_check_inexact, true);
  const_bool_nodef(fpu_check_nan_result, true);

  // ROUND_DEFAULT_CONSTRUCTOR is intentionally not defined.
  // ROUND_DEFAULT_OPERATOR is intentionally not defined.
  // ROUND_DEFAULT_FUNCTION is intentionally not defined.
  // ROUND_DEFAULT_INPUT is intentionally not defined.
  // ROUND_DEFAULT_OUTPUT is intentionally not defined.

  static void handle_result(Result r);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A policy checking for overflows.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
struct Check_Overflow_Policy {
  const_bool_nodef(check_overflow, true);
  const_bool_nodef(check_inf_add_inf, false);
  const_bool_nodef(check_inf_sub_inf, false);
  const_bool_nodef(check_inf_mul_zero, false);
  const_bool_nodef(check_div_zero, false);
  const_bool_nodef(check_inf_div_inf, false);
  const_bool_nodef(check_inf_mod, false);
  const_bool_nodef(check_sqrt_neg, false);
  const_bool_nodef(has_nan, std::numeric_limits<T>::has_quiet_NaN);
  const_bool_nodef(has_infinity, std::numeric_limits<T>::has_infinity);
  const_bool_nodef(convertible, true);
  const_bool_nodef(fpu_check_inexact, true);
  const_bool_nodef(fpu_check_nan_result, true);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T, typename Enable = void>
struct Native_Checked_From_Wrapper;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
struct Native_Checked_From_Wrapper<T, typename Enable_If<Is_Native<T>::value>::type> {
  typedef Checked_Number_Transparent_Policy<T> Policy;
  static const T& raw_value(const T& v) {
    return v;
  }
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T, typename P>
struct Native_Checked_From_Wrapper<Checked_Number<T, P> > {
  typedef P Policy;
  static const T& raw_value(const Checked_Number<T, P>& v) {
    return v.raw_value();
  }
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T, typename Enable = void>
struct Native_Checked_To_Wrapper;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
struct Native_Checked_To_Wrapper<T, typename Enable_If<Is_Native<T>::value>::type> {
  typedef Check_Overflow_Policy<T> Policy;
  static T& raw_value(T& v) {
    return v;
  }
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T, typename P>
struct Native_Checked_To_Wrapper<Checked_Number<T, P> > {
  typedef P Policy;
  static T& raw_value(Checked_Number<T, P>& v) {
    return v.raw_value();
  }
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
struct Is_Checked : public False { };

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T, typename P>
struct Is_Checked<Checked_Number<T, P> > : public True { };

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
struct Is_Native_Or_Checked
  : public Bool<Is_Native<T>::value || Is_Checked<T>::value> { };

//! A wrapper for numeric types implementing a given policy.
/*! \ingroup PPL_CXX_interface
  The wrapper and related functions implement an interface which is common
  to all kinds of coefficient types, therefore allowing for a uniform
  coding style. This class also implements the policy encoded by the
  second template parameter. The default policy is to perform the detection
  of overflow errors.
*/
template <typename T, typename Policy>
class Checked_Number {
public:

  //! \name Constructors
  //@{

  //! Default constructor.
  Checked_Number();

  //! Copy constructor.
  Checked_Number(const Checked_Number& y);

  //! Direct initialization from a Checked_Number and rounding mode.
  template <typename From, typename From_Policy>
  Checked_Number(const Checked_Number<From, From_Policy>& y, Rounding_Dir dir);

  //! Direct initialization from a plain char and rounding mode.
  Checked_Number(char y, Rounding_Dir dir);

  //! Direct initialization from a signed char and rounding mode.
  Checked_Number(signed char y, Rounding_Dir dir);

  //! Direct initialization from a signed short and rounding mode.
  Checked_Number(signed short y, Rounding_Dir dir);

  //! Direct initialization from a signed int and rounding mode.
  Checked_Number(signed int y, Rounding_Dir dir);

  //! Direct initialization from a signed long and rounding mode.
  Checked_Number(signed long y, Rounding_Dir dir);

  //! Direct initialization from a signed long long and rounding mode.
  Checked_Number(signed long long y, Rounding_Dir dir);

  //! Direct initialization from an unsigned char and rounding mode.
  Checked_Number(unsigned char y, Rounding_Dir dir);

  //! Direct initialization from an unsigned short and rounding mode.
  Checked_Number(unsigned short y, Rounding_Dir dir);

  //! Direct initialization from an unsigned int and rounding mode.
  Checked_Number(unsigned int y, Rounding_Dir dir);

  //! Direct initialization from an unsigned long and rounding mode.
  Checked_Number(unsigned long y, Rounding_Dir dir);

  //! Direct initialization from an unsigned long long and rounding mode.
  Checked_Number(unsigned long long y, Rounding_Dir dir);

#if PPL_SUPPORTED_FLOAT
  //! Direct initialization from a float and rounding mode.
  Checked_Number(float y, Rounding_Dir dir);
#endif

#if PPL_SUPPORTED_DOUBLE
  //! Direct initialization from a double and rounding mode.
  Checked_Number(double y, Rounding_Dir dir);
#endif

#if PPL_SUPPORTED_LONG_DOUBLE
  //! Direct initialization from a long double and rounding mode.
  Checked_Number(long double y, Rounding_Dir dir);
#endif

  //! Direct initialization from a rational and rounding mode.
  Checked_Number(const mpq_class& y, Rounding_Dir dir);

  //! Direct initialization from an unbounded integer and rounding mode.
  Checked_Number(const mpz_class& y, Rounding_Dir dir);

  //! Direct initialization from a C string and rounding mode.
  Checked_Number(const char* y, Rounding_Dir dir);

  //! Direct initialization from special and rounding mode.
  template <typename From>
  Checked_Number(const From&, Rounding_Dir dir,
                 typename Enable_If<Is_Special<From>::value, bool>::type
                 ignored = false);

  //! Direct initialization from a Checked_Number, default rounding mode.
  template <typename From, typename From_Policy>
  explicit Checked_Number(const Checked_Number<From, From_Policy>& y);

  //! Direct initialization from a plain char, default rounding mode.
  Checked_Number(char y);

  //! Direct initialization from a signed char, default rounding mode.
  Checked_Number(signed char y);

  //! Direct initialization from a signed short, default rounding mode.
  Checked_Number(signed short y);

  //! Direct initialization from a signed int, default rounding mode.
  Checked_Number(signed int y);

  //! Direct initialization from a signed long, default rounding mode.
  Checked_Number(signed long y);

  //! Direct initialization from a signed long long, default rounding mode.
  Checked_Number(signed long long y);

  //! Direct initialization from an unsigned char, default rounding mode.
  Checked_Number(unsigned char y);

  //! Direct initialization from an unsigned short, default rounding mode.
  Checked_Number(unsigned short y);

  //! Direct initialization from an unsigned int, default rounding mode.
  Checked_Number(unsigned int y);

  //! Direct initialization from an unsigned long, default rounding mode.
  Checked_Number(unsigned long y);

  //! Direct initialization from an unsigned long long, default rounding mode.
  Checked_Number(unsigned long long y);

  //! Direct initialization from a float, default rounding mode.
  Checked_Number(float y);

  //! Direct initialization from a double, default rounding mode.
  Checked_Number(double y);

  //! Direct initialization from a long double, default rounding mode.
  Checked_Number(long double y);

  //! Direct initialization from a rational, default rounding mode.
  Checked_Number(const mpq_class& y);

  //! Direct initialization from an unbounded integer, default rounding mode.
  Checked_Number(const mpz_class& y);

  //! Direct initialization from a C string, default rounding mode.
  Checked_Number(const char* y);

  //! Direct initialization from special, default rounding mode
  template <typename From>
  Checked_Number(const From&, typename Enable_If<Is_Special<From>::value, bool>::type ignored = false);


  //@} // Constructors

  //! \name Accessors and Conversions
  //@{

  //! Conversion operator: returns a copy of the underlying numeric value.
  operator T() const;

  //! Returns a reference to the underlying numeric value.
  T& raw_value();

  //! Returns a const reference to the underlying numeric value.
  const T& raw_value() const;

  //@} // Accessors and Conversions

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  //! Classifies *this.
  /*!
    Returns the appropriate Result characterizing:
    - whether \p *this is NaN,
      if \p nan is <CODE>true</CODE>;
    - whether \p *this is a (positive or negative) infinity,
      if \p inf is <CODE>true</CODE>;
    - the sign of \p *this,
      if \p sign is <CODE>true</CODE>.
  */
  Result classify(bool nan = true, bool inf = true, bool sign = true) const;

  //! \name Assignment Operators
  //@{

  //! Assignment operator.
  Checked_Number& operator=(const Checked_Number& y);

  //! Assignment operator.
  template <typename From>
  Checked_Number& operator=(const From& y);

  //! Add and assign operator.
  template <typename From_Policy>
  Checked_Number& operator+=(const Checked_Number<T, From_Policy>& y);

  //! Add and assign operator.
  Checked_Number& operator+=(const T& y);

  //! Add and assign operator.
  template <typename From>
  typename Enable_If<Is_Native_Or_Checked<From>::value,
                     Checked_Number<T, Policy>&>::type
  operator+=(const From& y);

  //! Subtract and assign operator.
  template <typename From_Policy>
  Checked_Number& operator-=(const Checked_Number<T, From_Policy>& y);

  //! Subtract and assign operator.
  Checked_Number& operator-=(const T& y);

  //! Subtract and assign operator.
  template <typename From>
  typename Enable_If<Is_Native_Or_Checked<From>::value,
                     Checked_Number<T, Policy>&>::type
  operator-=(const From& y);

  //! Multiply and assign operator.
  template <typename From_Policy>
  Checked_Number& operator*=(const Checked_Number<T, From_Policy>& y);

  //! Multiply and assign operator.
  Checked_Number& operator*=(const T& y);

  //! Multiply and assign operator.
  template <typename From>
  typename Enable_If<Is_Native_Or_Checked<From>::value,
                     Checked_Number<T, Policy>&>::type
  operator*=(const From& y);

  //! Divide and assign operator.
  template <typename From_Policy>
  Checked_Number& operator/=(const Checked_Number<T, From_Policy>& y);

  //! Divide and assign operator.
  Checked_Number& operator/=(const T& y);

  //! Divide and assign operator.
  template <typename From>
  typename Enable_If<Is_Native_Or_Checked<From>::value,
                     Checked_Number<T, Policy>&>::type
  operator/=(const From& y);

  //! Compute remainder and assign operator.
  template <typename From_Policy>
  Checked_Number& operator%=(const Checked_Number<T, From_Policy>& y);

  //! Compute remainder and assign operator.
  Checked_Number& operator%=(const T& y);

  //! Compute remainder and assign operator.
  template <typename From>
  typename Enable_If<Is_Native_Or_Checked<From>::value,
                     Checked_Number<T, Policy>& >::type
  operator%=(const From& y);

  //@} // Assignment Operators


  //! \name Increment and Decrement Operators
  //@{

  //! Pre-increment operator.
  Checked_Number& operator++();

  //! Post-increment operator.
  Checked_Number  operator++(int);

  //! Pre-decrement operator.
  Checked_Number& operator--();

  //! Post-decrement operator.
  Checked_Number  operator--(int);

  //@} // Increment and Decrement Operators

private:
  //! The underlying numeric value.
  T v;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T, typename P>
struct Slow_Copy<Checked_Number<T, P> > : public Bool<Slow_Copy<T>::value> {};

/*! \relates Checked_Number */
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_not_a_number(const T& x);

/*! \relates Checked_Number */
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_minus_infinity(const T& x);

/*! \relates Checked_Number */
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_plus_infinity(const T& x);

/*! \relates Checked_Number */
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, int>::type
infinity_sign(const T& x);

/*! \relates Checked_Number */
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_integer(const T& x);

/*! \relates Checked_Number */
template <typename To, typename From>
typename Enable_If<Is_Native_Or_Checked<To>::value && Is_Special<From>::value, Result>::type
construct(To& to, const From& x, Rounding_Dir dir);

/*! \relates Checked_Number */
template <typename To, typename From>
typename Enable_If<Is_Native_Or_Checked<To>::value && Is_Special<From>::value, Result>::type
assign_r(To& to, const From& x, Rounding_Dir dir);

/*! \relates Checked_Number */
template <typename To>
typename Enable_If<Is_Native_Or_Checked<To>::value, Result>::type
assign_r(To& to, const char* x, Rounding_Dir dir);

/*! \relates Checked_Number */
template <typename To, typename To_Policy>
typename Enable_If<Is_Native_Or_Checked<To>::value, Result>::type
assign_r(To& to, char* x, Rounding_Dir dir);

#define PPL_DECLARE_FUNC1_A(name) \
template <typename To, typename From> \
typename Enable_If<Is_Native_Or_Checked<To>::value \
                   && Is_Native_Or_Checked<From>::value, \
                   Result>::type \
 PPL_U(name)(To& to, const From& x, Rounding_Dir dir);

PPL_DECLARE_FUNC1_A(assign_r)
PPL_DECLARE_FUNC1_A(floor_assign_r)
PPL_DECLARE_FUNC1_A(ceil_assign_r)
PPL_DECLARE_FUNC1_A(trunc_assign_r)
PPL_DECLARE_FUNC1_A(neg_assign_r)
PPL_DECLARE_FUNC1_A(abs_assign_r)
PPL_DECLARE_FUNC1_A(sqrt_assign_r)

#undef PPL_DECLARE_FUNC1_A

#define PPL_DECLARE_FUNC1_B(name) \
template <typename To, typename From> \
typename Enable_If<Is_Native_Or_Checked<To>::value \
                   && Is_Native_Or_Checked<From>::value, \
                   Result>::type \
 PPL_U(name)(To& to, const From& x, unsigned int exp, Rounding_Dir dir);

PPL_DECLARE_FUNC1_B(add_2exp_assign_r)
PPL_DECLARE_FUNC1_B(sub_2exp_assign_r)
PPL_DECLARE_FUNC1_B(mul_2exp_assign_r)
PPL_DECLARE_FUNC1_B(div_2exp_assign_r)
PPL_DECLARE_FUNC1_B(smod_2exp_assign_r)
PPL_DECLARE_FUNC1_B(umod_2exp_assign_r)

#undef PPL_DECLARE_FUNC1_B

#define PPL_DECLARE_FUNC2(name) \
template <typename To, typename From1, typename From2> \
typename Enable_If<Is_Native_Or_Checked<To>::value \
                   && Is_Native_Or_Checked<From1>::value \
                   && Is_Native_Or_Checked<From2>::value, \
                   Result>::type \
 PPL_U(name)(To& to, const From1& x, const From2& y, Rounding_Dir dir);

PPL_DECLARE_FUNC2(add_assign_r)
PPL_DECLARE_FUNC2(sub_assign_r)
PPL_DECLARE_FUNC2(mul_assign_r)
PPL_DECLARE_FUNC2(div_assign_r)
PPL_DECLARE_FUNC2(idiv_assign_r)
PPL_DECLARE_FUNC2(rem_assign_r)
PPL_DECLARE_FUNC2(gcd_assign_r)
PPL_DECLARE_FUNC2(lcm_assign_r)
PPL_DECLARE_FUNC2(add_mul_assign_r)
PPL_DECLARE_FUNC2(sub_mul_assign_r)

#undef PPL_DECLARE_FUNC2

#define PPL_DECLARE_FUNC4(name) \
template <typename To1, typename To2, typename To3, \
          typename From1, typename From2> \
typename Enable_If<Is_Native_Or_Checked<To1>::value \
                   && Is_Native_Or_Checked<To2>::value \
                   && Is_Native_Or_Checked<To3>::value \
                   && Is_Native_Or_Checked<From1>::value \
                   && Is_Native_Or_Checked<From2>::value, \
                   Result>::type \
 PPL_U(name)(To1& to, To2& s, To3& t,     \
     const From1& x, const From2& y, \
     Rounding_Dir dir);

PPL_DECLARE_FUNC4(gcdext_assign_r)

#undef PPL_DECLARE_FUNC4

//! \name Accessor Functions
//@{

//@} // Accessor Functions

//! \name Memory Size Inspection Functions
//@{

//! Returns the total size in bytes of the memory occupied by \p x.
/*! \relates Checked_Number */
template <typename T, typename Policy>
memory_size_type
total_memory_in_bytes(const Checked_Number<T, Policy>& x);

//! Returns the size in bytes of the memory managed by \p x.
/*! \relates Checked_Number */
template <typename T, typename Policy>
memory_size_type
external_memory_in_bytes(const Checked_Number<T, Policy>& x);

//@} // Memory Size Inspection Functions

//! \name Arithmetic Operators
//@{

//! Unary plus operator.
/*! \relates Checked_Number */
template <typename T, typename Policy>
Checked_Number<T, Policy>
operator+(const Checked_Number<T, Policy>& x);

//! Unary minus operator.
/*! \relates Checked_Number */
template <typename T, typename Policy>
Checked_Number<T, Policy>
operator-(const Checked_Number<T, Policy>& x);

//! Assigns to \p x largest integral value not greater than \p x.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
floor_assign(Checked_Number<T, Policy>& x);

//! Assigns to \p x largest integral value not greater than \p y.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
floor_assign(Checked_Number<T, Policy>& x, const Checked_Number<T, Policy>& y);

//! Assigns to \p x smallest integral value not less than \p x.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
ceil_assign(Checked_Number<T, Policy>& x);

//! Assigns to \p x smallest integral value not less than \p y.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
ceil_assign(Checked_Number<T, Policy>& x, const Checked_Number<T, Policy>& y);

//! Round \p x to the nearest integer not larger in absolute value.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
trunc_assign(Checked_Number<T, Policy>& x);

//! Assigns to \p x the value of \p y rounded to the nearest integer not larger in absolute value.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
trunc_assign(Checked_Number<T, Policy>& x, const Checked_Number<T, Policy>& y);

//! Assigns to \p x its negation.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
neg_assign(Checked_Number<T, Policy>& x);

//! Assigns to \p x the negation of \p y.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
neg_assign(Checked_Number<T, Policy>& x, const Checked_Number<T, Policy>& y);

//! Assigns to \p x its absolute value.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
abs_assign(Checked_Number<T, Policy>& x);

//! Assigns to \p x the absolute value of \p y.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
abs_assign(Checked_Number<T, Policy>& x, const Checked_Number<T, Policy>& y);

//! Assigns to \p x the value <CODE>x + y * z</CODE>.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
add_mul_assign(Checked_Number<T, Policy>& x,
               const Checked_Number<T, Policy>& y,
               const Checked_Number<T, Policy>& z);

//! Assigns to \p x the value <CODE>x - y * z</CODE>.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
sub_mul_assign(Checked_Number<T, Policy>& x,
               const Checked_Number<T, Policy>& y,
               const Checked_Number<T, Policy>& z);

//! Assigns to \p x the greatest common divisor of \p y and \p z.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
gcd_assign(Checked_Number<T, Policy>& x,
           const Checked_Number<T, Policy>& y,
           const Checked_Number<T, Policy>& z);

/*! \brief
  Assigns to \p x the greatest common divisor of \p y and \p z,
  setting \p s and \p t such that s*y + t*z = x = gcd(y, z).
*/
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
gcdext_assign(Checked_Number<T, Policy>& x,
              Checked_Number<T, Policy>& s,
              Checked_Number<T, Policy>& t,
              const Checked_Number<T, Policy>& y,
              const Checked_Number<T, Policy>& z);

//! Assigns to \p x the least common multiple of \p y and \p z.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
lcm_assign(Checked_Number<T, Policy>& x,
           const Checked_Number<T, Policy>& y,
           const Checked_Number<T, Policy>& z);

//! Assigns to \p x the value \f$ y \cdot 2^\mathtt{exp} \f$.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
mul_2exp_assign(Checked_Number<T, Policy>& x,
                const Checked_Number<T, Policy>& y,
                unsigned int exp);

//! Assigns to \p x the value \f$ y / 2^\mathtt{exp} \f$.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void
div_2exp_assign(Checked_Number<T, Policy>& x,
                const Checked_Number<T, Policy>& y,
                unsigned int exp);

/*! \brief
  If \p z divides \p y, assigns to \p x the quotient of the integer
  division of \p y and \p z.

  \relates Checked_Number
  The behavior is undefined if \p z does not divide \p y.
*/
template <typename T, typename Policy>
void
exact_div_assign(Checked_Number<T, Policy>& x,
                 const Checked_Number<T, Policy>& y,
                 const Checked_Number<T, Policy>& z);

//! Assigns to \p x the integer square root of \p y.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void sqrt_assign(Checked_Number<T, Policy>& x,
                 const Checked_Number<T, Policy>& y);

//@} // Arithmetic Operators


//! \name Relational Operators and Comparison Functions
//@{

//! Equality operator.
/*! \relates Checked_Number */
template <typename T1, typename T2>
inline
typename Enable_If<Is_Native_Or_Checked<T1>::value
                   && Is_Native_Or_Checked<T2>::value
                   && (Is_Checked<T1>::value || Is_Checked<T2>::value),
                   bool>::type
operator==(const T1& x, const T2& y);

/*! \relates Checked_Number */
template <typename T1, typename T2>
inline typename Enable_If<Is_Native_Or_Checked<T1>::value
                          && Is_Native_Or_Checked<T2>::value,
                          bool>::type
equal(const T1& x, const T2& y);

//! Disequality operator.
/*! \relates Checked_Number */
template <typename T1, typename T2>
inline
typename Enable_If<Is_Native_Or_Checked<T1>::value
                   && Is_Native_Or_Checked<T2>::value
                   && (Is_Checked<T1>::value || Is_Checked<T2>::value),
                   bool>::type
operator!=(const T1& x, const T2& y);

/*! \relates Checked_Number */
template <typename T1, typename T2>
inline typename Enable_If<Is_Native_Or_Checked<T1>::value
                          && Is_Native_Or_Checked<T2>::value,
                          bool>::type
not_equal(const T1& x, const T2& y);

//! Greater than or equal to operator.
/*! \relates Checked_Number */
template <typename T1, typename T2>
inline
typename Enable_If<Is_Native_Or_Checked<T1>::value
                   && Is_Native_Or_Checked<T2>::value
                   && (Is_Checked<T1>::value || Is_Checked<T2>::value),
                   bool>::type
operator>=(const T1& x, const T2& y);

/*! \relates Checked_Number */
template <typename T1, typename T2>
inline typename Enable_If<Is_Native_Or_Checked<T1>::value
                          && Is_Native_Or_Checked<T2>::value,
                          bool>::type
greater_or_equal(const T1& x, const T2& y);

//! Greater than operator.
/*! \relates Checked_Number */
template <typename T1, typename T2>
inline
typename Enable_If<Is_Native_Or_Checked<T1>::value
                   && Is_Native_Or_Checked<T2>::value
                   && (Is_Checked<T1>::value || Is_Checked<T2>::value),
                   bool>::type
operator>(const T1& x, const T2& y);

/*! \relates Checked_Number */
template <typename T1, typename T2>
inline typename Enable_If<Is_Native_Or_Checked<T1>::value
                          && Is_Native_Or_Checked<T2>::value,
                          bool>::type
greater_than(const T1& x, const T2& y);

//! Less than or equal to operator.
/*! \relates Checked_Number */
template <typename T1, typename T2>
inline
typename Enable_If<Is_Native_Or_Checked<T1>::value
                   && Is_Native_Or_Checked<T2>::value
                   && (Is_Checked<T1>::value || Is_Checked<T2>::value),
                   bool>::type
operator<=(const T1& x, const T2& y);

/*! \relates Checked_Number */
template <typename T1, typename T2>
inline typename Enable_If<Is_Native_Or_Checked<T1>::value
                          && Is_Native_Or_Checked<T2>::value,
                          bool>::type
less_or_equal(const T1& x, const T2& y);

//! Less than operator.
/*! \relates Checked_Number */
template <typename T1, typename T2>
inline
typename Enable_If<Is_Native_Or_Checked<T1>::value
                   && Is_Native_Or_Checked<T2>::value
                   && (Is_Checked<T1>::value || Is_Checked<T2>::value),
                   bool>::type
operator<(const T1& x, const T2& y);

/*! \relates Checked_Number */
template <typename T1, typename T2>
inline typename Enable_If<Is_Native_Or_Checked<T1>::value
                          && Is_Native_Or_Checked<T2>::value,
                          bool>::type
less_than(const T1& x, const T2& y);

/*! \brief
  Returns \f$-1\f$, \f$0\f$ or \f$1\f$ depending on whether the value
  of \p x is negative, zero or positive, respectively.

  \relates Checked_Number
*/
template <typename From>
inline typename Enable_If<Is_Native_Or_Checked<From>::value, int>::type \
sgn(const From& x);

/*! \brief
  Returns a negative, zero or positive value depending on whether
  \p x is lower than, equal to or greater than \p y, respectively.

  \relates Checked_Number
*/
template <typename From1, typename From2>
inline typename Enable_If<Is_Native_Or_Checked<From1>::value
                          && Is_Native_Or_Checked<From2>::value,
                          int>::type
cmp(const From1& x, const From2& y);

//@} // Relational Operators and Comparison Functions

//! \name Input-Output Operators
//@{

/*! \relates Checked_Number */
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, Result>::type
output(std::ostream& os,
       const T& x,
       const Numeric_Format& format,
       Rounding_Dir dir);

//! Output operator.
/*! \relates Checked_Number */
template <typename T, typename Policy>
std::ostream&
operator<<(std::ostream& os, const Checked_Number<T, Policy>& x);

//! Ascii dump for native or checked.
/*! \relates Checked_Number */
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, void>::type
ascii_dump(std::ostream& s, const T& t);

//! Input function.
/*!
  \relates Checked_Number

  \param is
  Input stream to read from;

  \param x
  Number (possibly extended) to assign to in case of successful reading;

  \param dir
  Rounding mode to be applied.

  \return
  Result of the input operation.  Success, success with imprecision,
  overflow, parsing error: all possibilities are taken into account,
  checked for, and properly reported.

  This function attempts reading a (possibly extended) number from the given
  stream \p is, possibly rounding as specified by \p dir, assigning the result
  to \p x upon success, and returning the appropriate Result.

  The input syntax allows the specification of:
  - plain base-10 integer numbers as <CODE>34976098</CODE>,
    <CODE>-77</CODE> and <CODE>+13</CODE>;
  - base-10 integer numbers in scientific notation as <CODE>15e2</CODE>
    and <CODE>15*^2</CODE> (both meaning \f$15 \cdot 10^2 = 1500\f$),
    <CODE>9200e-2</CODE> and <CODE>-18*^+11111111111111111</CODE>;
  - base-10 rational numbers in fraction notation as
    <CODE>15/3</CODE> and <CODE>15/-3</CODE>;
  - base-10 rational numbers in fraction/scientific notation as
    <CODE>15/30e-1</CODE> (meaning \f$5\f$) and <CODE>15*^-3/29e2</CODE>
    (meaning \f$3/580000\f$);
  - base-10 rational numbers in floating point notation as
    <CODE>71.3</CODE> (meaning \f$713/10\f$) and
    <CODE>-0.123456</CODE> (meaning \f$-1929/15625\f$);
  - base-10 rational numbers in floating point scientific notation as
    <CODE>2.2e-1</CODE> (meaning \f$11/50\f$) and <CODE>-2.20001*^+3</CODE>
    (meaning \f$-220001/100\f$);
  - integers and rationals (in fractional, floating point and scientific
    notations) specified by using Mathematica-style bases, in the range
    from 2 to 36, as
    <CODE>2^^11</CODE> (meaning \f$3\f$),
    <CODE>36^^z</CODE> (meaning \f$35\f$),
    <CODE>36^^xyz</CODE> (meaning \f$44027\f$),
    <CODE>2^^11.1</CODE> (meaning \f$7/2\f$),
    <CODE>10^^2e3</CODE> (meaning \f$2000\f$),
    <CODE>8^^2e3</CODE> (meaning \f$1024\f$),
    <CODE>8^^2.1e3</CODE> (meaning \f$1088\f$),
    <CODE>8^^20402543.120347e7</CODE> (meaning \f$9073863231288\f$),
    <CODE>8^^2.1</CODE> (meaning \f$17/8\f$);
    note that the base and the exponent are always written as plain
    base-10 integer numbers; also, when an ambiguity may arise, the
    character <CODE>e</CODE> is interpreted as a digit, so that
    <CODE>16^^1e2</CODE> (meaning \f$482\f$) is different from
    <CODE>16^^1*^2</CODE> (meaning \f$256\f$);
  - the C-style hexadecimal prefix <CODE>0x</CODE> is interpreted as
    the Mathematica-style prefix <CODE>16^^</CODE>;
  - the C-style binary exponent indicator <CODE>p</CODE> can only be used
    when base 16 has been specified; if used, the exponent will be
    applied to base 2 (instead of base 16, as is the case when the
    indicator <CODE>e</CODE> is used);
  - special values like <CODE>inf</CODE> and <CODE>+inf</CODE>
    (meaning \f$+\infty\f$), <CODE>-inf</CODE> (meaning \f$-\infty\f$),
    and <CODE>nan</CODE> (meaning "not a number").

  The rationale behind the accepted syntax can be summarized as follows:
  - if the syntax is accepted by Mathematica, then this function
    accepts it with the same semantics;
  - if the syntax is acceptable as standard C++ integer or floating point
    literal (except for octal notation and type suffixes, which are not
    supported), then this function accepts it with the same semantics;
  - natural extensions of the above are accepted with the natural
    extensions of the semantics;
  - special values are accepted.

  Valid syntax is more formally and completely specified by the
  following grammar, with the additional provisos that everything is
  <EM>case insensitive</EM>, that the syntactic category
  <CODE>BDIGIT</CODE> is further restricted by the current base
  and that for all bases above 14, any <CODE>e</CODE> is always
  interpreted as a digit and never as a delimiter for the exponent part
  (if such a delimiter is desired, it has to be written as <CODE>*^</CODE>).

\code
number  : NAN                                   INF     : 'inf'
        | SIGN INF                                      ;
        | INF
        | num                                   NAN     : 'nan'
        | num DIV num                                   ;
        ;
                                                SIGN    : '-'
num     : u_num                                         | '+'
        | SIGN u_num                                    ;

u_num   : u_num1                                EXP     : 'e'
        | HEX u_num1                                    | 'p'
        | base BASE u_num1                              | '*^'
        ;                                               ;
                                                POINT   : '.'
u_num1  : mantissa                                      ;
        | mantissa EXP exponent
        ;                                       DIV     : '/'
                                                        ;
mantissa: bdigits
        | POINT bdigits                         MINUS   : '-'
        | bdigits POINT                                 ;
        | bdigits POINT bdigits
        ;                                       PLUS    : '+'
                                                ;
exponent: SIGN digits
        | digits                                HEX     : '0x'
        ;                                       ;

bdigits : BDIGIT                                BASE    : '^^'
        | bdigits BDIGIT                                ;
        ;
                                                DIGIT   : '0' .. '9'
digits  : DIGIT                                         ;
        | digits DIGIT
        ;                                       BDIGIT  : '0' .. '9'
                                                        | 'a' .. 'z'
                                                        ;
\endcode
*/
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, Result>::type
input(T& x, std::istream& is, Rounding_Dir dir);

//! Input operator.
/*! \relates Checked_Number */
template <typename T, typename Policy>
std::istream&
operator>>(std::istream& is, Checked_Number<T, Policy>& x);

//! Ascii load for native or checked.
/*! \relates Checked_Number */
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
ascii_load(std::ostream& s, T& t);

//@} // Input-Output Operators

void throw_result_exception(Result r);

template <typename T>
T
plus_infinity();

template <typename T>
T
minus_infinity();

template <typename T>
T
not_a_number();

//! Swaps \p x with \p y.
/*! \relates Checked_Number */
template <typename T, typename Policy>
void swap(Checked_Number<T, Policy>& x, Checked_Number<T, Policy>& y);

template <typename T, typename Policy>
struct FPU_Related<Checked_Number<T, Policy> > : public FPU_Related<T> {};

template <typename T>
void maybe_reset_fpu_inexact();

template <typename T>
int maybe_check_fpu_inexact();

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Checked_Number_inlines.hh line 1. */
/* Checked_Number class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Checked_Number_inlines.hh line 28. */
#include <stdexcept>
#include <sstream>

namespace Parma_Polyhedra_Library {

#ifndef NDEBUG
#define DEBUG_ROUND_NOT_NEEDED
#endif

inline Rounding_Dir
rounding_dir(Rounding_Dir dir) {
  if (dir == ROUND_NOT_NEEDED) {
#ifdef DEBUG_ROUND_NOT_NEEDED
    return ROUND_CHECK;
#endif
  }
  return dir;
}

inline Result
check_result(Result r, Rounding_Dir dir) {
  if (dir == ROUND_NOT_NEEDED) {
#ifdef DEBUG_ROUND_NOT_NEEDED
    PPL_ASSERT(result_relation(r) == VR_EQ);
#endif
    return r;
  }
  return r;
}


template <typename T>
inline void
Checked_Number_Transparent_Policy<T>::handle_result(Result) {
}

inline void
Extended_Number_Policy::handle_result(Result r) {
  if (result_class(r) == VC_NAN) {
    throw_result_exception(r);
  }
}

template <typename T, typename Policy>
inline
Checked_Number<T, Policy>::Checked_Number()
 : v(0) {
}

template <typename T, typename Policy>
inline
Checked_Number<T, Policy>::Checked_Number(const Checked_Number& y) {
  // TODO: avoid default construction of value member.
  Checked::copy<Policy, Policy>(v, y.raw_value());
}

template <typename T, typename Policy>
template <typename From, typename From_Policy>
inline
Checked_Number<T, Policy>
::Checked_Number(const Checked_Number<From, From_Policy>& y,
                 Rounding_Dir dir) {
  // TODO: avoid default construction of value member.
  Policy::handle_result(check_result(Checked::assign_ext<Policy, From_Policy>
                                     (v,
                                      y.raw_value(),
                                      rounding_dir(dir)),
                                     dir)
                        );
}

template <typename T, typename Policy>
template <typename From, typename From_Policy>
inline
Checked_Number<T, Policy>
::Checked_Number(const Checked_Number<From, From_Policy>& y) {
  // TODO: avoid default construction of value member.
  Rounding_Dir dir = Policy::ROUND_DEFAULT_CONSTRUCTOR;
  Policy::handle_result(check_result(Checked::assign_ext<Policy, From_Policy>
                                     (v,
                                      y.raw_value(),
                                      rounding_dir(dir)),
                                     dir));
}

// TODO: avoid default construction of value member.
#define PPL_DEFINE_CTOR(type) \
template <typename T, typename Policy> \
inline \
Checked_Number<T, Policy>::Checked_Number(const type y, Rounding_Dir dir) { \
  Policy::handle_result(check_result(Checked::assign_ext<Policy,        \
                                     Checked_Number_Transparent_Policy<PPL_U(type)> > \
                                     (v, y, rounding_dir(dir)),         \
                                     dir));                             \
}                                                                       \
template <typename T, typename Policy>                                  \
inline                                                                  \
Checked_Number<T, Policy>::Checked_Number(const type y) {               \
  Rounding_Dir dir = Policy::ROUND_DEFAULT_CONSTRUCTOR;                 \
  Policy::handle_result(check_result(Checked::assign_ext<Policy,        \
                                     Checked_Number_Transparent_Policy<PPL_U(type)> > \
                                     (v, y, rounding_dir(dir)),         \
                                     dir));                             \
}

PPL_DEFINE_CTOR(char)
PPL_DEFINE_CTOR(signed char)
PPL_DEFINE_CTOR(signed short)
PPL_DEFINE_CTOR(signed int)
PPL_DEFINE_CTOR(signed long)
PPL_DEFINE_CTOR(signed long long)
PPL_DEFINE_CTOR(unsigned char)
PPL_DEFINE_CTOR(unsigned short)
PPL_DEFINE_CTOR(unsigned int)
PPL_DEFINE_CTOR(unsigned long)
PPL_DEFINE_CTOR(unsigned long long)
#if PPL_SUPPORTED_FLOAT
PPL_DEFINE_CTOR(float)
#endif
#if PPL_SUPPORTED_DOUBLE
PPL_DEFINE_CTOR(double)
#endif
#if PPL_SUPPORTED_LONG_DOUBLE
PPL_DEFINE_CTOR(long double)
#endif
PPL_DEFINE_CTOR(mpq_class&)
PPL_DEFINE_CTOR(mpz_class&)

#undef PPL_DEFINE_CTOR


template <typename T, typename Policy>
inline
Checked_Number<T, Policy>::Checked_Number(const char* y, Rounding_Dir dir) {
  std::istringstream s(y);
  Policy::handle_result(check_result(Checked::input<Policy>(v,
                                                            s,
                                                            rounding_dir(dir)),
                                     dir));
}

template <typename T, typename Policy>
inline
Checked_Number<T, Policy>::Checked_Number(const char* y) {
  std::istringstream s(y);
  Rounding_Dir dir = Policy::ROUND_DEFAULT_CONSTRUCTOR;
  Policy::handle_result(check_result(Checked::input<Policy>(v,
                                                            s,
                                                            rounding_dir(dir)),
                                     dir));
}

template <typename T, typename Policy>
template <typename From>
inline
Checked_Number<T, Policy>
::Checked_Number(const From&,
                 Rounding_Dir dir,
                 typename Enable_If<Is_Special<From>::value, bool>::type) {
  Policy::handle_result(check_result(Checked::assign_special<Policy>(v,
                                                                     From::vclass,
                                                                     rounding_dir(dir)),
                                     dir));
}

template <typename T, typename Policy>
template <typename From>
inline
Checked_Number<T, Policy>::Checked_Number(const From&, typename Enable_If<Is_Special<From>::value, bool>::type) {
  Rounding_Dir dir = Policy::ROUND_DEFAULT_CONSTRUCTOR;
  Policy::handle_result(check_result(Checked::assign_special<Policy>(v,
                                                            From::vclass,
                                                            rounding_dir(dir)),
                                     dir));
}

template <typename To, typename From>
inline typename Enable_If<Is_Native_Or_Checked<To>::value
                          && Is_Special<From>::value, Result>::type
assign_r(To& to, const From&, Rounding_Dir dir) {
  return check_result(Checked::assign_special<typename Native_Checked_To_Wrapper<To>
                      ::Policy>(Native_Checked_To_Wrapper<To>::raw_value(to),
                                From::vclass,
                                rounding_dir(dir)),
                      dir);
}

template <typename To, typename From>
inline typename Enable_If<Is_Native_Or_Checked<To>::value && Is_Special<From>::value, Result>::type
construct(To& to, const From&, Rounding_Dir dir) {
  return check_result(Checked::construct_special<typename Native_Checked_To_Wrapper<To>
                      ::Policy>(Native_Checked_To_Wrapper<To>::raw_value(to),
                                From::vclass,
                                rounding_dir(dir)),
                      dir);
}

template <typename T>
inline typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_minus_infinity(const T& x) {
  return Checked::is_minf<typename Native_Checked_From_Wrapper<T>
    ::Policy>(Native_Checked_From_Wrapper<T>::raw_value(x));
}

template <typename T>
inline typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_plus_infinity(const T& x) {
  return Checked::is_pinf<typename Native_Checked_From_Wrapper<T>
    ::Policy>(Native_Checked_From_Wrapper<T>::raw_value(x));
}

template <typename T>
inline typename Enable_If<Is_Native_Or_Checked<T>::value, int>::type
infinity_sign(const T& x) {
  return is_minus_infinity(x) ? -1 : (is_plus_infinity(x) ? 1 : 0);
}

template <typename T>
inline typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_not_a_number(const T& x) {
  return Checked::is_nan<typename Native_Checked_From_Wrapper<T>
    ::Policy>(Native_Checked_From_Wrapper<T>::raw_value(x));
}

template <typename T>
inline typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_integer(const T& x) {
  return Checked::is_int<typename Native_Checked_From_Wrapper<T>
    ::Policy>(Native_Checked_From_Wrapper<T>::raw_value(x));
}

template <typename T, typename Policy>
inline
Checked_Number<T, Policy>::operator T() const {
  if (Policy::convertible) {
    return v;
  }
}

template <typename T, typename Policy>
inline T&
Checked_Number<T, Policy>::raw_value() {
  return v;
}

template <typename T, typename Policy>
inline const T&
Checked_Number<T, Policy>::raw_value() const {
  return v;
}

/*! \relates Checked_Number */
template <typename T, typename Policy>
inline const T&
raw_value(const Checked_Number<T, Policy>& x) {
  return x.raw_value();
}

/*! \relates Checked_Number */
template <typename T, typename Policy>
inline T&
raw_value(Checked_Number<T, Policy>& x) {
  return x.raw_value();
}

template <typename T, typename Policy>
inline bool
Checked_Number<T, Policy>::OK() const {
  return true;
}

template <typename T, typename Policy>
inline Result
Checked_Number<T, Policy>::classify(bool nan, bool inf, bool sign) const {
  return Checked::classify<Policy>(v, nan, inf, sign);
}

template <typename T, typename Policy>
inline bool
is_not_a_number(const Checked_Number<T, Policy>& x) {
  return Checked::is_nan<Policy>(x.raw_value());
}

template <typename T, typename Policy>
inline bool
is_minus_infinity(const Checked_Number<T, Policy>& x) {
  return Checked::is_minf<Policy>(x.raw_value());
}

template <typename T, typename Policy>
inline bool
is_plus_infinity(const Checked_Number<T, Policy>& x) {
  return Checked::is_pinf<Policy>(x.raw_value());
}

/*! \relates Checked_Number */
template <typename T, typename Policy>
inline memory_size_type
total_memory_in_bytes(const Checked_Number<T, Policy>& x) {
  return total_memory_in_bytes(x.raw_value());
}

/*! \relates Checked_Number */
template <typename T, typename Policy>
inline memory_size_type
external_memory_in_bytes(const Checked_Number<T, Policy>& x) {
  return external_memory_in_bytes(x.raw_value());
}


/*! \relates Checked_Number */
template <typename To>
inline typename Enable_If<Is_Native_Or_Checked<To>::value, Result>::type
assign_r(To& to, const char* x, Rounding_Dir dir) {
  std::istringstream s(x);
  return check_result(Checked::input<typename Native_Checked_To_Wrapper<To>
                      ::Policy>(Native_Checked_To_Wrapper<To>::raw_value(to),
                                s,
                                rounding_dir(dir)),
                      dir);
}

#define PPL_DEFINE_FUNC1_A(name, func) \
template <typename To, typename From>                                   \
inline typename Enable_If<Is_Native_Or_Checked<To>::value               \
                          && Is_Native_Or_Checked<From>::value,         \
                          Result>::type                                 \
 PPL_U(name)(To& to, const From& x, Rounding_Dir dir) {                 \
  return                                                                \
    check_result(Checked::func<typename Native_Checked_To_Wrapper<To>   \
                 ::Policy,                                              \
                 typename Native_Checked_From_Wrapper<From>             \
                 ::Policy>(Native_Checked_To_Wrapper<To>::raw_value(to), \
                           Native_Checked_From_Wrapper<From>::raw_value(x), \
                           rounding_dir(dir)), dir);                    \
}

PPL_DEFINE_FUNC1_A(construct, construct_ext)
PPL_DEFINE_FUNC1_A(assign_r, assign_ext)
PPL_DEFINE_FUNC1_A(floor_assign_r, floor_ext)
PPL_DEFINE_FUNC1_A(ceil_assign_r, ceil_ext)
PPL_DEFINE_FUNC1_A(trunc_assign_r, trunc_ext)
PPL_DEFINE_FUNC1_A(neg_assign_r, neg_ext)
PPL_DEFINE_FUNC1_A(abs_assign_r, abs_ext)
PPL_DEFINE_FUNC1_A(sqrt_assign_r, sqrt_ext)

#undef PPL_DEFINE_FUNC1_A

#define PPL_DEFINE_FUNC1_B(name, func) \
template <typename To, typename From>                                   \
inline typename Enable_If<Is_Native_Or_Checked<To>::value               \
                          && Is_Native_Or_Checked<From>::value,         \
                          Result>::type                                 \
 PPL_U(name)(To& to, const From& x, unsigned int exp, Rounding_Dir dir) { \
  return                                                                \
    check_result(Checked::func<typename Native_Checked_To_Wrapper<To>   \
                 ::Policy,                                              \
                 typename Native_Checked_From_Wrapper<From>             \
                 ::Policy>(Native_Checked_To_Wrapper<To>::raw_value(to), \
                           Native_Checked_From_Wrapper<From>::raw_value(x), \
                           exp,                                         \
                           rounding_dir(dir)),                          \
                 dir);                                                  \
}

PPL_DEFINE_FUNC1_B(add_2exp_assign_r, add_2exp_ext)
PPL_DEFINE_FUNC1_B(sub_2exp_assign_r, sub_2exp_ext)
PPL_DEFINE_FUNC1_B(mul_2exp_assign_r, mul_2exp_ext)
PPL_DEFINE_FUNC1_B(div_2exp_assign_r, div_2exp_ext)
PPL_DEFINE_FUNC1_B(smod_2exp_assign_r, smod_2exp_ext)
PPL_DEFINE_FUNC1_B(umod_2exp_assign_r, umod_2exp_ext)

#undef PPL_DEFINE_FUNC1_B

#define PPL_DEFINE_FUNC2(name, func) \
template <typename To, typename From1, typename From2>                  \
inline typename Enable_If<Is_Native_Or_Checked<To>::value               \
                          && Is_Native_Or_Checked<From1>::value         \
                          && Is_Native_Or_Checked<From2>::value,        \
                          Result>::type                                 \
 PPL_U(name)(To& to, const From1& x, const From2& y, Rounding_Dir dir) { \
  return                                                                \
    check_result(Checked::func<typename Native_Checked_To_Wrapper<To>   \
                 ::Policy,                                              \
                 typename Native_Checked_From_Wrapper<From1>            \
                 ::Policy,                                              \
                 typename Native_Checked_From_Wrapper<From2>            \
                 ::Policy>(Native_Checked_To_Wrapper<To>::raw_value(to), \
                           Native_Checked_From_Wrapper<From1>::raw_value(x), \
                           Native_Checked_From_Wrapper<From2>::raw_value(y), \
                           rounding_dir(dir)),                          \
                 dir);                                                  \
}

PPL_DEFINE_FUNC2(add_assign_r, add_ext)
PPL_DEFINE_FUNC2(sub_assign_r, sub_ext)
PPL_DEFINE_FUNC2(mul_assign_r, mul_ext)
PPL_DEFINE_FUNC2(div_assign_r, div_ext)
PPL_DEFINE_FUNC2(idiv_assign_r, idiv_ext)
PPL_DEFINE_FUNC2(rem_assign_r, rem_ext)
PPL_DEFINE_FUNC2(gcd_assign_r, gcd_ext)
PPL_DEFINE_FUNC2(lcm_assign_r, lcm_ext)
PPL_DEFINE_FUNC2(add_mul_assign_r, add_mul_ext)
PPL_DEFINE_FUNC2(sub_mul_assign_r, sub_mul_ext)

#undef PPL_DEFINE_FUNC2

#define PPL_DEFINE_FUNC4(name, func)                                    \
template <typename To1,                                                 \
          typename To2,                                                 \
          typename To3,                                                 \
          typename From1,                                               \
          typename From2>                                               \
inline typename Enable_If<Is_Native_Or_Checked<To1>::value              \
                          && Is_Native_Or_Checked<To2>::value           \
                          && Is_Native_Or_Checked<To3>::value           \
                          && Is_Native_Or_Checked<From1>::value         \
                          && Is_Native_Or_Checked<From2>::value,        \
                          Result>::type                                 \
 PPL_U(name)(To1& to, To2& s, To3& t, const From1& x, const From2& y,   \
     Rounding_Dir dir) {                                                \
  return                                                                \
    check_result(Checked::func<typename Native_Checked_To_Wrapper<To1>::Policy, \
                 typename Native_Checked_To_Wrapper<To2>::Policy,       \
                 typename Native_Checked_To_Wrapper<To3>::Policy,       \
                 typename Native_Checked_From_Wrapper<From1>::Policy,   \
                 typename Native_Checked_From_Wrapper<From2>::Policy>   \
                 (Native_Checked_To_Wrapper<To1>::raw_value(to),        \
                  Native_Checked_To_Wrapper<To2>::raw_value(s),         \
                  Native_Checked_To_Wrapper<To3>::raw_value(t),         \
                  Native_Checked_From_Wrapper<From1>::raw_value(x),     \
                  Native_Checked_From_Wrapper<From2>::raw_value(y),     \
                  rounding_dir(dir)),                                   \
                 dir);                                                  \
}

PPL_DEFINE_FUNC4(gcdext_assign_r, gcdext_ext)

#undef PPL_DEFINE_PPL_DEFINE_FUNC4

#define PPL_DEFINE_INCREMENT(f, fun) \
template <typename T, typename Policy> \
inline Checked_Number<T, Policy>& \
Checked_Number<T, Policy>::f() { \
  Policy::handle_result((fun)(*this, *this, T(1),             \
                            Policy::ROUND_DEFAULT_OPERATOR)); \
  return *this; \
} \
template <typename T, typename Policy> \
inline Checked_Number<T, Policy> \
Checked_Number<T, Policy>::f(int) {\
  T r = v;\
  Policy::handle_result((fun)(*this, *this, T(1),             \
                            Policy::ROUND_DEFAULT_OPERATOR)); \
  return r;\
}

PPL_DEFINE_INCREMENT(operator ++, add_assign_r)
PPL_DEFINE_INCREMENT(operator --, sub_assign_r)

#undef PPL_DEFINE_INCREMENT

template <typename T, typename Policy>
inline Checked_Number<T, Policy>&
Checked_Number<T, Policy>::operator=(const Checked_Number<T, Policy>& y) {
  Checked::copy<Policy, Policy>(v, y.raw_value());
  return *this;
}
template <typename T, typename Policy>
template <typename From>
inline Checked_Number<T, Policy>&
Checked_Number<T, Policy>::operator=(const From& y) {
  Policy::handle_result(assign_r(*this, y, Policy::ROUND_DEFAULT_OPERATOR));
  return *this;
}

#define PPL_DEFINE_BINARY_OP_ASSIGN(f, fun) \
template <typename T, typename Policy> \
template <typename From_Policy> \
inline Checked_Number<T, Policy>& \
Checked_Number<T, Policy>::f(const Checked_Number<T, From_Policy>& y) { \
  Policy::handle_result((fun)(*this, *this, y,                          \
                            Policy::ROUND_DEFAULT_OPERATOR)); \
  return *this; \
} \
template <typename T, typename Policy> \
inline Checked_Number<T, Policy>& \
Checked_Number<T, Policy>::f(const T& y) { \
  Policy::handle_result((fun)(*this, *this, y,                \
                            Policy::ROUND_DEFAULT_OPERATOR)); \
  return *this; \
} \
template <typename T, typename Policy> \
template <typename From> \
inline typename Enable_If<Is_Native_Or_Checked<From>::value, \
                          Checked_Number<T, Policy>& >::type \
Checked_Number<T, Policy>::f(const From& y) { \
  Checked_Number<T, Policy> cy(y); \
  Policy::handle_result((fun)(*this, *this, cy,               \
                            Policy::ROUND_DEFAULT_OPERATOR)); \
  return *this; \
}

PPL_DEFINE_BINARY_OP_ASSIGN(operator +=, add_assign_r)
PPL_DEFINE_BINARY_OP_ASSIGN(operator -=, sub_assign_r)
PPL_DEFINE_BINARY_OP_ASSIGN(operator *=, mul_assign_r)
PPL_DEFINE_BINARY_OP_ASSIGN(operator /=, div_assign_r)
PPL_DEFINE_BINARY_OP_ASSIGN(operator %=, rem_assign_r)

#undef PPL_DEFINE_BINARY_OP_ASSIGN

#define PPL_DEFINE_BINARY_OP(f, fun) \
template <typename T, typename Policy> \
inline Checked_Number<T, Policy> \
 PPL_U(f)(const Checked_Number<T, Policy>& x,   \
         const Checked_Number<T, Policy>& y) {  \
  Checked_Number<T, Policy> r; \
  Policy::handle_result((fun)(r, x, y, Policy::ROUND_DEFAULT_OPERATOR)); \
  return r; \
} \
template <typename Type, typename T, typename Policy>   \
inline \
typename Enable_If<Is_Native<Type>::value, Checked_Number<T, Policy> >::type \
 PPL_U(f)(const Type& x, const Checked_Number<T, Policy>& y) {          \
  Checked_Number<T, Policy> r(x); \
  Policy::handle_result((fun)(r, r, y, Policy::ROUND_DEFAULT_OPERATOR)); \
  return r; \
} \
template <typename T, typename Policy, typename Type>   \
inline \
typename Enable_If<Is_Native<Type>::value, Checked_Number<T, Policy> >::type \
 PPL_U(f)(const Checked_Number<T, Policy>& x, const Type& y) {          \
  Checked_Number<T, Policy> r(y); \
  Policy::handle_result((fun)(r, x, r, Policy::ROUND_DEFAULT_OPERATOR)); \
  return r; \
}

PPL_DEFINE_BINARY_OP(operator +, add_assign_r)
PPL_DEFINE_BINARY_OP(operator -, sub_assign_r)
PPL_DEFINE_BINARY_OP(operator *, mul_assign_r)
PPL_DEFINE_BINARY_OP(operator /, div_assign_r)
PPL_DEFINE_BINARY_OP(operator %, rem_assign_r)

#undef PPL_DEFINE_BINARY_OP

#define PPL_DEFINE_COMPARE_OP(f, fun)                                   \
template <typename T1, typename T2>                                     \
inline                                                                  \
typename Enable_If<Is_Native_Or_Checked<T1>::value                      \
                   && Is_Native_Or_Checked<T2>::value                   \
                   && (Is_Checked<T1>::value || Is_Checked<T2>::value), \
                   bool>::type                                          \
 PPL_U(f)(const T1& x, const T2& y) {                                   \
  return Checked::fun<typename Native_Checked_From_Wrapper<T1>::Policy, \
                      typename Native_Checked_From_Wrapper<T2>::Policy> \
    (Native_Checked_From_Wrapper<T1>::raw_value(x),                     \
     Native_Checked_From_Wrapper<T2>::raw_value(y));                    \
}

PPL_DEFINE_COMPARE_OP(operator ==, eq_ext)
PPL_DEFINE_COMPARE_OP(operator !=, ne_ext)
PPL_DEFINE_COMPARE_OP(operator >=, ge_ext)
PPL_DEFINE_COMPARE_OP(operator >, gt_ext)
PPL_DEFINE_COMPARE_OP(operator <=, le_ext)
PPL_DEFINE_COMPARE_OP(operator <, lt_ext)

#undef PPL_DEFINE_COMPARE_OP

#define PPL_DEFINE_COMPARE(f, fun)                                      \
template <typename T1, typename T2>                                     \
inline typename Enable_If<Is_Native_Or_Checked<T1>::value               \
                          && Is_Native_Or_Checked<T2>::value,           \
                          bool>::type                                   \
 PPL_U(f)(const T1& x, const T2& y) {                                   \
  return Checked::fun<typename Native_Checked_From_Wrapper<T1>::Policy, \
                      typename Native_Checked_From_Wrapper<T2>::Policy> \
    (Native_Checked_From_Wrapper<T1>::raw_value(x),                     \
     Native_Checked_From_Wrapper<T2>::raw_value(y));                    \
}

PPL_DEFINE_COMPARE(equal, eq_ext)
PPL_DEFINE_COMPARE(not_equal, ne_ext)
PPL_DEFINE_COMPARE(greater_or_equal, ge_ext)
PPL_DEFINE_COMPARE(greater_than, gt_ext)
PPL_DEFINE_COMPARE(less_or_equal, le_ext)
PPL_DEFINE_COMPARE(less_than, lt_ext)

#undef PPL_DEFINE_COMPARE

/*! \relates Checked_Number */
template <typename T, typename Policy>
inline Checked_Number<T, Policy>
operator+(const Checked_Number<T, Policy>& x) {
  return x;
}

/*! \relates Checked_Number */
template <typename T, typename Policy>
inline Checked_Number<T, Policy>
operator-(const Checked_Number<T, Policy>& x) {
  Checked_Number<T, Policy> r;
  Policy::handle_result(neg_assign_r(r, x, Policy::ROUND_DEFAULT_OPERATOR));
  return r;
}

#define PPL_DEFINE_ASSIGN_FUN2_1(f, fun) \
template <typename T, typename Policy> \
inline void \
 PPL_U(f)(Checked_Number<T, Policy>& x) {                               \
  Policy::handle_result((fun)(x, x, Policy::ROUND_DEFAULT_FUNCTION));   \
}

#define PPL_DEFINE_ASSIGN_FUN2_2(f, fun) \
template <typename T, typename Policy> \
inline void \
 PPL_U(f)(Checked_Number<T, Policy>& x, const Checked_Number<T, Policy>& y) { \
  Policy::handle_result((fun)(x, y, Policy::ROUND_DEFAULT_FUNCTION)); \
}

#define PPL_DEFINE_ASSIGN_FUN3_3(f, fun) \
template <typename T, typename Policy> \
inline void \
 PPL_U(f)(Checked_Number<T, Policy>& x, const Checked_Number<T, Policy>& y, \
  const Checked_Number<T, Policy>& z) { \
  Policy::handle_result((fun)(x, y, z, Policy::ROUND_DEFAULT_FUNCTION)); \
}

#define PPL_DEFINE_ASSIGN_FUN5_5(f, fun)                                        \
template <typename T, typename Policy>                                  \
inline void                                                             \
 PPL_U(f)(Checked_Number<T, Policy>& x,                                 \
  Checked_Number<T, Policy>& s, Checked_Number<T, Policy>& t,           \
  const Checked_Number<T, Policy>& y,                                   \
  const Checked_Number<T, Policy>& z) {                                 \
  Policy::handle_result((fun)(x, s, t, y, z, Policy::ROUND_DEFAULT_FUNCTION)); \
}

PPL_DEFINE_ASSIGN_FUN2_2(sqrt_assign, sqrt_assign_r)

PPL_DEFINE_ASSIGN_FUN2_1(floor_assign, floor_assign_r)
PPL_DEFINE_ASSIGN_FUN2_2(floor_assign, floor_assign_r)

PPL_DEFINE_ASSIGN_FUN2_1(ceil_assign, ceil_assign_r)
PPL_DEFINE_ASSIGN_FUN2_2(ceil_assign, ceil_assign_r)

PPL_DEFINE_ASSIGN_FUN2_1(trunc_assign, trunc_assign_r)
PPL_DEFINE_ASSIGN_FUN2_2(trunc_assign, trunc_assign_r)

PPL_DEFINE_ASSIGN_FUN2_1(neg_assign, neg_assign_r)
PPL_DEFINE_ASSIGN_FUN2_2(neg_assign, neg_assign_r)

PPL_DEFINE_ASSIGN_FUN2_1(abs_assign, abs_assign_r)
PPL_DEFINE_ASSIGN_FUN2_2(abs_assign, abs_assign_r)

PPL_DEFINE_ASSIGN_FUN3_3(add_mul_assign, add_mul_assign_r)

PPL_DEFINE_ASSIGN_FUN3_3(sub_mul_assign, sub_mul_assign_r)

PPL_DEFINE_ASSIGN_FUN3_3(rem_assign, rem_assign_r)

PPL_DEFINE_ASSIGN_FUN3_3(gcd_assign, gcd_assign_r)

PPL_DEFINE_ASSIGN_FUN5_5(gcdext_assign, gcdext_assign_r)

PPL_DEFINE_ASSIGN_FUN3_3(lcm_assign, lcm_assign_r)

#undef PPL_DEFINE_ASSIGN_FUN2_1
#undef PPL_DEFINE_ASSIGN_FUN2_2
#undef PPL_DEFINE_ASSIGN_FUN3_2
#undef PPL_DEFINE_ASSIGN_FUN3_3
#undef PPL_DEFINE_ASSIGN_FUN5_5

#define PPL_DEFINE_ASSIGN_2EXP(f, fun)                                  \
template <typename T, typename Policy>                                  \
inline void                                                             \
 PPL_U(f)(Checked_Number<T, Policy>& x,                                 \
          const Checked_Number<T, Policy>& y, unsigned int exp) {       \
  Policy::handle_result((fun)(x, y, exp, Policy::ROUND_DEFAULT_FUNCTION)); \
}

PPL_DEFINE_ASSIGN_2EXP(mul_2exp_assign, mul_2exp_assign_r)
PPL_DEFINE_ASSIGN_2EXP(div_2exp_assign, div_2exp_assign_r)

template <typename T, typename Policy>
inline void
exact_div_assign(Checked_Number<T, Policy>& x,
                 const Checked_Number<T, Policy>& y,
                 const Checked_Number<T, Policy>& z) {
  Policy::handle_result(div_assign_r(x, y, z, ROUND_NOT_NEEDED));
}

/*! \relates Checked_Number */
template <typename From>
inline typename Enable_If<Is_Native_Or_Checked<From>::value, int>::type
sgn(const From& x) {
  Result_Relation r
    = Checked::sgn_ext<typename Native_Checked_From_Wrapper<From>::Policy>
        (Native_Checked_From_Wrapper<From>::raw_value(x));
  switch (r) {
  case VR_LT:
    return -1;
  case VR_EQ:
    return 0;
  case VR_GT:
    return 1;
  default:
    throw(0);
  }
}

/*! \relates Checked_Number */
template <typename From1, typename From2>
inline typename Enable_If<Is_Native_Or_Checked<From1>::value
                          && Is_Native_Or_Checked<From2>::value,
                          int>::type
cmp(const From1& x, const From2& y) {
  Result_Relation r
    = Checked::cmp_ext<typename Native_Checked_From_Wrapper<From1>::Policy,
                       typename Native_Checked_From_Wrapper<From2>::Policy>
                 (Native_Checked_From_Wrapper<From1>::raw_value(x),
                  Native_Checked_From_Wrapper<From2>::raw_value(y));
  switch (r) {
  case VR_LT:
    return -1;
  case VR_EQ:
    return 0;
  case VR_GT:
    return 1;
  default:
    throw(0);
  }
}

/*! \relates Checked_Number */
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, Result>::type
output(std::ostream& os, const T& x,
       const Numeric_Format& format, Rounding_Dir dir) {
  return check_result(Checked::output_ext<typename Native_Checked_From_Wrapper<T>::Policy>
                      (os,
                       Native_Checked_From_Wrapper<T>::raw_value(x),
                       format,
                       rounding_dir(dir)),
                      dir);
}

/*! \relates Checked_Number */
template <typename T, typename Policy>
inline std::ostream&
operator<<(std::ostream& os, const Checked_Number<T, Policy>& x) {
  Policy::handle_result(output(os, x, Numeric_Format(), ROUND_IGNORE));
  return os;
}

/*! \relates Checked_Number */
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, Result>::type
input(T& x, std::istream& is, Rounding_Dir dir) {
  return check_result(Checked::input_ext<typename Native_Checked_To_Wrapper<T>::Policy>
                      (Native_Checked_To_Wrapper<T>::raw_value(x),
                       is,
                       rounding_dir(dir)),
                      dir);
}

/*! \relates Checked_Number */
template <typename T, typename Policy>
inline std::istream& operator>>(std::istream& is,
                                Checked_Number<T, Policy>& x) {
  Result r = input(x, is, Policy::ROUND_DEFAULT_INPUT);
  if (r == V_CVT_STR_UNK) {
    is.setstate(std::ios::failbit);
  }
  else {
    Policy::handle_result(r);
  }
  return is;
}

template <typename T>
inline T
plus_infinity() {
  return PLUS_INFINITY;
}

template <typename T>
inline T
minus_infinity() {
  return MINUS_INFINITY;
}

template <typename T>
inline T
not_a_number() {
  return NOT_A_NUMBER;
}

/*! \relates Checked_Number */
template <typename T, typename Policy>
inline void
swap(Checked_Number<T, Policy>& x, Checked_Number<T, Policy>& y) {
  using std::swap;
  swap(x.raw_value(), y.raw_value());
}

template <typename T>
inline void
maybe_reset_fpu_inexact() {
  if (FPU_Related<T>::value) {
    return fpu_reset_inexact();
  }
}

template <typename T>
inline int
maybe_check_fpu_inexact() {
  if (FPU_Related<T>::value) {
    return fpu_check_inexact();
  }
  else {
    return 0;
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Checked_Number_templates.hh line 1. */
/* Checked_Number class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/Checked_Number_templates.hh line 28. */
#include <iomanip>
#include <limits>

namespace Parma_Polyhedra_Library {

template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, void>::type
ascii_dump(std::ostream& s, const T& t) {
  if (std::numeric_limits<T>::is_exact) {
    // An exact data type: pretty printer is accurate.
    s << t;
  }
  else {
    // An inexact data type (probably floating point):
    // first dump its hexadecimal representation ...
    const std::ios::fmtflags old_flags = s.setf(std::ios::hex,
                                                std::ios::basefield);
    const unsigned char* p = reinterpret_cast<const unsigned char*>(&t);
    for (unsigned i = 0; i < sizeof(T); ++i) {
      s << std::setw(2) << std::setfill('0') << static_cast<unsigned>(p[i]);
    }
    s.flags(old_flags);
    // ... and then pretty print it for readability.
    s << " (" << t << ")";
  }
}

template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
ascii_load(std::istream& s, T& t) {
  if (std::numeric_limits<T>::is_exact) {
    // An exact data type: input from pretty printed version is accurate.
    s >> t;
    return !s.fail();
  }
  else {
    // An inexact data type (probably floating point):
    // first load its hexadecimal representation ...
    std::string str;
    if (!(s >> str) || str.size() != 2*sizeof(T)) {
      return false;
    }
    unsigned char* p = reinterpret_cast<unsigned char*>(&t);
    // CHECKME: any (portable) simpler way?
    for (unsigned i = 0; i < sizeof(T); ++i) {
      unsigned byte_value = 0;
      for (unsigned j = 0; j < 2; ++j) {
        byte_value <<= 4;
        unsigned half_byte_value;
        // Interpret single hex character.
        switch (str[2*i + j]) {
        case '0':
          half_byte_value = 0;
          break;
        case '1':
          half_byte_value = 1;
          break;
        case '2':
          half_byte_value = 2;
          break;
        case '3':
          half_byte_value = 3;
          break;
        case '4':
          half_byte_value = 4;
          break;
        case '5':
          half_byte_value = 5;
          break;
        case '6':
          half_byte_value = 6;
          break;
        case '7':
          half_byte_value = 7;
          break;
        case '8':
          half_byte_value = 8;
          break;
        case '9':
          half_byte_value = 9;
          break;
        case 'A':
        case 'a':
          half_byte_value = 10;
          break;
        case 'B':
        case 'b':
          half_byte_value = 11;
          break;
        case 'C':
        case 'c':
          half_byte_value = 12;
          break;
        case 'D':
        case 'd':
          half_byte_value = 13;
          break;
        case 'E':
        case 'e':
          half_byte_value = 14;
          break;
        case 'F':
        case 'f':
          half_byte_value = 15;
          break;
        default:
          return false;
        }
        byte_value += half_byte_value;
      }
      PPL_ASSERT(byte_value <= 255);
      p[i] = static_cast<unsigned char>(byte_value);
    }
    // ... then read and discard pretty printed value.
    if (!(s >> str)) {
      return false;
    }
    const std::string::size_type sz = str.size();
    return sz > 2 && str[0] == '(' && str[sz-1] == ')';
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Checked_Number_defs.hh line 1069. */

/* Automatically generated from PPL source file ../src/checked_numeric_limits.hh line 29. */
#include <limits>

namespace std {

using namespace Parma_Polyhedra_Library;

#define PPL_SPECIALIZE_LIMITS_INT(T)                                    \
  /*! \brief Partial specialization of std::numeric_limits. */          \
  template <typename Policy>                                            \
  class numeric_limits<Checked_Number<PPL_U(T), Policy> >              \
    : public numeric_limits<PPL_U(T)> {                                 \
  private:                                                              \
    typedef Checked_Number<PPL_U(T), Policy> Type;                      \
                                                                        \
  public:                                                               \
    static const bool has_infinity = Policy::has_infinity;              \
    static const bool has_quiet_NaN =  Policy::has_nan;                 \
                                                                        \
    static Type min() {                                                 \
      Type v;                                                           \
      v.raw_value() = Checked::Extended_Int<Policy, PPL_U(T)>::min;     \
      return v;                                                         \
    }                                                                   \
                                                                        \
    static Type max() {                                                 \
      Type v;                                                           \
      v.raw_value() = Checked::Extended_Int<Policy, PPL_U(T)>::max;     \
      return v;                                                         \
    }                                                                   \
                                                                        \
    static Type infinity() {                                            \
      Type v;                                                           \
      Checked::assign_special<Policy>(v.raw_value(), VC_PLUS_INFINITY,  \
                                      ROUND_IGNORE);                    \
      return v;                                                         \
    }                                                                   \
                                                                        \
    static Type quiet_NaN() {                                           \
      Type v;                                                           \
      Checked::assign_special<Policy>(v.raw_value(), VC_NAN,            \
                                      ROUND_IGNORE);                    \
      return v;                                                         \
    }                                                                   \
  };

PPL_SPECIALIZE_LIMITS_INT(char)

PPL_SPECIALIZE_LIMITS_INT(signed char)
PPL_SPECIALIZE_LIMITS_INT(signed short)
PPL_SPECIALIZE_LIMITS_INT(signed int)
PPL_SPECIALIZE_LIMITS_INT(signed long)
PPL_SPECIALIZE_LIMITS_INT(signed long long)

PPL_SPECIALIZE_LIMITS_INT(unsigned char)
PPL_SPECIALIZE_LIMITS_INT(unsigned short)
PPL_SPECIALIZE_LIMITS_INT(unsigned int)
PPL_SPECIALIZE_LIMITS_INT(unsigned long)
PPL_SPECIALIZE_LIMITS_INT(unsigned long long)

#undef PPL_SPECIALIZE_LIMITS_INT

#define PPL_SPECIALIZE_LIMITS_FLOAT(T)                                  \
  /*! \brief Partial specialization of std::numeric_limits. */          \
  template <typename Policy>                                            \
  struct numeric_limits<Checked_Number<PPL_U(T), Policy> >              \
    : public numeric_limits<PPL_U(T)> {                                 \
};

#if PPL_SUPPORTED_FLOAT
PPL_SPECIALIZE_LIMITS_FLOAT(float)
#endif
#if PPL_SUPPORTED_DOUBLE
PPL_SPECIALIZE_LIMITS_FLOAT(double)
#endif
#if PPL_SUPPORTED_LONG_DOUBLE
PPL_SPECIALIZE_LIMITS_FLOAT(long double)
#endif

#undef PPL_SPECIALIZE_LIMITS_FLOAT

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Partial specialization of std::numeric_limits.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Policy>
class
numeric_limits<Checked_Number<mpz_class, Policy> >
  : public numeric_limits<mpz_class> {
private:
  typedef Checked_Number<mpz_class, Policy> Type;

public:
  static const bool has_infinity = Policy::has_infinity;
  static const bool has_quiet_NaN =  Policy::has_nan;

  static Type infinity() {
    Type v;
    Checked::assign_special<Policy>(v.raw_value(), VC_PLUS_INFINITY,
                                    ROUND_IGNORE);
    return v;
  }

  static Type quiet_NaN() {
    Type v;
    Checked::assign_special<Policy>(v.raw_value(), VC_NAN, ROUND_IGNORE);
    return v;
  }
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Partial specialization of std::numeric_limits.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Policy>
class
numeric_limits<Checked_Number<mpq_class, Policy> >
: public numeric_limits<mpq_class> {
private:
  typedef Checked_Number<mpq_class, Policy> Type;

public:
  static const bool has_infinity = Policy::has_infinity;
  static const bool has_quiet_NaN =  Policy::has_nan;

  static Type infinity() {
    Type v;
    Checked::assign_special<Policy>(v.raw_value(), VC_PLUS_INFINITY,
                                    ROUND_IGNORE);
    return v;
  }

  static Type quiet_NaN() {
    Type v;
    Checked::assign_special<Policy>(v.raw_value(), VC_NAN, ROUND_IGNORE);
    return v;
  }
};

} // namespace std

/* Automatically generated from PPL source file ../src/stdiobuf_defs.hh line 1. */
/* stdiobuf class declaration.
*/


/* Automatically generated from PPL source file ../src/stdiobuf_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class stdiobuf;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/stdiobuf_defs.hh line 28. */
#include <cstdio>
#include <streambuf>

class Parma_Polyhedra_Library::stdiobuf
  : public std::basic_streambuf<char, std::char_traits<char> > {
public:
  //! Constructor.
  stdiobuf(FILE* file);

protected:
  /*! \brief
    Gets a character in case of underflow.

    \remarks
    Specified by ISO/IEC 14882:1998: 27.5.2.4.3.
  */
  virtual int_type underflow();

  /*! \brief
    In case of underflow, gets a character and advances the next pointer.

    \remarks
    Specified by ISO/IEC 14882:1998: 27.5.2.4.3.
  */
  virtual int_type uflow();

  /*! \brief
    Gets a sequence of characters.

    \remarks
    Specified by ISO/IEC 14882:1998: 27.5.2.4.3.
  */
  virtual std::streamsize xsgetn(char_type* s, std::streamsize n);

  /*! \brief
    Puts character back in case of backup underflow.

    \remarks
    Specified by ISO/IEC 14882:1998: 27.5.2.4.4.
  */
  virtual int_type pbackfail(int_type c = traits_type::eof());

  /*! \brief
    Writes a sequence of characters.

    \remarks
    Specified by ISO/IEC 14882:1998: 27.5.2.4.5.
  */
  virtual std::streamsize xsputn(const char_type* s, std::streamsize n);

  /*! \brief
    Writes a character in case of overflow.

    Specified by ISO/IEC 14882:1998: 27.5.2.4.5.
  */
  virtual int_type overflow(int_type c);

  /*! \brief
    Synchronizes the stream buffer.

    Specified by ISO/IEC 14882:1998: 27.5.2.4.2.
  */
  virtual int sync();

private:
  //! Character type of the streambuf.
  typedef char char_type;

  //! Traits type of the streambuf.
  typedef std::char_traits<char_type> traits_type;

  //! Integer type of the streambuf.
  typedef traits_type::int_type int_type;

  //! The encapsulated stdio file.
  FILE* fp;

  //! Buffer for the last character read.
  int_type unget_char_buf;
};

/* Automatically generated from PPL source file ../src/stdiobuf_inlines.hh line 1. */
/* stdiobuf class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline
stdiobuf::stdiobuf(FILE* file)
  : fp(file), unget_char_buf(traits_type::eof()) {
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/stdiobuf_defs.hh line 110. */

/* Automatically generated from PPL source file ../src/c_streambuf_defs.hh line 1. */
/* c_streambuf class declaration.
*/


/* Automatically generated from PPL source file ../src/c_streambuf_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class c_streambuf;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/c_streambuf_defs.hh line 28. */
#include <streambuf>
#include <cstddef>

class Parma_Polyhedra_Library::c_streambuf
  : public std::basic_streambuf<char, std::char_traits<char> > {
public:
  //! Constructor.
  c_streambuf();

  //! Destructor.
  virtual ~c_streambuf();

protected:
  /*! \brief
    Gets a character in case of underflow.

    \remarks
    Specified by ISO/IEC 14882:1998: 27.5.2.4.3.
  */
  virtual int_type underflow();

  /*! \brief
    In case of underflow, gets a character and advances the next pointer.

    \remarks
    Specified by ISO/IEC 14882:1998: 27.5.2.4.3.
  */
  virtual int_type uflow();

  /*! \brief
    Gets a sequence of characters.

    \remarks
    Specified by ISO/IEC 14882:1998: 27.5.2.4.3.
  */
  virtual std::streamsize xsgetn(char_type* s, std::streamsize n);

  /*! \brief
    Puts character back in case of backup underflow.

    \remarks
    Specified by ISO/IEC 14882:1998: 27.5.2.4.4.
  */
  virtual int_type pbackfail(int_type c = traits_type::eof());

  /*! \brief
    Writes a sequence of characters.

    \remarks
    Specified by ISO/IEC 14882:1998: 27.5.2.4.5.
  */
  virtual std::streamsize xsputn(const char_type* s, std::streamsize n);

  /*! \brief
    Writes a character in case of overflow.

    Specified by ISO/IEC 14882:1998: 27.5.2.4.5.
  */
  virtual int_type overflow(int_type c);

  /*! \brief
    Synchronizes the stream buffer.

    Specified by ISO/IEC 14882:1998: 27.5.2.4.2.
  */
  virtual int sync();

private:
  //! Character type of the streambuf.
  typedef char char_type;

  //! Traits type of the streambuf.
  typedef std::char_traits<char_type> traits_type;

  //! Integer type of the streambuf.
  typedef traits_type::int_type int_type;

  //! Buffer for the last character read.
  int_type unget_char_buf;

  //! Buffer for next character
  int_type next_char_buf;

  virtual size_t cb_read(char *, size_t) {
    return 0;
  }
  virtual size_t cb_write(const char *, size_t) {
    return 0;
  }
  virtual int cb_sync() {
    return 0;
  }
  virtual int cb_flush() {
    return 0;
  }
};

/* Automatically generated from PPL source file ../src/c_streambuf_inlines.hh line 1. */
/* c_streambuf class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline
c_streambuf::c_streambuf()
  : unget_char_buf(traits_type::eof()), next_char_buf(traits_type::eof()) {
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/c_streambuf_defs.hh line 126. */

/* Automatically generated from PPL source file ../src/Integer_Interval.hh line 1. */
/* Integer_Interval class declaration and implementation.
*/


/* Automatically generated from PPL source file ../src/Interval_defs.hh line 1. */
/* Declarations for the Interval class and its constituents.
*/


/* Automatically generated from PPL source file ../src/Interval_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename Boundary, typename Info>
class Interval;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/assign_or_swap.hh line 1. */
/* The assign_or_swap() utility functions.
*/


/* Automatically generated from PPL source file ../src/Has_Assign_Or_Swap.hh line 1. */
/* Has_Assign_Or_Swap classes declarations.
*/


/* Automatically generated from PPL source file ../src/Has_Assign_Or_Swap.hh line 28. */

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface
  The assign_or_swap() method is not present by default.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T, typename Enable = void>
struct Has_Assign_Or_Swap : public False {
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface
  The assign_or_swap() method is present if it is present (!).
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
struct Has_Assign_Or_Swap<T,
                          typename Enable_If_Is<void (T::*)(T& x),
                                                &T::assign_or_swap>::type>
  : public True {
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/assign_or_swap.hh line 30. */

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface
  If there is an assign_or_swap() method, use it.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
inline typename Enable_If<Has_Assign_Or_Swap<T>::value, void>::type
assign_or_swap(T& to, T& from) {
  to.assign_or_swap(from);
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface
  If there is no assign_or_swap() method but copies are not slow, copy.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
inline typename Enable_If<!Has_Assign_Or_Swap<T>::value
                          && !Slow_Copy<T>::value, void>::type
assign_or_swap(T& to, T& from) {
  to = from;
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface
  If there is no assign_or_swap() and copies are slow, delegate to swap().
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
inline typename Enable_If<!Has_Assign_Or_Swap<T>::value
                          && Slow_Copy<T>::value, void>::type
assign_or_swap(T& to, T& from) {
  using std::swap;
  swap(to, from);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/intervals_defs.hh line 1. */
/* Helper classes for intervals.
*/


/* Automatically generated from PPL source file ../src/intervals_defs.hh line 29. */
#include <cstdlib>

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! The result of an operation on intervals.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
enum I_Result {
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Result may be empty.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_EMPTY = 1U,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Result may have only one value.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_SINGLETON = 2U,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief \hideinitializer
    Result may have more than one value, but it is not the domain universe.
  */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_SOME = 4U,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Result may be the domain universe.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_UNIVERSE = 8U,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Result is not empty.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_NOT_EMPTY = I_SINGLETON | I_SOME | I_UNIVERSE,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Result may be empty or not empty.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_ANY = I_EMPTY | I_NOT_EMPTY,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Result may be empty or not empty.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_NOT_UNIVERSE = I_EMPTY | I_SINGLETON | I_SOME,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Result is neither empty nor the domain universe.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_NOT_DEGENERATE = I_SINGLETON | I_SOME,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Result is definitely exact.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_EXACT = 16,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Result is definitely inexact.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_INEXACT = 32,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Operation has definitely changed the set.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_CHANGED = 64,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Operation has left the set definitely unchanged.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_UNCHANGED = 128,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \hideinitializer Operation is undefined for some combination of values.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  I_SINGULARITIES = 256
};

inline I_Result
operator|(I_Result a, I_Result b) {
  return static_cast<I_Result>(static_cast<unsigned>(a)
                               | static_cast<unsigned>(b));
}

inline I_Result
operator&(I_Result a, I_Result b) {
  return static_cast<I_Result>(static_cast<unsigned>(a)
                               & static_cast<unsigned>(b));
}

inline I_Result
operator-(I_Result a, I_Result b) {
    return static_cast<I_Result>(static_cast<unsigned>(a)
                                 & ~static_cast<unsigned>(b));
}

template <typename Criteria, typename T>
struct Use_By_Ref;

struct Use_Slow_Copy;
template <typename T>
struct Use_By_Ref<Use_Slow_Copy, T>
  : public Bool<Slow_Copy<T>::value> {
};

struct By_Value;
template <typename T>
struct Use_By_Ref<By_Value, T>
  : public False {
};

struct By_Ref;
template <typename T>
struct Use_By_Ref<By_Ref, T>
  : public True {
};

template <typename T, typename Criteria = Use_Slow_Copy, typename Enable = void>
class Val_Or_Ref;

template <typename T, typename Criteria>
class Val_Or_Ref<T, Criteria,
                 typename Enable_If<!Use_By_Ref<Criteria, T>::value>::type> {
  T value;
public:
  typedef T Arg_Type;
  typedef T Return_Type;
  Val_Or_Ref()
    : value() {
  }
  explicit Val_Or_Ref(Arg_Type v, bool = false)
    : value(v) {
  }
  Val_Or_Ref& operator=(Arg_Type v) {
    value = v;
    return *this;
  }
  void set(Arg_Type v, bool = false) {
    value = v;
  }
  Return_Type get() const {
    return value;
  }
  operator Return_Type() const {
    return get();
  }
};

template <typename T, typename Criteria>
class Val_Or_Ref<T, Criteria,
                 typename Enable_If<Use_By_Ref<Criteria, T>::value>::type> {
  const T* ptr;
public:
  typedef T& Arg_Type;
  typedef const T& Return_Type;
  Val_Or_Ref()
    : ptr(0) {
  }
  explicit Val_Or_Ref(Arg_Type v)
    : ptr(&v) {
  }
  Val_Or_Ref(const T& v, bool)
    : ptr(&v) {
  }
  Val_Or_Ref& operator=(Arg_Type v) {
    ptr = &v;
    return *this;
  }
  void set(Arg_Type v) {
    ptr = &v;
  }
  void set(const T& v, bool) {
    ptr = &v;
  }
  Return_Type get() const {
    return *ptr;
  }
  operator Return_Type() const {
    return get();
  }
};

class I_Constraint_Base {
};

template <typename Derived>
class I_Constraint_Common : public I_Constraint_Base {
public:
  template <typename T>
  Result convert_real(T& to) const {
    const Derived& c = static_cast<const Derived&>(*this);
    Result r = c.rel();
    switch (r) {
    case V_EMPTY:
    case V_LGE:
      return r;
    case V_LE:
      r = assign_r(to, c.value(), (ROUND_UP | ROUND_STRICT_RELATION));
      r = result_relation_class(r);
      if (r == V_EQ) {
        return V_LE;
      }
      goto lt;
    case V_LT:
      r = assign_r(to, c.value(), ROUND_UP);
      r = result_relation_class(r);
    lt:
      switch (r) {
      case V_EMPTY:
      case V_LT_PLUS_INFINITY:
      case V_EQ_MINUS_INFINITY:
        return r;
      case V_LT:
      case V_LE:
      case V_EQ:
        return V_LT;
      default:
        break;
      }
      break;
    case V_GE:
      r = assign_r(to, c.value(), (ROUND_DOWN | ROUND_STRICT_RELATION));
      r = result_relation_class(r);
      if (r == V_EQ) {
        return V_GE;
      }
      goto gt;
    case V_GT:
      r = assign_r(to, c.value(), ROUND_DOWN);
      r = result_relation_class(r);
    gt:
      switch (r) {
      case V_EMPTY:
      case V_GT_MINUS_INFINITY:
      case V_EQ_PLUS_INFINITY:
        return r;
      case V_LT:
      case V_LE:
      case V_EQ:
        return V_GT;
      default:
        break;
      }
      break;
    case V_EQ:
      r = assign_r(to, c.value(), ROUND_CHECK);
      r = result_relation_class(r);
      PPL_ASSERT(r != V_LT && r != V_GT);
      if (r == V_EQ) {
        return V_EQ;
      }
      else {
        return V_EMPTY;
      }
    case V_NE:
      r = assign_r(to, c.value(), ROUND_CHECK);
      r = result_relation_class(r);
      if (r == V_EQ) {
        return V_NE;
      }
      else {
        return V_LGE;
      }
    default:
      break;
    }
    PPL_UNREACHABLE;
    return V_EMPTY;
  }
  template <typename T>
  Result convert_real(T& to1, Result& rel2, T& to2) const {
    const Derived& c = static_cast<const Derived&>(*this);
    Result rel1;
    if (c.rel() != V_EQ) {
      rel2 = convert(to2);
      return V_LGE;
    }
    rel2 = assign_r(to2, c.value(), ROUND_UP);
    rel2 = result_relation_class(rel2);
    switch (rel2) {
    case V_EMPTY:
    case V_EQ_MINUS_INFINITY:
    case V_EQ:
      return V_LGE;
    default:
      break;
    }
    rel1 = assign_r(to1, c.value(), ROUND_DOWN);
    rel1 = result_relation_class(rel1);
    switch (rel1) {
    case V_EQ:
      PPL_ASSERT(rel2 == V_LE);
      goto eq;
    case V_EQ_PLUS_INFINITY:
    case V_EMPTY:
      rel2 = rel1;
      return V_LGE;
    case V_GE:
      if (rel2 == V_LE && to1 == to2) {
      eq:
        rel2 = V_EQ;
        return V_LGE;
      }
      /* Fall through*/
    case V_GT:
    case V_GT_MINUS_INFINITY:
      return rel1;
    default:
      PPL_UNREACHABLE;
      return V_EMPTY;
    }
    switch (rel2) {
    case V_LE:
    case V_LT:
    case V_LT_PLUS_INFINITY:
      return rel1;
    default:
      PPL_UNREACHABLE;
      return V_EMPTY;
    }
  }
  template <typename T>
  Result convert_integer(T& to) const {
    Result rel = convert_real(to);
    switch (rel) {
    case V_LT:
      if (is_integer(to)) {
        rel = sub_assign_r(to, to, T(1), (ROUND_UP | ROUND_STRICT_RELATION));
        rel = result_relation_class(rel);
        return (rel == V_EQ) ? V_LE : rel;
      }
      /* Fall through */
    case V_LE:
      rel = floor_assign_r(to, to, ROUND_UP);
      rel = result_relation_class(rel);
      PPL_ASSERT(rel == V_EQ);
      return V_LE;
    case V_GT:
      if (is_integer(to)) {
        rel = add_assign_r(to, to, T(1), (ROUND_DOWN | ROUND_STRICT_RELATION));
        rel = result_relation_class(rel);
        return (rel == V_EQ) ? V_GE : rel;
      }
      /* Fall through */
    case V_GE:
      rel = ceil_assign_r(to, to, ROUND_DOWN);
      rel = result_relation_class(rel);
      PPL_ASSERT(rel == V_EQ);
      return V_GE;
    case V_EQ:
      if (is_integer(to)) {
        return V_EQ;
      }
      return V_EMPTY;
    case V_NE:
      if (is_integer(to)) {
        return V_NE;
      }
      return V_LGE;
    default:
      return rel;
    }
  }
};

struct I_Constraint_Rel {
  Result rel;
  I_Constraint_Rel(Result r)
    : rel(r) {
    PPL_ASSERT(result_relation_class(r) == r);
  }
  I_Constraint_Rel(Relation_Symbol r)
    : rel(static_cast<Result>(r)) {
  }
  operator Result() const {
    return rel;
  }
};

template <typename T, typename Val_Or_Ref_Criteria = Use_Slow_Copy,
          bool extended = false>
class I_Constraint
  : public I_Constraint_Common<I_Constraint<T, Val_Or_Ref_Criteria,
                                            extended> > {
  typedef Val_Or_Ref<T, Val_Or_Ref_Criteria> Val_Ref;
  typedef typename Val_Ref::Arg_Type Arg_Type;
  typedef typename Val_Ref::Return_Type Return_Type;
  Result rel_;
  Val_Ref value_;
public:
  typedef T value_type;
  explicit I_Constraint()
    : rel_(V_LGE) {
  }
  I_Constraint(I_Constraint_Rel r, Arg_Type v)
    : rel_(r), value_(v) {
  }
  I_Constraint(I_Constraint_Rel r, const T& v, bool force)
    : rel_(r), value_(v, force) {
  }
  template <typename U>
  I_Constraint(I_Constraint_Rel r, const U& v)
    : rel_(r), value_(v) {
  }
  void set(I_Constraint_Rel r, Arg_Type v) {
    rel_ =  r;
    value_.set(v);
  }
  void set(I_Constraint_Rel r, const T& v, bool force) {
    rel_ =  r;
    value_.set(v, force);
  }
  template <typename U>
  void set(I_Constraint_Rel r, const U& v) {
    rel_ = r;
    value_.set(v);
  }
  Return_Type value() const {
    return value_;
  }
  Result rel() const {
    return rel_;
  }
};

template <typename T>
inline I_Constraint<T>
i_constraint(I_Constraint_Rel rel, const T& v) {
  return I_Constraint<T>(rel, v);
}

template <typename T>
inline I_Constraint<T>
i_constraint(I_Constraint_Rel rel, const T& v, bool force) {
  return I_Constraint<T>(rel, v, force);
}

template <typename T>
inline I_Constraint<T>
i_constraint(I_Constraint_Rel rel, T& v) {
  return I_Constraint<T>(rel, v);
}

template <typename T, typename Val_Or_Ref_Criteria>
inline I_Constraint<T, Val_Or_Ref_Criteria>
i_constraint(I_Constraint_Rel rel, const T& v, const Val_Or_Ref_Criteria&) {
  return I_Constraint<T, Val_Or_Ref_Criteria>(rel, v);
}

template <typename T, typename Val_Or_Ref_Criteria>
inline I_Constraint<T, Val_Or_Ref_Criteria>
i_constraint(I_Constraint_Rel rel, const T& v, bool force,
             const Val_Or_Ref_Criteria&) {
  return I_Constraint<T, Val_Or_Ref_Criteria>(rel, v, force);
}

template <typename T, typename Val_Or_Ref_Criteria>
inline I_Constraint<T, Val_Or_Ref_Criteria>
i_constraint(I_Constraint_Rel rel, T& v, const Val_Or_Ref_Criteria&) {
  return I_Constraint<T, Val_Or_Ref_Criteria>(rel, v);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Interval_Info_defs.hh line 1. */
/* Interval_Info class declaration and implementation.
*/


/* Automatically generated from PPL source file ../src/Boundary_defs.hh line 1. */
/* Interval boundary functions.
*/


/* Automatically generated from PPL source file ../src/Boundary_defs.hh line 28. */

namespace Parma_Polyhedra_Library {

namespace Boundary_NS {

struct Property {
  enum Type {
    SPECIAL_,
    OPEN_
  };
  typedef bool Value;
  static const Value default_value = true;
  static const Value unsupported_value = false;
  Property(Type t)
    : type(t) {
  }
  Type type;
};

static const Property SPECIAL(Property::SPECIAL_);
static const Property OPEN(Property::OPEN_);

enum Boundary_Type {
  LOWER = ROUND_DOWN,
  UPPER = ROUND_UP
};

inline Rounding_Dir
round_dir_check(Boundary_Type t, bool check = false) {
  if (check) {
    return static_cast<Rounding_Dir>(t) | ROUND_STRICT_RELATION;
  }
  else {
    return static_cast<Rounding_Dir>(t);
  }
}

template <typename T, typename Info>
inline Result
special_set_boundary_infinity(Boundary_Type type, T&, Info& info) {
  PPL_ASSERT(Info::store_special);
  info.set_boundary_property(type, SPECIAL);
  return V_EQ;
}

template <typename T, typename Info>
inline bool
special_is_open(Boundary_Type, const T&, const Info&) {
  return !Info::may_contain_infinity;
}

template <typename T, typename Info>
inline bool
normal_is_open(Boundary_Type type, const T& x, const Info& info) {
  if (Info::store_open) {
    return info.get_boundary_property(type, OPEN);
  }
  else {
    return !Info::store_special && !Info::may_contain_infinity
      && normal_is_boundary_infinity(type, x, info);
  }
}

template <typename T, typename Info>
inline bool
is_open(Boundary_Type type, const T& x, const Info& info) {
  if (Info::store_open) {
    return info.get_boundary_property(type, OPEN);
  }
  else {
    return !Info::may_contain_infinity
      && is_boundary_infinity(type, x, info);
  }
}

template <typename T, typename Info>
inline Result
set_unbounded(Boundary_Type type, T& x, Info& info) {
  PPL_COMPILE_TIME_CHECK(Info::store_special
                         || std::numeric_limits<T>::is_bounded
                         || std::numeric_limits<T>::has_infinity,
                         "unbounded is not representable");
  Result r;
  if (Info::store_special) {
    r = special_set_boundary_infinity(type, x, info);
  }
  else if (type == LOWER) {
    r = assign_r(x, MINUS_INFINITY, ROUND_UP);
  }
  else {
    r = assign_r(x, PLUS_INFINITY, ROUND_DOWN);
  }
  if (result_relation(r) == VR_EQ && !Info::may_contain_infinity) {
    info.set_boundary_property(type, OPEN);
  }
  return r;
}

template <typename T, typename Info>
inline Result
set_minus_infinity(Boundary_Type type, T& x, Info& info, bool open = false) {
  if (open) {
    PPL_ASSERT(type == LOWER);
  }
  else {
    PPL_ASSERT(Info::may_contain_infinity);
  }
  Result r;
  if (Info::store_special) {
    PPL_ASSERT(type == LOWER);
    r = special_set_boundary_infinity(type, x, info);
  }
  else {
    r = assign_r(x, MINUS_INFINITY, round_dir_check(type));
    PPL_ASSERT(result_representable(r));
  }
  if (open || result_relation(r) != VR_EQ) {
    info.set_boundary_property(type, OPEN);
  }
  return r;
}

template <typename T, typename Info>
inline Result
set_plus_infinity(Boundary_Type type, T& x, Info& info, bool open = false) {
  if (open) {
    PPL_ASSERT(type == UPPER);
  }
  else {
    PPL_ASSERT(Info::may_contain_infinity);
  }
  Result r;
  if (Info::store_special) {
    PPL_ASSERT(type == UPPER);
    r = special_set_boundary_infinity(type, x, info);
  }
  else {
    r = assign_r(x, PLUS_INFINITY, round_dir_check(type));
    PPL_ASSERT(result_representable(r));
  }
  if (open || result_relation(r) != VR_EQ) {
    info.set_boundary_property(type, OPEN);
  }
  return r;
}

template <typename T, typename Info>
inline Result
set_boundary_infinity(Boundary_Type type, T& x, Info& info, bool open = false) {
  PPL_ASSERT(open || Info::may_contain_infinity);
  Result r;
  if (Info::store_special) {
    r = special_set_boundary_infinity(type, x, info);
  }
  else if (type == LOWER) {
    r = assign_r(x, MINUS_INFINITY, round_dir_check(type));
  }
  else {
    r = assign_r(x, PLUS_INFINITY, round_dir_check(type));
  }
  PPL_ASSERT(result_representable(r));
  if (open) {
    info.set_boundary_property(type, OPEN);
  }
  return r;
}

template <typename T, typename Info>
inline bool
is_domain_inf(Boundary_Type type, const T& x, const Info& info) {
  if (Info::store_special && type == LOWER) {
    return info.get_boundary_property(type, SPECIAL);
  }
  else if (std::numeric_limits<T>::has_infinity) {
    return Parma_Polyhedra_Library::is_minus_infinity(x);
  }
  else if (std::numeric_limits<T>::is_bounded) {
    return x == std::numeric_limits<T>::min();
  }
  else {
    return false;
  }
}

template <typename T, typename Info>
inline bool
is_domain_sup(Boundary_Type type, const T& x, const Info& info) {
  if (Info::store_special && type == UPPER) {
    return info.get_boundary_property(type, SPECIAL);
  }
  else if (std::numeric_limits<T>::has_infinity) {
    return Parma_Polyhedra_Library::is_plus_infinity(x);
  }
  else if (std::numeric_limits<T>::is_bounded) {
      return x == std::numeric_limits<T>::max();
  }
  else {
    return false;
  }
}

template <typename T, typename Info>
inline bool
normal_is_boundary_infinity(Boundary_Type type, const T& x, const Info&) {
  if (!std::numeric_limits<T>::has_infinity) {
    return false;
  }
  if (type == LOWER) {
    return Parma_Polyhedra_Library::is_minus_infinity(x);
  }
  else {
    return Parma_Polyhedra_Library::is_plus_infinity(x);
  }
}

template <typename T, typename Info>
inline bool
is_boundary_infinity(Boundary_Type type, const T& x, const Info& info) {
  if (Info::store_special) {
    return info.get_boundary_property(type, SPECIAL);
  }
  else {
    return normal_is_boundary_infinity(type, x, info);
  }
}

template <typename T, typename Info>
inline bool
normal_is_reverse_infinity(Boundary_Type type, const T& x, const Info&) {
  if (!Info::may_contain_infinity) {
    return false;
  }
  else if (type == LOWER) {
    return Parma_Polyhedra_Library::is_plus_infinity(x);
  }
  else {
    return Parma_Polyhedra_Library::is_minus_infinity(x);
  }
}

template <typename T, typename Info>
inline bool
is_minus_infinity(Boundary_Type type, const T& x, const Info& info) {
  if (type == LOWER) {
    if (Info::store_special) {
      return info.get_boundary_property(type, SPECIAL);
    }
    else {
      return normal_is_boundary_infinity(type, x, info);
    }
  }
  else {
    return !Info::store_special && normal_is_reverse_infinity(type, x, info);
  }
}

template <typename T, typename Info>
inline bool
is_plus_infinity(Boundary_Type type, const T& x, const Info& info) {
  if (type == UPPER) {
    if (Info::store_special) {
      return info.get_boundary_property(type, SPECIAL);
    }
    else {
      return normal_is_boundary_infinity(type, x, info);
    }
  }
  else {
    return !Info::store_special && normal_is_reverse_infinity(type, x, info);
  }
}

template <typename T, typename Info>
inline bool
is_reverse_infinity(Boundary_Type type, const T& x, const Info& info) {
  return normal_is_reverse_infinity(type, x, info);
}

template <typename T, typename Info>
inline int
infinity_sign(Boundary_Type type, const T& x, const Info& info) {
  if (is_boundary_infinity(type, x, info)) {
    return (type == LOWER) ? -1 : 1;
  }
  else if (is_reverse_infinity(type, x, info)) {
    return (type == UPPER) ? -1 : 1;
  }
  else {
    return 0;
  }
}

template <typename T, typename Info>
inline bool
is_boundary_infinity_closed(Boundary_Type type, const T& x, const Info& info) {
  return Info::may_contain_infinity
    && !info.get_boundary_property(type, OPEN)
    && is_boundary_infinity(type, x, info);
}

template <typename Info>
inline bool
boundary_infinity_is_open(Boundary_Type type, const Info& info) {
  return !Info::may_contain_infinity
    || info.get_boundary_property(type, OPEN);
}

template <typename T, typename Info>
inline int
sgn_b(Boundary_Type type, const T& x, const Info& info) {
  if (info.get_boundary_property(type, SPECIAL)) {
    return (type == LOWER) ? -1 : 1;
  }
  else {
    // The following Parma_Polyhedra_Library:: qualification is to work
    // around a bug of GCC 4.0.x.
    return Parma_Polyhedra_Library::sgn(x);
  }
}

template <typename T, typename Info>
inline int
sgn(Boundary_Type type, const T& x, const Info& info) {
  int sign = sgn_b(type, x, info);
  if (x == 0 && info.get_boundary_property(type, OPEN)) {
    return (type == LOWER) ? -1 : 1;
  }
  else {
    return sign;
  }
}

template <typename T1, typename Info1, typename T2, typename Info2>
inline bool
eq(Boundary_Type type1, const T1& x1, const Info1& info1,
   Boundary_Type type2, const T2& x2, const Info2& info2) {
  if (type1 == type2) {
    if (is_open(type1, x1, info1)
        != is_open(type2, x2, info2)) {
      return false;
    }
  }
  else if (is_open(type1, x1, info1)
           || is_open(type2, x2, info2)) {
    return false;
  }
  if (is_minus_infinity(type1, x1, info1)) {
    return is_minus_infinity(type2, x2, info2);
  }
  else if (is_plus_infinity(type1, x1, info1)) {
    return is_plus_infinity(type2, x2, info2);
  }
  else if (is_minus_infinity(type2, x2, info2)
           || is_plus_infinity(type2, x2, info2)) {
    return false;
  }
  else {
    return equal(x1, x2);
  }
}

template <typename T1, typename Info1, typename T2, typename Info2>
inline bool
lt(Boundary_Type type1, const T1& x1, const Info1& info1,
   Boundary_Type type2, const T2& x2, const Info2& info2) {
  if (is_open(type1, x1, info1)) {
    if (type1 == UPPER
        && (type2 == LOWER
            || !is_open(type2, x2, info2))) {
      goto le;
    }
  }
  else if (type2 == LOWER
           && is_open(type2, x2, info2)) {
  le:
    if (is_minus_infinity(type1, x1, info1)
        || is_plus_infinity(type2, x2, info2)) {
      return true;
    }
    if (is_plus_infinity(type1, x1, info1)
        || is_minus_infinity(type2, x2, info2)) {
      return false;
    }
    else {
      return less_or_equal(x1, x2);
    }
  }
  if (is_plus_infinity(type1, x1, info1)
      || is_minus_infinity(type2, x2, info2)) {
    return false;
  }
  if (is_minus_infinity(type1, x1, info1)
      || is_plus_infinity(type2, x2, info2)) {
    return true;
  }
  else {
    return less_than(x1, x2);
  }
}

template <typename T1, typename Info1, typename T2, typename Info2>
inline bool
gt(Boundary_Type type1, const T1& x1, const Info1& info1,
   Boundary_Type type2, const T2& x2, const Info2& info2) {
  return lt(type2, x2, info2, type1, x1, info1);
}

template <typename T1, typename Info1, typename T2, typename Info2>
inline bool
le(Boundary_Type type1, const T1& x1, const Info1& info1,
   Boundary_Type type2, const T2& x2, const Info2& info2) {
  return !gt(type1, x1, info1, type2, x2, info2);
}

template <typename T1, typename Info1, typename T2, typename Info2>
inline bool
ge(Boundary_Type type1, const T1& x1, const Info1& info1,
   Boundary_Type type2, const T2& x2, const Info2& info2) {
  return !lt(type1, x1, info1, type2, x2, info2);
}

template <typename T, typename Info>
inline Result
adjust_boundary(Boundary_Type type, T& x, Info& info,
                bool open, Result r) {
  r = result_relation_class(r);
  if (type == LOWER) {
    switch (r) {
    case V_GT_MINUS_INFINITY:
      open = true;
      /* Fall through */
    case V_EQ_MINUS_INFINITY:
      if (!Info::store_special) {
        return r;
      }
      if (open) {
        info.set_boundary_property(type, OPEN);
      }
      return special_set_boundary_infinity(type, x, info);
    case V_GT:
      open = true;
      /* Fall through */
    case V_GE:
    case V_EQ:
      if (open) {
        info.set_boundary_property(type, OPEN);
      }
      return r;
    default:
      PPL_UNREACHABLE;
      return V_NAN;
    }
  }
  else {
    switch (r) {
    case V_LT_PLUS_INFINITY:
      open = true;
      /* Fall through */
    case V_EQ_PLUS_INFINITY:
      if (!Info::store_special) {
        return r;
      }
      if (open) {
        info.set_boundary_property(type, OPEN);
      }
      return special_set_boundary_infinity(type, x, info);
    case V_LT:
      open = true;
      /* Fall through */
    case V_LE:
    case V_EQ:
      if (open) {
        info.set_boundary_property(type, OPEN);
      }
      return r;
    default:
      PPL_UNREACHABLE;
      return V_NAN;
    }
  }
}

template <typename To, typename To_Info, typename T, typename Info>
inline Result
complement(Boundary_Type to_type, To& to, To_Info& to_info,
           Boundary_Type type, const T& x, const Info& info) {
  PPL_ASSERT(to_type != type);
  bool should_shrink;
  if (info.get_boundary_property(type, SPECIAL)) {
    should_shrink = !special_is_open(type, x, info);
    if (type == LOWER) {
      return set_minus_infinity(to_type, to, to_info, should_shrink);
    }
    else {
      return set_plus_infinity(to_type, to, to_info, should_shrink);
    }
  }
  should_shrink = !normal_is_open(type, x, info);
  bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open));
  Result r = assign_r(to, x, round_dir_check(to_type, check));
  return adjust_boundary(to_type, to, to_info, should_shrink, r);
}

template <typename To, typename To_Info, typename T, typename Info>
inline Result
assign(Boundary_Type to_type, To& to, To_Info& to_info,
       Boundary_Type type, const T& x, const Info& info,
       bool should_shrink = false) {
  PPL_ASSERT(to_type == type);
  if (info.get_boundary_property(type, SPECIAL)) {
    should_shrink = (should_shrink || special_is_open(type, x, info));
    return set_boundary_infinity(to_type, to, to_info, should_shrink);
  }
  should_shrink = (should_shrink || normal_is_open(type, x, info));
  const bool check
    = (To_Info::check_inexact || (!should_shrink && To_Info::store_open));
  const Result r = assign_r(to, x, round_dir_check(to_type, check));
  return adjust_boundary(to_type, to, to_info, should_shrink, r);
}

template <typename To, typename To_Info, typename T, typename Info>
inline Result
min_assign(Boundary_Type to_type, To& to, To_Info& to_info,
           Boundary_Type type, const T& x, const Info& info) {
  if (lt(type, x, info, to_type, to, to_info)) {
    to_info.clear_boundary_properties(to_type);
    return assign(to_type, to, to_info, type, x, info);
  }
  return V_EQ;
}

template <typename To, typename To_Info, typename T1, typename Info1, typename T2, typename Info2>
inline Result
min_assign(Boundary_Type to_type, To& to, To_Info& to_info,
           Boundary_Type type1, const T1& x1, const Info1& info1,
           Boundary_Type type2, const T2& x2, const Info2& info2) {
  if (lt(type1, x1, info1, type2, x2, info2)) {
    return assign(to_type, to, to_info, type1, x1, info1);
  }
  else {
    return assign(to_type, to, to_info, type2, x2, info2);
  }
}

template <typename To, typename To_Info, typename T, typename Info>
inline Result
max_assign(Boundary_Type to_type, To& to, To_Info& to_info,
           Boundary_Type type, const T& x, const Info& info) {
  if (gt(type, x, info, to_type, to, to_info)) {
    to_info.clear_boundary_properties(to_type);
    return assign(to_type, to, to_info, type, x, info);
  }
  return V_EQ;
}

template <typename To, typename To_Info, typename T1, typename Info1, typename T2, typename Info2>
inline Result
max_assign(Boundary_Type to_type, To& to, To_Info& to_info,
           Boundary_Type type1, const T1& x1, const Info1& info1,
           Boundary_Type type2, const T2& x2, const Info2& info2) {
  if (gt(type1, x1, info1, type2, x2, info2)) {
    return assign(to_type, to, to_info, type1, x1, info1);
  }
  else {
    return assign(to_type, to, to_info, type2, x2, info2);
  }
}

template <typename To, typename To_Info, typename T, typename Info>
inline Result
neg_assign(Boundary_Type to_type, To& to, To_Info& to_info,
           Boundary_Type type, const T& x, const Info& info) {
  PPL_ASSERT(to_type != type);
  bool should_shrink;
  if (info.get_boundary_property(type, SPECIAL)) {
    should_shrink = special_is_open(type, x, info);
    return set_boundary_infinity(to_type, to, to_info, should_shrink);
  }
  should_shrink = normal_is_open(type, x, info);
  bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open));
  Result r = neg_assign_r(to, x, round_dir_check(to_type, check));
  return adjust_boundary(to_type, to, to_info, should_shrink, r);
}

template <typename To, typename To_Info, typename T1, typename Info1, typename T2, typename Info2>
inline Result
add_assign(Boundary_Type to_type, To& to, To_Info& to_info,
           Boundary_Type type1, const T1& x1, const Info1& info1,
           Boundary_Type type2, const T2& x2, const Info2& info2) {
  PPL_ASSERT(type1 == type2);
  bool should_shrink;
  if (is_boundary_infinity(type1, x1, info1)) {
    should_shrink = (boundary_infinity_is_open(type1, info1)
                     && !is_boundary_infinity_closed(type2, x2, info2));
    return set_boundary_infinity(to_type, to, to_info, should_shrink);
  }
  else if (is_boundary_infinity(type2, x2, info2)) {
    should_shrink = (boundary_infinity_is_open(type2, info2)
                     && !is_boundary_infinity_closed(type1, x1, info1));
    return set_boundary_infinity(to_type, to, to_info, should_shrink);
  }
  should_shrink = (normal_is_open(type1, x1, info1)
                   || normal_is_open(type2, x2, info2));
  bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open));
  // FIXME: extended handling is not needed
  Result r = add_assign_r(to, x1, x2, round_dir_check(to_type, check));
  return adjust_boundary(to_type, to, to_info, should_shrink, r);
}

template <typename To, typename To_Info, typename T1, typename Info1, typename T2, typename Info2>
inline Result
sub_assign(Boundary_Type to_type, To& to, To_Info& to_info,
           Boundary_Type type1, const T1& x1, const Info1& info1,
           Boundary_Type type2, const T2& x2, const Info2& info2) {
  PPL_ASSERT(type1 != type2);
  bool should_shrink;
  if (is_boundary_infinity(type1, x1, info1)) {
    should_shrink = (boundary_infinity_is_open(type1, info1)
                     && !is_boundary_infinity_closed(type2, x2, info2));
    return set_boundary_infinity(to_type, to, to_info, should_shrink);
  }
  else if (is_boundary_infinity(type2, x2, info2)) {
    should_shrink = (boundary_infinity_is_open(type2, info2)
                     && !is_boundary_infinity_closed(type1, x1, info1));
    return set_boundary_infinity(to_type, to, to_info, should_shrink);
  }
  should_shrink = (normal_is_open(type1, x1, info1)
                   || normal_is_open(type2, x2, info2));
  bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open));
  // FIXME: extended handling is not needed
  Result r = sub_assign_r(to, x1, x2, round_dir_check(to_type, check));
  return adjust_boundary(to_type, to, to_info, should_shrink, r);
}

template <typename To, typename To_Info, typename T1, typename Info1, typename T2, typename Info2>
inline Result
mul_assign(Boundary_Type to_type, To& to, To_Info& to_info,
           Boundary_Type type1, const T1& x1, const Info1& info1,
           Boundary_Type type2, const T2& x2, const Info2& info2) {
  bool should_shrink;
  if (is_boundary_infinity(type1, x1, info1)) {
    should_shrink = (boundary_infinity_is_open(type1, info1)
                     && !is_boundary_infinity_closed(type2, x2, info2));
    return set_boundary_infinity(to_type, to, to_info, should_shrink);
  }
  else if (is_boundary_infinity(type2, x2, info2)) {
    should_shrink = (boundary_infinity_is_open(type2, info2)
                     && !is_boundary_infinity_closed(type1, x1, info1));
    return set_boundary_infinity(to_type, to, to_info, should_shrink);
  }
  should_shrink = (normal_is_open(type1, x1, info1)
                   || normal_is_open(type2, x2, info2));
  bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open));
  PPL_ASSERT(x1 != Constant<0>::value && x2 != Constant<0>::value);
  // FIXME: extended handling is not needed
  Result r = mul_assign_r(to, x1, x2, round_dir_check(to_type, check));
  return adjust_boundary(to_type, to, to_info, should_shrink, r);
}

template <typename To, typename To_Info>
inline Result
set_zero(Boundary_Type to_type, To& to, To_Info& to_info, bool should_shrink) {
  bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open));
  Result r = assign_r(to, Constant<0>::value, round_dir_check(to_type, check));
  return adjust_boundary(to_type, to, to_info, should_shrink, r);
}

template <typename To, typename To_Info, typename T1, typename Info1, typename T2, typename Info2>
inline Result
mul_assign_z(Boundary_Type to_type, To& to, To_Info& to_info,
             Boundary_Type type1, const T1& x1, const Info1& info1, int x1s,
             Boundary_Type type2, const T2& x2, const Info2& info2, int x2s) {
  bool should_shrink;
  if (x1s != 0) {
    if (x2s != 0) {
      return mul_assign(to_type, to, to_info,
                        type1, x1, info1,
                        type2, x2, info2);
    }
    else {
      should_shrink = info2.get_boundary_property(type2, OPEN);
    }
  }
  else {
    should_shrink = (info1.get_boundary_property(type1, OPEN)
                     && (x2s != 0 || info2.get_boundary_property(type2, OPEN)));
  }
  return set_zero(to_type, to, to_info, should_shrink);
}

template <typename To, typename To_Info, typename T1, typename Info1, typename T2, typename Info2>
inline Result
div_assign(Boundary_Type to_type, To& to, To_Info& to_info,
           Boundary_Type type1, const T1& x1, const Info1& info1,
           Boundary_Type type2, const T2& x2, const Info2& info2) {
  bool should_shrink;
  if (is_boundary_infinity(type1, x1, info1)) {
    should_shrink = boundary_infinity_is_open(type1, info1);
    return set_boundary_infinity(to_type, to, to_info, should_shrink);
  }
  else if (is_boundary_infinity(type2, x2, info2)) {
    should_shrink = boundary_infinity_is_open(type2, info2);
    return set_zero(to_type, to, to_info, should_shrink);
  }
  should_shrink = (normal_is_open(type1, x1, info1)
                   || normal_is_open(type2, x2, info2));
  bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open));
  PPL_ASSERT(x1 != Constant<0>::value && x2 != Constant<0>::value);
  // FIXME: extended handling is not needed
  Result r = div_assign_r(to, x1, x2, round_dir_check(to_type, check));
  return adjust_boundary(to_type, to, to_info, should_shrink, r);
}


template <typename To, typename To_Info, typename T1, typename Info1, typename T2, typename Info2>
inline Result
div_assign_z(Boundary_Type to_type, To& to, To_Info& to_info,
             Boundary_Type type1, const T1& x1, const Info1& info1, int x1s,
             Boundary_Type type2, const T2& x2, const Info2& info2, int x2s) {
  if (x1s != 0) {
    if (x2s != 0) {
      return div_assign(to_type, to, to_info,
                        type1, x1, info1,
                        type2, x2, info2);
    }
    else {
      return set_boundary_infinity(to_type, to, to_info, true);
    }
  }
  else {
    bool should_shrink = info1.get_boundary_property(type1, OPEN)
      && !is_boundary_infinity_closed(type2, x2, info2);
    return set_zero(to_type, to, to_info, should_shrink);
  }
}

template <typename To, typename To_Info, typename T, typename Info>
inline Result
umod_2exp_assign(Boundary_Type to_type, To& to, To_Info& to_info,
                 Boundary_Type type, const T& x, const Info& info,
                 unsigned int exp) {
  PPL_ASSERT(to_type == type);
  bool should_shrink;
  if (is_boundary_infinity(type, x, info)) {
    should_shrink = boundary_infinity_is_open(type, info);
    return set_boundary_infinity(to_type, to, to_info, should_shrink);
  }
  should_shrink = normal_is_open(type, x, info);
  bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open));
  Result r = umod_2exp_assign_r(to, x, exp, round_dir_check(to_type, check));
  return adjust_boundary(to_type, to, to_info, should_shrink, r);
}

template <typename To, typename To_Info, typename T, typename Info>
inline Result
smod_2exp_assign(Boundary_Type to_type, To& to, To_Info& to_info,
                 Boundary_Type type, const T& x, const Info& info,
                 unsigned int exp) {
  PPL_ASSERT(to_type == type);
  bool should_shrink;
  if (is_boundary_infinity(type, x, info)) {
    should_shrink = boundary_infinity_is_open(type, info);
    return set_boundary_infinity(to_type, to, to_info, should_shrink);
  }
  should_shrink = normal_is_open(type, x, info);
  bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open));
  Result r = smod_2exp_assign_r(to, x, exp, round_dir_check(to_type, check));
  return adjust_boundary(to_type, to, to_info, should_shrink, r);
}

} // namespace Boundary_NS

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Interval_Info_defs.hh line 28. */
#include <iostream>

namespace Parma_Polyhedra_Library {

namespace Interval_NS {

struct Property {
  enum Type {
    CARDINALITY_0_,
    CARDINALITY_1_,
    CARDINALITY_IS_
  };
  typedef bool Value;
  static const Value default_value = true;
  static const Value unsupported_value = false;
  Property(Type t)
    : type(t) {
  }
  Type type;
};

const Property CARDINALITY_0(Property::CARDINALITY_0_);
const Property CARDINALITY_1(Property::CARDINALITY_1_);
const Property CARDINALITY_IS(Property::CARDINALITY_IS_);

template <typename T>
inline void
reset_bits(T& bits) {
  bits = 0;
}

template <typename T>
inline void
reset_bit(T& bits, unsigned int bit) {
  bits &= ~(static_cast<T>(1) << bit);
}

template <typename T>
inline void
set_bit(T& bits, unsigned int bit, bool value) {
  if (value) {
    bits |= static_cast<T>(1) << bit;
  }
  else {
    reset_bit(bits, bit);
  }
}

template <typename T>
inline bool
get_bit(const T& bits, unsigned int bit) {
  return (bits & (static_cast<T>(1) << bit)) != 0;
}

template <typename T>
inline void
set_bits(T& bits, unsigned int start, unsigned int len, T value) {
  bits &= ~(((static_cast<T>(1) << len) - 1) << start);
  bits |= value << start;
}

template <typename T>
inline T
get_bits(T& bits, unsigned int start, unsigned int len) {
  return (bits >> start) & ((static_cast<T>(1) << len) - 1);
}

} // namespace Interval_NS

using namespace Interval_NS;
using namespace Boundary_NS;


template <typename Policy>
class Interval_Info_Null {
public:
  const_bool_nodef(may_be_empty, Policy::may_be_empty);
  const_bool_nodef(may_contain_infinity, Policy::may_contain_infinity);
  const_bool_nodef(check_inexact, Policy::check_inexact);
  const_bool_nodef(store_special, false);
  const_bool_nodef(store_open, false);
  const_bool_nodef(cache_empty, false);
  const_bool_nodef(cache_singleton, false);
  Interval_Info_Null() {
  }
  void clear() {
  }
  void clear_boundary_properties(Boundary_Type) {
  }

  template <typename Property>
  void set_boundary_property(Boundary_Type, const Property&, typename Property::Value = Property::default_value) {
  }
  template <typename Property>
  typename Property::Value get_boundary_property(Boundary_Type, const Property&) const {
    return Property::unsupported_value;
  }
  template <typename Property>
  void set_interval_property(const Property&, typename Property::Value = Property::default_value) {
  }
  template <typename Property>
  typename Property::Value get_interval_property(const Property&) const {
    return Property::unsupported_value;
  }

  //! Swaps \p *this with \p y.
  void m_swap(Interval_Info_Null& y);

  void ascii_dump(std::ostream& s) const;
  bool ascii_load(std::istream& s);
};

template <typename Policy>
class Interval_Info_Null_Open : public Interval_Info_Null<Policy> {
public:
  const_bool_nodef(store_open, true);
  Interval_Info_Null_Open(bool o)
    : open(o) {
  }
  bool get_boundary_property(Boundary_Type,
                             const Boundary_NS::Property& p) const {
    if (p.type == Boundary_NS::Property::OPEN_) {
      return open;
    }
    else {
      return Boundary_NS::Property::unsupported_value;
    }
  }

  void ascii_dump(std::ostream& s) const;
  bool ascii_load(std::istream& s);

private:
  bool open;
};


template <typename T, typename Policy>
class Interval_Info_Bitset {
public:
  const_bool_nodef(may_be_empty, Policy::may_be_empty);
  const_bool_nodef(may_contain_infinity, Policy::may_contain_infinity);
  const_bool_nodef(check_inexact, Policy::check_inexact);
  const_bool_nodef(store_special, Policy::store_special);
  const_bool_nodef(store_open, Policy::store_open);
  const_bool_nodef(cache_empty, Policy::cache_empty);
  const_bool_nodef(cache_singleton, Policy::cache_singleton);
  const_int_nodef(lower_special_bit, Policy::next_bit);
  const_int_nodef(lower_open_bit, lower_special_bit + (store_special ? 1 : 0));
  const_int_nodef(upper_special_bit, lower_open_bit + (store_open ? 1 : 0));
  const_int_nodef(upper_open_bit, upper_special_bit + (store_special ? 1 : 0));
  const_int_nodef(cardinality_is_bit, upper_open_bit + (store_open ? 1 : 0));
  const_int_nodef(cardinality_0_bit, cardinality_is_bit
                  + ((cache_empty || cache_singleton) ? 1 : 0));
  const_int_nodef(cardinality_1_bit, cardinality_0_bit + (cache_empty ? 1 : 0));
  const_int_nodef(next_bit, cardinality_1_bit + (cache_singleton ? 1 : 0));

  Interval_Info_Bitset() {
    // FIXME: would we have speed benefits with uninitialized info?
    // (Dirty_Temp)
    clear();
  }

  void clear() {
    reset_bits(bitset);
  }
  void clear_boundary_properties(Boundary_Type t) {
    set_boundary_property(t, SPECIAL, false);
    set_boundary_property(t, OPEN, false);
  }
  void set_boundary_property(Boundary_Type t,
                             const Boundary_NS::Property& p,
                             bool value = true) {
    switch (p.type) {
    case Boundary_NS::Property::SPECIAL_:
      if (store_special) {
        if (t == LOWER) {
          set_bit(bitset, lower_special_bit, value);
        }
        else {
          set_bit(bitset, upper_special_bit, value);
        }
      }
      break;
    case Boundary_NS::Property::OPEN_:
      if (store_open) {
        if (t == LOWER) {
          set_bit(bitset, lower_open_bit, value);
        }
        else {
          set_bit(bitset, upper_open_bit, value);
        }
      }
      break;
    default:
      break;
    }
  }
  bool get_boundary_property(Boundary_Type t, const Boundary_NS::Property& p) const {
    switch (p.type) {
    case Boundary_NS::Property::SPECIAL_:
      if (!store_special) {
        return false;
      }
      if (t == LOWER) {
        return get_bit(bitset, lower_special_bit);
      }
      else {
        return get_bit(bitset, upper_special_bit);
      }
    case Boundary_NS::Property::OPEN_:
      if (!store_open) {
        return false;
      }
      else if (t == LOWER) {
        return get_bit(bitset, lower_open_bit);
      }
      else {
        return get_bit(bitset, upper_open_bit);
      }
    default:
      return false;
    }
  }
  void set_interval_property(const Interval_NS::Property& p, bool value = true) {
    switch (p.type) {
    case Interval_NS::Property::CARDINALITY_0_:
      if (cache_empty) {
        set_bit(bitset, cardinality_0_bit, value);
      }
      break;
    case Interval_NS::Property::CARDINALITY_1_:
      if (cache_singleton) {
        set_bit(bitset, cardinality_1_bit, value);
      }
      break;
    case Interval_NS::Property::CARDINALITY_IS_:
      if (cache_empty || cache_singleton) {
        set_bit(bitset, cardinality_is_bit, value);
      }
      break;
    default:
      break;
    }
  }
  bool get_interval_property(Interval_NS::Property p) const {
    switch (p.type) {
    case Interval_NS::Property::CARDINALITY_0_:
      return cache_empty && get_bit(bitset, cardinality_0_bit);
    case Interval_NS::Property::CARDINALITY_1_:
      return cache_singleton && get_bit(bitset, cardinality_1_bit);
    case Interval_NS::Property::CARDINALITY_IS_:
      return (cache_empty || cache_singleton)
        && get_bit(bitset, cardinality_is_bit);
    default:
      return false;
    }
  }

  //! Swaps \p *this with \p y.
  void m_swap(Interval_Info_Bitset& y);

  void ascii_dump(std::ostream& s) const;
  bool ascii_load(std::istream& s);

protected:
  T bitset;
};

}

/* Automatically generated from PPL source file ../src/Interval_Info_inlines.hh line 1. */
/* Interval_Info class implementation: inline functions.
*/


#include <iomanip>

namespace Parma_Polyhedra_Library {

template <typename Policy>
inline void
Interval_Info_Null<Policy>::m_swap(Interval_Info_Null<Policy>&) {
}

template <typename Policy>
inline void
Interval_Info_Null<Policy>::ascii_dump(std::ostream&) const {
}

template <typename Policy>
inline bool
Interval_Info_Null<Policy>::ascii_load(std::istream&) {
  return true;
}

template <typename Policy>
inline void
Interval_Info_Null_Open<Policy>::ascii_dump(std::ostream& s) const {
  s << (open ? "open" : "closed");
}

template <typename Policy>
inline bool
Interval_Info_Null_Open<Policy>::ascii_load(std::istream& s) {
  std::string str;
  if (!(s >> str)) {
    return false;
  }
  if (str == "open") {
    open = true;
    return true;
  }
  if (str == "closed") {
    open = false;
    return true;
  }
  return false;
}

template <typename T, typename Policy>
inline void
Interval_Info_Bitset<T, Policy>::m_swap(Interval_Info_Bitset<T, Policy>& y) {
  using std::swap;
  swap(bitset, y.bitset);
}

template <typename T, typename Policy>
inline void
Interval_Info_Bitset<T, Policy>::ascii_dump(std::ostream& s) const {
  const std::ios::fmtflags old_flags = s.setf(std::ios::hex,
                                              std::ios::basefield);
  s << bitset;
  s.flags(old_flags);
}

template <typename T, typename Policy>
inline bool
Interval_Info_Bitset<T, Policy>::ascii_load(std::istream& s) {
  const std::ios::fmtflags old_flags = s.setf(std::ios::hex,
                                              std::ios::basefield);
  s >> bitset;
  s.flags(old_flags);
  return !s.fail();
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Interval_Info_Null */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Policy>
inline void
swap(Interval_Info_Null<Policy>& x, Interval_Info_Null<Policy>& y) {
  x.m_swap(y);
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Interval_Info_Bitset */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T, typename Policy>
inline void
swap(Interval_Info_Bitset<T, Policy>& x, Interval_Info_Bitset<T, Policy>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Interval_Info_defs.hh line 300. */

/* Automatically generated from PPL source file ../src/Interval_defs.hh line 33. */
#include <iosfwd>

namespace Parma_Polyhedra_Library {

enum Ternary { T_YES, T_NO, T_MAYBE };

inline I_Result
combine(Result l, Result u) {
  const unsigned res
    = static_cast<unsigned>(l) | (static_cast<unsigned>(u) << 6);
  return static_cast<I_Result>(res);
}

struct Interval_Base {
};

using namespace Boundary_NS;
using namespace Interval_NS;

template <typename T, typename Enable = void>
struct Is_Singleton : public Is_Native_Or_Checked<T> {};

template <typename T>
struct Is_Interval : public Is_Same_Or_Derived<Interval_Base, T> {};

//! A generic, not necessarily closed, possibly restricted interval.
/*! \ingroup PPL_CXX_interface
  The class template type parameter \p Boundary represents the type
  of the interval boundaries, and can be chosen, among other possibilities,
  within one of the following number families:

  - a bounded precision native integer type (that is,
    from <CODE>signed char</CODE> to <CODE>long long</CODE>
    and from <CODE>int8_t</CODE> to <CODE>int64_t</CODE>);
  - a bounded precision floating point type (<CODE>float</CODE>,
    <CODE>double</CODE> or <CODE>long double</CODE>);
  - an unbounded integer or rational type, as provided by the C++ interface
    of GMP (<CODE>mpz_class</CODE> or <CODE>mpq_class</CODE>).

  The class template type parameter \p Info allows to control a number
  of features of the class, among which:

  - the ability to support open as well as closed boundaries;
  - the ability to represent empty intervals in addition to nonempty ones;
  - the ability to represent intervals of extended number families
    that contain positive and negative infinities;
*/
template <typename Boundary, typename Info>
class Interval : public Interval_Base, private Info {
private:
  PPL_COMPILE_TIME_CHECK(!Info::store_special
                         || !std::numeric_limits<Boundary>::has_infinity,
                         "store_special is meaningless"
                         " when boundary type may contains infinity");
  Info& w_info() const {
    return const_cast<Interval&>(*this);
  }

public:
  typedef Boundary boundary_type;
  typedef Info info_type;

  typedef Interval_NS::Property Property;

  template <typename T>
  typename Enable_If<Is_Singleton<T>::value || Is_Interval<T>::value, Interval&>::type
  operator=(const T& x) {
    assign(x);
    return *this;
  }

  template <typename T>
  typename Enable_If<Is_Singleton<T>::value || Is_Interval<T>::value, Interval&>::type
  operator+=(const T& x) {
    add_assign(*this, x);
    return *this;
  }
  template <typename T>
  typename Enable_If<Is_Singleton<T>::value || Is_Interval<T>::value, Interval&>::type
  operator-=(const T& x) {
    sub_assign(*this, x);
    return *this;
  }
  template <typename T>
  typename Enable_If<Is_Singleton<T>::value || Is_Interval<T>::value, Interval&>::type
  operator*=(const T& x) {
    mul_assign(*this, x);
    return *this;
  }
  template <typename T>
  typename Enable_If<Is_Singleton<T>::value || Is_Interval<T>::value, Interval&>::type
  operator/=(const T& x) {
    div_assign(*this, x);
    return *this;
  }

  //! Swaps \p *this with \p y.
  void m_swap(Interval& y);

  Info& info() {
    return *this;
  }

  const Info& info() const {
    return *this;
  }

  Boundary& lower() {
    return lower_;
  }

  const Boundary& lower() const {
    return lower_;
  }

  Boundary& upper() {
    return upper_;
  }

  const Boundary& upper() const {
    return upper_;
  }

  I_Constraint<boundary_type> lower_constraint() const {
    PPL_ASSERT(!is_empty());
    if (info().get_boundary_property(LOWER, SPECIAL)) {
      return I_Constraint<boundary_type>();
    }
    return i_constraint(lower_is_open() ? GREATER_THAN : GREATER_OR_EQUAL,
                        lower(), true);
  }
  I_Constraint<boundary_type> upper_constraint() const {
    PPL_ASSERT(!is_empty());
    if (info().get_boundary_property(UPPER, SPECIAL)) {
      return I_Constraint<boundary_type>();
    }
    return i_constraint(upper_is_open() ? LESS_THAN : LESS_OR_EQUAL,
                        upper(), true);
  }

  bool is_empty() const {
    return lt(UPPER, upper(), info(), LOWER, lower(), info());
  }

  bool check_empty(I_Result r) const {
    return (r & I_ANY) == I_EMPTY
      || ((r & I_ANY) != I_NOT_EMPTY && is_empty());
  }

  bool is_singleton() const {
    return eq(LOWER, lower(), info(), UPPER, upper(), info());
  }

  bool lower_is_open() const {
    PPL_ASSERT(OK());
    return is_open(LOWER, lower(), info());
  }

  bool upper_is_open() const {
    PPL_ASSERT(OK());
    return is_open(UPPER, upper(), info());
  }

  bool lower_is_boundary_infinity() const {
    PPL_ASSERT(OK());
    return Boundary_NS::is_boundary_infinity(LOWER, lower(), info());
  }

  bool upper_is_boundary_infinity() const {
    PPL_ASSERT(OK());
    return Boundary_NS::is_boundary_infinity(UPPER, upper(), info());
  }

  bool lower_is_domain_inf() const {
    PPL_ASSERT(OK());
    return Boundary_NS::is_domain_inf(LOWER, lower(), info());
  }

  bool upper_is_domain_sup() const {
    PPL_ASSERT(OK());
    return Boundary_NS::is_domain_sup(UPPER, upper(), info());
  }

  bool is_bounded() const {
    PPL_ASSERT(OK());
    return !lower_is_boundary_infinity() && !upper_is_boundary_infinity();
  }

  bool is_universe() const {
    PPL_ASSERT(OK());
    return lower_is_domain_inf() && upper_is_domain_sup();
  }

  I_Result lower_extend() {
    info().clear_boundary_properties(LOWER);
    set_unbounded(LOWER, lower(), info());
    return I_ANY;
  }

  template <typename C>
  typename Enable_If<Is_Same_Or_Derived<I_Constraint_Base, C>::value, I_Result>::type
  lower_extend(const C& c);

  I_Result upper_extend() {
    info().clear_boundary_properties(UPPER);
    set_unbounded(UPPER, upper(), info());
    return I_ANY;
  }

  template <typename C>
  typename Enable_If<Is_Same_Or_Derived<I_Constraint_Base, C>::value, I_Result>::type
  upper_extend(const C& c);

  I_Result build() {
    return assign(UNIVERSE);
  }

  template <typename C>
  typename Enable_If<Is_Same_Or_Derived<I_Constraint_Base, C>::value, I_Result>::type
  build(const C& c) {
    Relation_Symbol rs;
    switch (c.rel()) {
    case V_LGE:
    case V_GT_MINUS_INFINITY:
    case V_LT_PLUS_INFINITY:
      return assign(UNIVERSE);
    default:
      return assign(EMPTY);
    case V_LT:
    case V_LE:
    case V_GT:
    case V_GE:
    case V_EQ:
    case V_NE:
      assign(UNIVERSE);
      rs = static_cast<Relation_Symbol>(c.rel());
      return refine_existential(rs, c.value());
    }
  }

  template <typename C1, typename C2>
  typename Enable_If<Is_Same_Or_Derived<I_Constraint_Base, C1>::value
                     &&
                     Is_Same_Or_Derived<I_Constraint_Base, C2>::value,
                     I_Result>::type
  build(const C1& c1, const C2& c2) {
    switch (c1.rel()) {
    case V_LGE:
      return build(c2);
    case V_NAN:
      return assign(EMPTY);
    default:
      break;
    }
    switch (c2.rel()) {
    case V_LGE:
      return build(c1);
    case V_NAN:
      return assign(EMPTY);
    default:
      break;
    }
    build(c1);
    const I_Result r = add_constraint(c2);
    return r - (I_CHANGED | I_UNCHANGED);
  }

  template <typename C>
  typename Enable_If<Is_Same_Or_Derived<I_Constraint_Base, C>::value, I_Result>::type
  add_constraint(const C& c) {
    Interval x;
    x.build(c);
    return intersect_assign(x);
  }

  I_Result assign(Degenerate_Element e) {
    I_Result r;
    info().clear();
    switch (e) {
    case EMPTY:
      lower_ = 1;
      upper_ = 0;
      r = I_EMPTY | I_EXACT;
      break;
    case UNIVERSE:
      set_unbounded(LOWER, lower(), info());
      set_unbounded(UPPER, upper(), info());
      r = I_UNIVERSE | I_EXACT;
      break;
    default:
      PPL_UNREACHABLE;
      r = I_EMPTY;
      break;
    }
    PPL_ASSERT(OK());
    return r;
  }

  template <typename From>
  typename Enable_If<Is_Special<From>::value, I_Result>::type
  assign(const From&) {
    info().clear();
    Result rl;
    Result ru;
    switch (From::vclass) {
    case VC_MINUS_INFINITY:
      rl = Boundary_NS::set_minus_infinity(LOWER, lower(), info());
      ru = Boundary_NS::set_minus_infinity(UPPER, upper(), info());
      break;
    case VC_PLUS_INFINITY:
      rl = Boundary_NS::set_plus_infinity(LOWER, lower(), info());
      ru = Boundary_NS::set_plus_infinity(UPPER, upper(), info());
      break;
    default:
      PPL_UNREACHABLE;
      rl = V_NAN;
      ru = V_NAN;
      break;
    }
    PPL_ASSERT(OK());
    return combine(rl, ru);
  }

  I_Result set_infinities() {
    info().clear();
    Result rl = Boundary_NS::set_minus_infinity(LOWER, lower(), info());
    Result ru = Boundary_NS::set_plus_infinity(UPPER, upper(), info());
    PPL_ASSERT(OK());
    return combine(rl, ru);
  }

  static bool is_always_topologically_closed() {
    return !Info::store_open;
  }

  bool is_topologically_closed() const {
    PPL_ASSERT(OK());
    return is_always_topologically_closed()
      || is_empty()
      || ((lower_is_boundary_infinity() || !lower_is_open())
          && (upper_is_boundary_infinity() || !upper_is_open()));
  }

  //! Assigns to \p *this its topological closure.
  void topological_closure_assign() {
    if (!Info::store_open || is_empty()) {
      return;
    }
    if (lower_is_open() && !lower_is_boundary_infinity()) {
      info().set_boundary_property(LOWER, OPEN, false);
    }
    if (upper_is_open() && !upper_is_boundary_infinity()) {
      info().set_boundary_property(UPPER, OPEN, false);
    }
  }

  void remove_inf() {
    PPL_ASSERT(!is_empty());
    if (!Info::store_open) {
      return;
    }
    info().set_boundary_property(LOWER, OPEN, true);
  }

  void remove_sup() {
    PPL_ASSERT(!is_empty());
    if (!Info::store_open) {
      return;
    }
    info().set_boundary_property(UPPER, OPEN, true);
  }

  int infinity_sign() const {
    PPL_ASSERT(OK());
    if (is_reverse_infinity(LOWER, lower(), info())) {
      return 1;
    }
    else if (is_reverse_infinity(UPPER, upper(), info())) {
      return -1;
    }
    else {
      return 0;
    }
  }

  bool contains_integer_point() const {
    PPL_ASSERT(OK());
    if (is_empty()) {
      return false;
    }
    if (!is_bounded()) {
      return true;
    }
    Boundary l;
    if (lower_is_open()) {
      add_assign_r(l, lower(), Boundary(1), ROUND_DOWN);
      floor_assign_r(l, l, ROUND_DOWN);
    }
    else {
      ceil_assign_r(l, lower(), ROUND_DOWN);
    }
    Boundary u;
    if (upper_is_open()) {
      sub_assign_r(u, upper(), Boundary(1), ROUND_UP);
      ceil_assign_r(u, u, ROUND_UP);
    }
    else {
      floor_assign_r(u, upper(), ROUND_UP);
    }
    return u >= l;
  }

  void drop_some_non_integer_points() {
    if (is_empty()) {
      return;
    }
    if (lower_is_open() && !lower_is_boundary_infinity()) {
      add_assign_r(lower(), lower(), Boundary(1), ROUND_DOWN);
      floor_assign_r(lower(), lower(), ROUND_DOWN);
      info().set_boundary_property(LOWER, OPEN, false);
    }
    else {
      ceil_assign_r(lower(), lower(), ROUND_DOWN);
    }
    if (upper_is_open() && !upper_is_boundary_infinity()) {
      sub_assign_r(upper(), upper(), Boundary(1), ROUND_UP);
      ceil_assign_r(upper(), upper(), ROUND_UP);
      info().set_boundary_property(UPPER, OPEN, false);
    }
    else {
      floor_assign_r(upper(), upper(), ROUND_UP);
    }
  }

  template <typename From>
  typename Enable_If<Is_Singleton<From>::value || Is_Interval<From>::value, I_Result>::type
  wrap_assign(Bounded_Integer_Type_Width w,
              Bounded_Integer_Type_Representation r,
              const From& refinement) {
    if (is_empty()) {
      return I_EMPTY;
    }
    if (lower_is_boundary_infinity() || upper_is_boundary_infinity()) {
      return assign(refinement);
    }
    PPL_DIRTY_TEMP(Boundary, u);
    Result result = sub_2exp_assign_r(u, upper(), w, ROUND_UP);
    if (result_overflow(result) == 0 && u > lower()) {
      return assign(refinement);
    }
    info().clear();
    switch (r) {
    case UNSIGNED:
      umod_2exp_assign(LOWER, lower(), info(),
                       LOWER, lower(), info(), w);
      umod_2exp_assign(UPPER, upper(), info(),
                       UPPER, upper(), info(), w);
      break;
    case SIGNED_2_COMPLEMENT:
      smod_2exp_assign(LOWER, lower(), info(),
                       LOWER, lower(), info(), w);
      smod_2exp_assign(UPPER, upper(), info(),
                       UPPER, upper(), info(), w);
      break;
    default:
      PPL_UNREACHABLE;
      break;
    }
    if (le(LOWER, lower(), info(), UPPER, upper(), info())) {
      return intersect_assign(refinement);
    }
    PPL_DIRTY_TEMP(Interval, tmp);
    tmp.info().clear();
    Boundary_NS::assign(LOWER, tmp.lower(), tmp.info(),
                        LOWER, lower(), info());
    set_unbounded(UPPER, tmp.upper(), tmp.info());
    tmp.intersect_assign(refinement);
    lower_extend();
    intersect_assign(refinement);
    return join_assign(tmp);
  }

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  void ascii_dump(std::ostream& s) const;
  bool ascii_load(std::istream& s);

  bool OK() const {
    if (!Info::may_be_empty && is_empty()) {
#ifndef NDEBUG
      std::cerr << "The interval is unexpectedly empty.\n";
#endif
      return false;
    }

    if (is_open(LOWER, lower(), info())) {
      if (is_plus_infinity(LOWER, lower(), info())) {
#ifndef NDEBUG
        std::cerr << "The lower boundary is +inf open.\n";
#endif
      }
    }
    else if (!Info::may_contain_infinity
             && (is_minus_infinity(LOWER, lower(), info())
                 || is_plus_infinity(LOWER, lower(), info()))) {
#ifndef NDEBUG
      std::cerr << "The lower boundary is unexpectedly infinity.\n";
#endif
      return false;
    }
    if (!info().get_boundary_property(LOWER, SPECIAL)) {
      if (is_not_a_number(lower())) {
#ifndef NDEBUG
        std::cerr << "The lower boundary is not a number.\n";
#endif
        return false;
      }
    }

    if (is_open(UPPER, upper(), info())) {
      if (is_minus_infinity(UPPER, upper(), info())) {
#ifndef NDEBUG
        std::cerr << "The upper boundary is -inf open.\n";
#endif
      }
    }
    else if (!Info::may_contain_infinity
             && (is_minus_infinity(UPPER, upper(), info())
                 || is_plus_infinity(UPPER, upper(), info()))) {
#ifndef NDEBUG
      std::cerr << "The upper boundary is unexpectedly infinity."
                << std::endl;
#endif
      return false;
    }
    if (!info().get_boundary_property(UPPER, SPECIAL)) {
      if (is_not_a_number(upper())) {
#ifndef NDEBUG
        std::cerr << "The upper boundary is not a number.\n";
#endif
        return false;
      }
    }

    // Everything OK.
    return true;
  }

  Interval() {
  }

  template <typename T>
  explicit Interval(const T& x) {
    assign(x);
  }

  /*! \brief
    Builds the smallest interval containing the number whose textual
    representation is contained in \p s.
  */
  explicit Interval(const char* s);

  template <typename T>
  typename Enable_If<Is_Singleton<T>::value
                     || Is_Interval<T>::value, bool>::type
  contains(const T& y) const;

  template <typename T>
  typename Enable_If<Is_Singleton<T>::value
                     || Is_Interval<T>::value, bool>::type
  strictly_contains(const T& y) const;

  template <typename T>
  typename Enable_If<Is_Singleton<T>::value
                     || Is_Interval<T>::value, bool>::type
  is_disjoint_from(const T& y) const;


  template <typename From>
  typename Enable_If<Is_Singleton<From>::value
                     || Is_Interval<From>::value, I_Result>::type
  assign(const From& x);

  template <typename Type>
  typename Enable_If<Is_Singleton<Type>::value
                     || Is_Interval<Type>::value, bool>::type
  can_be_exactly_joined_to(const Type& x) const;

  template <typename From>
  typename Enable_If<Is_Singleton<From>::value
                     || Is_Interval<From>::value, I_Result>::type
  join_assign(const From& x);

  template <typename From1, typename From2>
  typename Enable_If<((Is_Singleton<From1>::value
                       || Is_Interval<From1>::value)
                      && (Is_Singleton<From2>::value
                          || Is_Interval<From2>::value)), I_Result>::type
  join_assign(const From1& x, const From2& y);

  template <typename From>
  typename Enable_If<Is_Singleton<From>::value
                     || Is_Interval<From>::value, I_Result>::type
  intersect_assign(const From& x);

  template <typename From1, typename From2>
  typename Enable_If<((Is_Singleton<From1>::value
                       || Is_Interval<From1>::value)
                      && (Is_Singleton<From2>::value
                          || Is_Interval<From2>::value)), I_Result>::type
  intersect_assign(const From1& x, const From2& y);

  /*! \brief
    Assigns to \p *this the smallest interval containing the set-theoretic
    difference of \p *this and \p x.
  */
  template <typename From>
  typename Enable_If<Is_Singleton<From>::value
                     || Is_Interval<From>::value, I_Result>::type
  difference_assign(const From& x);

  /*! \brief
    Assigns to \p *this the smallest interval containing the set-theoretic
    difference of \p x and \p y.
  */
  template <typename From1, typename From2>
  typename Enable_If<((Is_Singleton<From1>::value
                       || Is_Interval<From1>::value)
                      && (Is_Singleton<From2>::value
                          || Is_Interval<From2>::value)), I_Result>::type
  difference_assign(const From1& x, const From2& y);

  /*! \brief
    Assigns to \p *this the largest interval contained in the set-theoretic
    difference of \p *this and \p x.
  */
  template <typename From>
  typename Enable_If<Is_Singleton<From>::value
                     || Is_Interval<From>::value, I_Result>::type
  lower_approximation_difference_assign(const From& x);

  /*! \brief
    Assigns to \p *this a \ref Meet_Preserving_Simplification
    "meet-preserving simplification" of \p *this with respect to \p y.

    \return
    \c false if and only if the meet of \p *this and \p y is empty.
  */
  template <typename From>
  typename Enable_If<Is_Interval<From>::value, bool>::type
  simplify_using_context_assign(const From& y);

  /*! \brief
    Assigns to \p *this an interval having empty intersection with \p y.
    The assigned interval should be as large as possible.
  */
  template <typename From>
  typename Enable_If<Is_Interval<From>::value, void>::type
  empty_intersection_assign(const From& y);

  /*! \brief
    Refines \p to according to the existential relation \p rel with \p x.

    The \p to interval is restricted to become, upon successful exit,
    the smallest interval of its type that contains the set
    \f[
      \{\,
        a \in \mathtt{to}
      \mid
        \exists b \in \mathtt{x} \st a \mathrel{\mathtt{rel}} b
      \,\}.
    \f]
    \return
    ???
  */
  template <typename From>
  typename Enable_If<Is_Singleton<From>::value
                     || Is_Interval<From>::value, I_Result>::type
  refine_existential(Relation_Symbol rel, const From& x);

  /*! \brief
    Refines \p to so that it satisfies the universal relation \p rel with \p x.

    The \p to interval is restricted to become, upon successful exit,
    the smallest interval of its type that contains the set
    \f[
      \{\,
        a \in \mathtt{to}
      \mid
        \forall b \in \mathtt{x} \itc a \mathrel{\mathtt{rel}} b
      \,\}.
    \f]
    \return
    ???
  */
  template <typename From>
  typename Enable_If<Is_Singleton<From>::value
                     || Is_Interval<From>::value, I_Result>::type
  refine_universal(Relation_Symbol rel, const From& x);

  template <typename From>
  typename Enable_If<Is_Singleton<From>::value
                     || Is_Interval<From>::value, I_Result>::type
  neg_assign(const From& x);

  template <typename From1, typename From2>
  typename Enable_If<((Is_Singleton<From1>::value || Is_Interval<From1>::value)
                      && (Is_Singleton<From2>::value || Is_Interval<From2>::value)), I_Result>::type
  add_assign(const From1& x, const From2& y);

  template <typename From1, typename From2>
  typename Enable_If<((Is_Singleton<From1>::value || Is_Interval<From1>::value)
                      && (Is_Singleton<From2>::value || Is_Interval<From2>::value)), I_Result>::type
  sub_assign(const From1& x, const From2& y);

  template <typename From1, typename From2>
  typename Enable_If<((Is_Singleton<From1>::value || Is_Interval<From1>::value)
                      && (Is_Singleton<From2>::value || Is_Interval<From2>::value)), I_Result>::type
  mul_assign(const From1& x, const From2& y);

  template <typename From1, typename From2>
  typename Enable_If<((Is_Singleton<From1>::value || Is_Interval<From1>::value)
                      && (Is_Singleton<From2>::value || Is_Interval<From2>::value)), I_Result>::type
  div_assign(const From1& x, const From2& y);

  template <typename From, typename Iterator>
  typename Enable_If<Is_Interval<From>::value, void>::type
  CC76_widening_assign(const From& y, Iterator first, Iterator last);

private:
  Boundary lower_;
  Boundary upper_;
};

//! Swaps \p x with \p y.
/*! \relates Interval */
template <typename Boundary, typename Info>
void swap(Interval<Boundary, Info>& x, Interval<Boundary, Info>& y);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Interval_inlines.hh line 1. */
/* Inline functions for the Interval class and its constituents.
*/


namespace Parma_Polyhedra_Library {

template <typename Boundary, typename Info>
inline memory_size_type
Interval<Boundary, Info>::external_memory_in_bytes() const {
  return Parma_Polyhedra_Library::external_memory_in_bytes(lower())
    + Parma_Polyhedra_Library::external_memory_in_bytes(upper());
}

template <typename Boundary, typename Info>
inline memory_size_type
Interval<Boundary, Info>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename Boundary, typename Info>
inline void
Interval<Boundary, Info>::m_swap(Interval<Boundary, Info>& y) {
  using std::swap;
  swap(lower(), y.lower());
  swap(upper(), y.upper());
  swap(info(), y.info());
}

template <typename Boundary, typename Info>
inline bool
f_is_empty(const Interval<Boundary, Info>& x) {
  return x.is_empty();
}
template <typename Boundary, typename Info>
inline bool
f_is_singleton(const Interval<Boundary, Info>& x) {
  return x.is_singleton();
}
template <typename Boundary, typename Info>
inline int
infinity_sign(const Interval<Boundary, Info>& x) {
  return x.infinity_sign();
}

namespace Interval_NS {

template <typename Boundary, typename Info>
inline const Boundary&
f_lower(const Interval<Boundary, Info>& x) {
  return x.lower();
}
template <typename Boundary, typename Info>
inline const Boundary&
f_upper(const Interval<Boundary, Info>& x) {
  return x.upper();
}
template <typename Boundary, typename Info>
inline const Info&
f_info(const Interval<Boundary, Info>& x) {
  return x.info();
}

struct Scalar_As_Interval_Policy {
  const_bool_nodef(may_be_empty, true);
  const_bool_nodef(may_contain_infinity, true);
  const_bool_nodef(check_inexact, false);
};

typedef Interval_Info_Null<Scalar_As_Interval_Policy>
Scalar_As_Interval_Info;

const Scalar_As_Interval_Info SCALAR_INFO;

typedef Interval_Info_Null_Open<Scalar_As_Interval_Policy>
Scalar_As_Interval_Info_Open;

template <typename T>
inline typename Enable_If<Is_Singleton<T>::value, const T&>::type
f_lower(const T& x) {
  return x;
}
template <typename T>
inline typename Enable_If<Is_Singleton<T>::value, const T&>::type
f_upper(const T& x) {
  return x;
}
template <typename T>
inline typename Enable_If<Is_Singleton<T>::value,
                          const Scalar_As_Interval_Info&>::type
f_info(const T&) {
  return SCALAR_INFO;
}
template <typename T>
inline typename Enable_If<Is_Singleton<T>::value,
                          Scalar_As_Interval_Info_Open>::type
f_info(const T&, bool open) {
  return Scalar_As_Interval_Info_Open(open);
}

template <typename T>
inline typename Enable_If<Is_Singleton<T>::value, bool>::type
f_is_empty(const T& x) {
  return is_not_a_number(x);
}

template <typename T>
inline typename Enable_If<Is_Singleton<T>::value, bool>::type
f_is_singleton(const T& x) {
  return !f_is_empty(x);
}

} // namespace Interval_NS

template <typename T>
inline typename Enable_If<Is_Singleton<T>::value
                          || Is_Interval<T>::value, bool>::type
is_singleton_integer(const T& x) {
  return is_singleton(x) && is_integer(f_lower(x));
}

template <typename T>
inline typename Enable_If<Is_Singleton<T>::value
                          || Is_Interval<T>::value, bool>::type
check_empty_arg(const T& x) {
  if (f_info(x).may_be_empty) {
    return f_is_empty(x);
  }
  else {
    PPL_ASSERT(!f_is_empty(x));
    return false;
  }
}

template <typename T1, typename T2>
inline typename Enable_If<((Is_Singleton<T1>::value
                            || Is_Interval<T1>::value)
                           && (Is_Singleton<T2>::value
                               || Is_Interval<T2>::value)
                           && (Is_Interval<T1>::value
                               || Is_Interval<T2>::value)),
                          bool>::type
operator==(const T1& x, const T2& y) {
  PPL_ASSERT(f_OK(x));
  PPL_ASSERT(f_OK(y));
  if (check_empty_arg(x)) {
    return check_empty_arg(y);
  }
  else if (check_empty_arg(y)) {
    return false;
  }
  return eq(LOWER, f_lower(x), f_info(x), LOWER, f_lower(y), f_info(y))
    && eq(UPPER, f_upper(x), f_info(x), UPPER, f_upper(y), f_info(y));
}

template <typename T1, typename T2>
inline typename Enable_If<((Is_Singleton<T1>::value
                            || Is_Interval<T1>::value)
                           && (Is_Singleton<T2>::value
                               || Is_Interval<T2>::value)
                           && (Is_Interval<T1>::value
                               || Is_Interval<T2>::value)),
                          bool>::type
operator!=(const T1& x, const T2& y) {
  return !(x == y);
}

template <typename Boundary, typename Info>
template <typename T>
inline typename Enable_If<Is_Singleton<T>::value
                          || Is_Interval<T>::value, bool>::type
Interval<Boundary, Info>::contains(const T& y) const {
  PPL_ASSERT(OK());
  PPL_ASSERT(f_OK(y));
  if (check_empty_arg(y)) {
    return true;
  }
  if (check_empty_arg(*this)) {
    return false;
  }
  return le(LOWER, lower(), info(), LOWER, f_lower(y), f_info(y))
    && ge(UPPER, upper(), info(), UPPER, f_upper(y), f_info(y));
}

template <typename Boundary, typename Info>
template <typename T>
inline typename Enable_If<Is_Singleton<T>::value
                          || Is_Interval<T>::value, bool>::type
Interval<Boundary, Info>::strictly_contains(const T& y) const {
  PPL_ASSERT(OK());
  PPL_ASSERT(f_OK(y));
  if (check_empty_arg(y)) {
    return !check_empty_arg(*this);
  }
  if (check_empty_arg(*this)) {
    return false;
  }
  return (lt(LOWER, lower(), info(), LOWER, f_lower(y), f_info(y))
          && ge(UPPER, upper(), info(), UPPER, f_upper(y), f_info(y)))
    || (le(LOWER, lower(), info(), LOWER, f_lower(y), f_info(y))
        && gt(UPPER, upper(), info(), UPPER, f_upper(y), f_info(y)));
}

template <typename Boundary, typename Info>
template <typename T>
inline typename Enable_If<Is_Singleton<T>::value
                          || Is_Interval<T>::value, bool>::type
Interval<Boundary, Info>::is_disjoint_from(const T& y) const {
  PPL_ASSERT(OK());
  PPL_ASSERT(f_OK(y));
  if (check_empty_arg(*this) || check_empty_arg(y)) {
    return true;
  }
  return gt(LOWER, lower(), info(), UPPER, f_upper(y), f_info(y))
    || lt(UPPER, upper(), info(), LOWER, f_lower(y), f_info(y));
}

template <typename To_Boundary, typename To_Info>
template <typename From>
inline typename Enable_If<Is_Singleton<From>::value
                          || Is_Interval<From>::value, I_Result>::type
Interval<To_Boundary, To_Info>::assign(const From& x) {
  PPL_ASSERT(f_OK(x));
  if (check_empty_arg(x)) {
    return assign(EMPTY);
  }
  PPL_DIRTY_TEMP(To_Info, to_info);
  to_info.clear();
  const Result rl = Boundary_NS::assign(LOWER, lower(), to_info,
                                        LOWER, f_lower(x), f_info(x));
  const Result ru = Boundary_NS::assign(UPPER, upper(), to_info,
                                        UPPER, f_upper(x), f_info(x));
  assign_or_swap(info(), to_info);
  PPL_ASSERT(OK());
  return combine(rl, ru);
}

template <typename To_Boundary, typename To_Info>
template <typename From>
inline typename Enable_If<Is_Singleton<From>::value
                          || Is_Interval<From>::value, I_Result>::type
Interval<To_Boundary, To_Info>::join_assign(const From& x) {
  PPL_ASSERT(f_OK(x));
  if (check_empty_arg(*this)) {
    return assign(x);
  }
  if (check_empty_arg(x)) {
    return combine(V_EQ, V_EQ);
  }
  Result rl;
  Result ru;
  rl = min_assign(LOWER, lower(), info(), LOWER, f_lower(x), f_info(x));
  ru = max_assign(UPPER, upper(), info(), UPPER, f_upper(x), f_info(x));
  PPL_ASSERT(OK());
  return combine(rl, ru);
}

template <typename To_Boundary, typename To_Info>
template <typename From1, typename From2>
inline typename Enable_If<((Is_Singleton<From1>::value
                            || Is_Interval<From1>::value)
                           && (Is_Singleton<From2>::value
                               || Is_Interval<From2>::value)), I_Result>::type
Interval<To_Boundary, To_Info>::join_assign(const From1& x, const From2& y) {
  PPL_ASSERT(f_OK(x));
  PPL_ASSERT(f_OK(y));
  if (check_empty_arg(x)) {
    return assign(y);
  }
  if (check_empty_arg(y)) {
    return assign(x);
  }
  PPL_DIRTY_TEMP(To_Info, to_info);
  to_info.clear();
  Result rl;
  Result ru;
  rl = min_assign(LOWER, lower(), to_info,
                  LOWER, f_lower(x), f_info(x),
                  LOWER, f_lower(y), f_info(y));
  ru = max_assign(UPPER, upper(), to_info,
                  UPPER, f_upper(x), f_info(x),
                  UPPER, f_upper(y), f_info(y));
  assign_or_swap(info(), to_info);
  PPL_ASSERT(OK());
  return combine(rl, ru);
}

template <typename Boundary, typename Info>
template <typename Type>
inline typename Enable_If<Is_Singleton<Type>::value
                          || Is_Interval<Type>::value, bool>::type
Interval<Boundary, Info>::can_be_exactly_joined_to(const Type& x) const {
  PPL_DIRTY_TEMP(Boundary, b);
  if (gt(LOWER, lower(), info(), UPPER, f_upper(x), f_info(x))) {
    b = lower();
    return eq(LOWER, b, info(), UPPER, f_upper(x), f_info(x));
  }
  else if (lt(UPPER, upper(), info(), LOWER, f_lower(x), f_info(x))) {
    b = upper();
    return eq(UPPER, b, info(), LOWER, f_lower(x), f_info(x));
  }
  return true;
}


template <typename To_Boundary, typename To_Info>
template <typename From>
inline typename Enable_If<Is_Singleton<From>::value
                          || Is_Interval<From>::value, I_Result>::type
Interval<To_Boundary, To_Info>::intersect_assign(const From& x) {
  PPL_ASSERT(f_OK(x));
  max_assign(LOWER, lower(), info(), LOWER, f_lower(x), f_info(x));
  min_assign(UPPER, upper(), info(), UPPER, f_upper(x), f_info(x));
  PPL_ASSERT(OK());
  return I_ANY;
}

template <typename To_Boundary, typename To_Info>
template <typename From1, typename From2>
inline typename Enable_If<((Is_Singleton<From1>::value
                            || Is_Interval<From1>::value)
                           && (Is_Singleton<From2>::value
                               || Is_Interval<From2>::value)), I_Result>::type
Interval<To_Boundary, To_Info>::intersect_assign(const From1& x,
                                                 const From2& y) {
  PPL_ASSERT(f_OK(x));
  PPL_ASSERT(f_OK(y));
  PPL_DIRTY_TEMP(To_Info, to_info);
  to_info.clear();
  max_assign(LOWER, lower(), to_info,
             LOWER, f_lower(x), f_info(x),
             LOWER, f_lower(y), f_info(y));
  min_assign(UPPER, upper(), to_info,
             UPPER, f_upper(x), f_info(x),
             UPPER, f_upper(y), f_info(y));
  assign_or_swap(info(), to_info);
  PPL_ASSERT(OK());
  return I_NOT_EMPTY;
}

template <typename To_Boundary, typename To_Info>
template <typename From>
inline typename Enable_If<Is_Singleton<From>::value
                          || Is_Interval<From>::value, I_Result>::type
Interval<To_Boundary, To_Info>::difference_assign(const From& x) {
  PPL_ASSERT(f_OK(x));
  if (lt(UPPER, upper(), info(), LOWER, f_lower(x), f_info(x))
      || gt(LOWER, lower(), info(), UPPER, f_upper(x), f_info(x))) {
    return combine(V_EQ, V_EQ);
  }
  bool nl = ge(LOWER, lower(), info(), LOWER, f_lower(x), f_info(x));
  bool nu = le(UPPER, upper(), info(), UPPER, f_upper(x), f_info(x));
  Result rl = V_EQ;
  Result ru = V_EQ;
  if (nl) {
    if (nu) {
      return assign(EMPTY);
    }
    else {
      info().clear_boundary_properties(LOWER);
      rl = complement(LOWER, lower(), info(), UPPER, f_upper(x), f_info(x));
    }
  }
  else if (nu) {
    info().clear_boundary_properties(UPPER);
    ru = complement(UPPER, upper(), info(), LOWER, f_lower(x), f_info(x));
  }
  PPL_ASSERT(OK());
  return combine(rl, ru);
}

template <typename To_Boundary, typename To_Info>
template <typename From1, typename From2>
inline typename Enable_If<((Is_Singleton<From1>::value
                            || Is_Interval<From1>::value)
                           && (Is_Singleton<From2>::value
                               || Is_Interval<From2>::value)), I_Result>::type
Interval<To_Boundary, To_Info>::difference_assign(const From1& x,
                                                  const From2& y) {
  PPL_ASSERT(f_OK(x));
  PPL_ASSERT(f_OK(y));
  PPL_DIRTY_TEMP(To_Info, to_info);
  to_info.clear();
  if (lt(UPPER, f_upper(x), f_info(x), LOWER, f_lower(y), f_info(y))
      || gt(LOWER, f_lower(x), f_info(x), UPPER, f_upper(y), f_info(y))) {
    return assign(x);
  }
  bool nl = ge(LOWER, f_lower(x), f_info(x), LOWER, f_lower(y), f_info(y));
  bool nu = le(UPPER, f_upper(x), f_info(x), UPPER, f_upper(y), f_info(y));
  Result rl = V_EQ;
  Result ru = V_EQ;
  if (nl) {
    if (nu) {
      return assign(EMPTY);
    }
    else {
      rl = complement(LOWER, lower(), info(), UPPER, f_upper(y), f_info(y));
      ru = Boundary_NS::assign(UPPER, upper(), info(), UPPER, f_upper(x), f_info(x));
    }
  }
  else if (nu) {
    ru = complement(UPPER, upper(), info(), LOWER, f_lower(y), f_info(y));
    rl = Boundary_NS::assign(LOWER, lower(), info(),
                             LOWER, f_lower(x), f_info(x));
  }
  assign_or_swap(info(), to_info);
  PPL_ASSERT(OK());
  return combine(rl, ru);
}

template <typename To_Boundary, typename To_Info>
template <typename From>
inline typename Enable_If<Is_Singleton<From>::value
                          || Is_Interval<From>::value, I_Result>::type
Interval<To_Boundary, To_Info>
::refine_existential(Relation_Symbol rel, const From& x) {
  PPL_ASSERT(OK());
  PPL_ASSERT(f_OK(x));
  if (check_empty_arg(x)) {
    return assign(EMPTY);
  }
  switch (rel) {
  case LESS_THAN:
    {
      if (lt(UPPER, upper(), info(), UPPER, f_upper(x), f_info(x))) {
        return combine(V_EQ, V_EQ);
      }
      info().clear_boundary_properties(UPPER);
      Boundary_NS::assign(UPPER, upper(), info(),
                          UPPER, f_upper(x), f_info(x), true);
      return I_ANY;
    }
  case LESS_OR_EQUAL:
    {
      if (le(UPPER, upper(), info(), UPPER, f_upper(x), f_info(x))) {
        return combine(V_EQ, V_EQ);
      }
      info().clear_boundary_properties(UPPER);
      Boundary_NS::assign(UPPER, upper(), info(),
                          UPPER, f_upper(x), f_info(x));
      return I_ANY;
    }
  case GREATER_THAN:
    {
      if (gt(LOWER, lower(), info(), LOWER, f_lower(x), f_info(x))) {
        return combine(V_EQ, V_EQ);
      }
      info().clear_boundary_properties(LOWER);
      Boundary_NS::assign(LOWER, lower(), info(),
                          LOWER, f_lower(x), f_info(x), true);
      return I_ANY;
    }
  case GREATER_OR_EQUAL:
    {
      if (ge(LOWER, lower(), info(), LOWER, f_lower(x), f_info(x))) {
        return combine(V_EQ, V_EQ);
      }
      info().clear_boundary_properties(LOWER);
      Boundary_NS::assign(LOWER, lower(), info(),
                          LOWER, f_lower(x), f_info(x));
      return I_ANY;
    }
  case EQUAL:
    return intersect_assign(x);
  case NOT_EQUAL:
    {
      if (!f_is_singleton(x)) {
        return combine(V_EQ, V_EQ);
      }
      if (check_empty_arg(*this)) {
        return I_EMPTY;
      }
      if (eq(LOWER, lower(), info(), LOWER, f_lower(x), f_info(x))) {
        remove_inf();
      }
      if (eq(UPPER, upper(), info(), UPPER, f_upper(x), f_info(x))) {
        remove_sup();
      }
      return I_ANY;
    }
  default:
    PPL_UNREACHABLE;
    return I_EMPTY;
  }
}

template <typename To_Boundary, typename To_Info>
template <typename From>
inline typename Enable_If<Is_Singleton<From>::value
                          || Is_Interval<From>::value, I_Result>::type
Interval<To_Boundary, To_Info>::refine_universal(Relation_Symbol rel,
                                                 const From& x) {
  PPL_ASSERT(OK());
  PPL_ASSERT(f_OK(x));
  if (check_empty_arg(x)) {
    return combine(V_EQ, V_EQ);
  }
  switch (rel) {
  case LESS_THAN:
    {
      if (lt(UPPER, upper(), info(), LOWER, f_lower(x), f_info(x))) {
        return combine(V_EQ, V_EQ);
      }
      info().clear_boundary_properties(UPPER);
      Result ru = Boundary_NS::assign(UPPER, upper(), info(),
                                      LOWER, f_lower(x), SCALAR_INFO,
                                      !is_open(LOWER, f_lower(x), f_info(x)));
      PPL_USED(ru);
      return I_ANY;
    }
  case LESS_OR_EQUAL:
    {
      if (le(UPPER, upper(), info(), LOWER, f_lower(x), f_info(x))) {
        return combine(V_EQ, V_EQ);
      }
      info().clear_boundary_properties(UPPER);
      Result ru = Boundary_NS::assign(UPPER, upper(), info(),
                                      LOWER, f_lower(x), SCALAR_INFO);
      PPL_USED(ru);
      return I_ANY;
    }
  case GREATER_THAN:
    {
      if (gt(LOWER, lower(), info(), UPPER, f_upper(x), f_info(x))) {
        return combine(V_EQ, V_EQ);
      }
      info().clear_boundary_properties(LOWER);
      Result rl = Boundary_NS::assign(LOWER, lower(), info(),
                                      UPPER, f_upper(x), SCALAR_INFO,
                                      !is_open(UPPER, f_upper(x), f_info(x)));
      PPL_USED(rl);
      return I_ANY;
    }
  case GREATER_OR_EQUAL:
    {
      if (ge(LOWER, lower(), info(), UPPER, f_upper(x), f_info(x))) {
        return combine(V_EQ, V_EQ);
      }
      info().clear_boundary_properties(LOWER);
      Result rl = Boundary_NS::assign(LOWER, lower(), info(),
                                      UPPER, f_upper(x), SCALAR_INFO);
      PPL_USED(rl);
      return I_ANY;
    }
  case EQUAL:
    if (!f_is_singleton(x)) {
      return assign(EMPTY);
    }
    return intersect_assign(x);
  case NOT_EQUAL:
    {
      if (check_empty_arg(*this)) {
        return I_EMPTY;
      }
      if (eq(LOWER, lower(), info(), LOWER, f_lower(x), f_info(x))) {
        remove_inf();
      }
      if (eq(UPPER, upper(), info(), UPPER, f_upper(x), f_info(x))) {
        remove_sup();
      }
      return I_ANY;
    }
  default:
    PPL_UNREACHABLE;
    return I_EMPTY;
  }
}

template <typename To_Boundary, typename To_Info>
template <typename From>
inline typename Enable_If<Is_Singleton<From>::value
                          || Is_Interval<From>::value, I_Result>::type
Interval<To_Boundary, To_Info>::neg_assign(const From& x) {
  PPL_ASSERT(f_OK(x));
  if (check_empty_arg(x)) {
    return assign(EMPTY);
  }
  PPL_DIRTY_TEMP(To_Info, to_info);
  to_info.clear();
  Result rl;
  Result ru;
  PPL_DIRTY_TEMP(To_Boundary, to_lower);
  rl = Boundary_NS::neg_assign(LOWER, to_lower, to_info, UPPER, f_upper(x), f_info(x));
  ru = Boundary_NS::neg_assign(UPPER, upper(), to_info, LOWER, f_lower(x), f_info(x));
  assign_or_swap(lower(), to_lower);
  assign_or_swap(info(), to_info);
  PPL_ASSERT(OK());
  return combine(rl, ru);
}

template <typename To_Boundary, typename To_Info>
template <typename From1, typename From2>
inline typename Enable_If<((Is_Singleton<From1>::value
                            || Is_Interval<From1>::value)
                           && (Is_Singleton<From2>::value
                               || Is_Interval<From2>::value)), I_Result>::type
Interval<To_Boundary, To_Info>::add_assign(const From1& x, const From2& y) {
  PPL_ASSERT(f_OK(x));
  PPL_ASSERT(f_OK(y));
  if (check_empty_arg(x) || check_empty_arg(y)) {
    return assign(EMPTY);
  }
  int inf_sign = Parma_Polyhedra_Library::infinity_sign(x);
  if (inf_sign != 0) {
    if (Parma_Polyhedra_Library::infinity_sign(y) == -inf_sign) {
      return assign(EMPTY);
    }
  }
  else {
    inf_sign = Parma_Polyhedra_Library::infinity_sign(y);
  }
  if (inf_sign < 0) {
    return assign(MINUS_INFINITY);
  }
  else if (inf_sign > 0) {
    return assign(PLUS_INFINITY);
  }
  PPL_DIRTY_TEMP(To_Info, to_info);
  to_info.clear();
  Result rl = Boundary_NS::add_assign(LOWER, lower(), to_info,
                                      LOWER, f_lower(x), f_info(x),
                                      LOWER, f_lower(y), f_info(y));
  Result ru = Boundary_NS::add_assign(UPPER, upper(), to_info,
                                      UPPER, f_upper(x), f_info(x),
                                      UPPER, f_upper(y), f_info(y));
  assign_or_swap(info(), to_info);
  PPL_ASSERT(OK());
  return combine(rl, ru);
}

template <typename To_Boundary, typename To_Info>
template <typename From1, typename From2>
inline typename Enable_If<((Is_Singleton<From1>::value
                            || Is_Interval<From1>::value)
                           && (Is_Singleton<From2>::value
                               || Is_Interval<From2>::value)), I_Result>::type
Interval<To_Boundary, To_Info>::sub_assign(const From1& x, const From2& y) {
  PPL_ASSERT(f_OK(x));
  PPL_ASSERT(f_OK(y));
  if (check_empty_arg(x) || check_empty_arg(y)) {
    return assign(EMPTY);
  }
  int inf_sign = Parma_Polyhedra_Library::infinity_sign(x);
  if (inf_sign != 0) {
    if (Parma_Polyhedra_Library::infinity_sign(y) == inf_sign) {
      return assign(EMPTY);
    }
  }
  else {
    inf_sign = -Parma_Polyhedra_Library::infinity_sign(y);
  }
  if (inf_sign < 0) {
    return assign(MINUS_INFINITY);
  }
  else if (inf_sign > 0) {
    return assign(PLUS_INFINITY);
  }
  PPL_DIRTY_TEMP(To_Info, to_info);
  to_info.clear();
  Result rl;
  Result ru;
  PPL_DIRTY_TEMP(To_Boundary, to_lower);
  rl = Boundary_NS::sub_assign(LOWER, to_lower, to_info,
                               LOWER, f_lower(x), f_info(x),
                               UPPER, f_upper(y), f_info(y));
  ru = Boundary_NS::sub_assign(UPPER, upper(), to_info,
                               UPPER, f_upper(x), f_info(x),
                               LOWER, f_lower(y), f_info(y));
  assign_or_swap(lower(), to_lower);
  assign_or_swap(info(), to_info);
  PPL_ASSERT(OK());
  return combine(rl, ru);
}

/**
+---------+-----------+-----------+-----------------+
|    *    |  yl > 0   |  yu < 0   |  yl < 0, yu > 0 |
+---------+-----------+-----------+-----------------+
| xl > 0  |xl*yl,xu*yu|xu*yl,xl*yu|   xu*yl,xu*yu   |
+---------+-----------+-----------+-----------------+
| xu < 0  |xl*yu,xu*yl|xu*yu,xl*yl|   xl*yu,xl*yl   |
+---------+-----------+-----------+-----------------+
|xl<0 xu>0|xl*yu,xu*yu|xu*yl,xl*yl|min(xl*yu,xu*yl),|
|         |           |           |max(xl*yl,xu*yu) |
+---------+-----------+-----------+-----------------+
**/
template <typename To_Boundary, typename To_Info>
template <typename From1, typename From2>
inline typename Enable_If<((Is_Singleton<From1>::value
                            || Is_Interval<From1>::value)
                           && (Is_Singleton<From2>::value
                               || Is_Interval<From2>::value)), I_Result>::type
Interval<To_Boundary, To_Info>::mul_assign(const From1& x, const From2& y) {
  PPL_ASSERT(f_OK(x));
  PPL_ASSERT(f_OK(y));
  if (check_empty_arg(x) || check_empty_arg(y)) {
    return assign(EMPTY);
  }
  int xls = sgn_b(LOWER, f_lower(x), f_info(x));
  int xus = (xls > 0) ? 1 : sgn_b(UPPER, f_upper(x), f_info(x));
  int yls = sgn_b(LOWER, f_lower(y), f_info(y));
  int yus = (yls > 0) ? 1 : sgn_b(UPPER, f_upper(y), f_info(y));
  int inf_sign = Parma_Polyhedra_Library::infinity_sign(x);
  int ls;
  int us;
  if (inf_sign != 0) {
    ls = yls;
    us = yus;
    goto inf;
  }
  else {
    inf_sign = Parma_Polyhedra_Library::infinity_sign(y);
    if (inf_sign != 0) {
      ls = xls;
      us = xus;
    inf:
      if (ls == 0 && us == 0) {
        return assign(EMPTY);
      }
      if (ls == -us) {
        return set_infinities();
      }
      if (ls < 0 || us < 0) {
        inf_sign = -inf_sign;
      }
      if (inf_sign < 0) {
        return assign(MINUS_INFINITY);
      }
      else {
        return assign(PLUS_INFINITY);
      }
    }
  }

  PPL_DIRTY_TEMP(To_Info, to_info);
  to_info.clear();
  Result rl;
  Result ru;
  PPL_DIRTY_TEMP(To_Boundary, to_lower);

  if (xls >= 0) {
    if (yls >= 0) {
      // 0 <= xl <= xu, 0 <= yl <= yu
      rl = mul_assign_z(LOWER, to_lower, to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        LOWER, f_lower(y), f_info(y), yls);
      ru = mul_assign_z(UPPER, upper(), to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        UPPER, f_upper(y), f_info(y), yus);
    }
    else if (yus <= 0) {
      // 0 <= xl <= xu, yl <= yu <= 0
      rl = mul_assign_z(LOWER, to_lower, to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        LOWER, f_lower(y), f_info(y), yls);
      ru = mul_assign_z(UPPER, upper(), to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        UPPER, f_upper(y), f_info(y), yus);
    }
    else {
      // 0 <= xl <= xu, yl < 0 < yu
      rl = mul_assign_z(LOWER, to_lower, to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        LOWER, f_lower(y), f_info(y), yls);
      ru = mul_assign_z(UPPER, upper(), to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        UPPER, f_upper(y), f_info(y), yus);
    }
  }
  else if (xus <= 0) {
    if (yls >= 0) {
      // xl <= xu <= 0, 0 <= yl <= yu
      rl = mul_assign_z(LOWER, to_lower, to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        UPPER, f_upper(y), f_info(y), yus);
      ru = mul_assign_z(UPPER, upper(), to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        LOWER, f_lower(y), f_info(y), yls);
    }
    else if (yus <= 0) {
      // xl <= xu <= 0, yl <= yu <= 0
      rl = mul_assign_z(LOWER, to_lower, to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        UPPER, f_upper(y), f_info(y), yus);
      ru = mul_assign_z(UPPER, upper(), to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        LOWER, f_lower(y), f_info(y), yls);
    }
    else {
      // xl <= xu <= 0, yl < 0 < yu
      rl = mul_assign_z(LOWER, to_lower, to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        UPPER, f_upper(y), f_info(y), yus);
      ru = mul_assign_z(UPPER, upper(), to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        LOWER, f_lower(y), f_info(y), yls);
    }
  }
  else if (yls >= 0) {
    // xl < 0 < xu, 0 <= yl <= yu
    rl = mul_assign_z(LOWER, to_lower, to_info,
                      LOWER, f_lower(x), f_info(x), xls,
                      UPPER, f_upper(y), f_info(y), yus);
    ru = mul_assign_z(UPPER, upper(), to_info,
                      UPPER, f_upper(x), f_info(x), xus,
                      UPPER, f_upper(y), f_info(y), yus);
  }
  else if (yus <= 0) {
    // xl < 0 < xu, yl <= yu <= 0
    rl = mul_assign_z(LOWER, to_lower, to_info,
                      UPPER, f_upper(x), f_info(x), xus,
                      LOWER, f_lower(y), f_info(y), yls);
    ru = mul_assign_z(UPPER, upper(), to_info,
                      LOWER, f_lower(x), f_info(x), xls,
                      LOWER, f_lower(y), f_info(y), yls);
  }
  else {
    // xl < 0 < xu, yl < 0 < yu
    PPL_DIRTY_TEMP(To_Boundary, tmp);
    PPL_DIRTY_TEMP(To_Info, tmp_info);
    tmp_info.clear();
    Result tmp_r;
    tmp_r = Boundary_NS::mul_assign(LOWER, tmp, tmp_info,
                                    UPPER, f_upper(x), f_info(x),
                                    LOWER, f_lower(y), f_info(y));
    rl = Boundary_NS::mul_assign(LOWER, to_lower, to_info,
                                 LOWER, f_lower(x), f_info(x),
                                 UPPER, f_upper(y), f_info(y));
    if (gt(LOWER, to_lower, to_info, LOWER, tmp, tmp_info)) {
      to_lower = tmp;
      rl = tmp_r;
    }
    tmp_info.clear();
    tmp_r = Boundary_NS::mul_assign(UPPER, tmp, tmp_info,
                                    UPPER, f_upper(x), f_info(x),
                                    UPPER, f_upper(y), f_info(y));
    ru = Boundary_NS::mul_assign(UPPER, upper(), to_info,
                                 LOWER, f_lower(x), f_info(x),
                                 LOWER, f_lower(y), f_info(y));
    if (lt(UPPER, upper(), to_info, UPPER, tmp, tmp_info)) {
      upper() = tmp;
      ru = tmp_r;
    }
  }
  assign_or_swap(lower(), to_lower);
  assign_or_swap(info(), to_info);
  PPL_ASSERT(OK());
  return combine(rl, ru);
}

/**
+-----------+-----------+-----------+
|     /     |  yu < 0   |  yl > 0   |
+-----------+-----------+-----------+
|   xu<=0   |xu/yl,xl/yu|xl/yl,xu/yu|
+-----------+-----------+-----------+
|xl<=0 xu>=0|xu/yu,xl/yu|xl/yl,xu/yl|
+-----------+-----------+-----------+
|   xl>=0   |xu/yu,xl/yl|xl/yu,xu/yl|
+-----------+-----------+-----------+
**/
template <typename To_Boundary, typename To_Info>
template <typename From1, typename From2>
inline typename Enable_If<((Is_Singleton<From1>::value
                            || Is_Interval<From1>::value)
                           && (Is_Singleton<From2>::value
                               || Is_Interval<From2>::value)), I_Result>::type
Interval<To_Boundary, To_Info>::div_assign(const From1& x, const From2& y) {
  PPL_ASSERT(f_OK(x));
  PPL_ASSERT(f_OK(y));
  if (check_empty_arg(x) || check_empty_arg(y)) {
    return assign(EMPTY);
  }
  int yls = sgn_b(LOWER, f_lower(y), f_info(y));
  int yus = (yls > 0) ? 1 : sgn_b(UPPER, f_upper(y), f_info(y));
  if (yls == 0 && yus == 0) {
    return assign(EMPTY);
  }
  int inf_sign = Parma_Polyhedra_Library::infinity_sign(x);
  if (inf_sign != 0) {
    if (Parma_Polyhedra_Library::infinity_sign(y) != 0) {
      return assign(EMPTY);
    }
    if (yls == -yus) {
      return set_infinities();
    }
    if (yls < 0 || yus < 0) {
      inf_sign = -inf_sign;
    }
    if (inf_sign < 0) {
      return assign(MINUS_INFINITY);
    }
    else {
      return assign(PLUS_INFINITY);
    }
  }
  int xls = sgn_b(LOWER, f_lower(x), f_info(x));
  int xus = (xls > 0) ? 1 : sgn_b(UPPER, f_upper(x), f_info(x));

  PPL_DIRTY_TEMP(To_Info, to_info);
  to_info.clear();
  Result rl;
  Result ru;
  PPL_DIRTY_TEMP(To_Boundary, to_lower);
  if (yls >= 0) {
    if (xls >= 0) {
      rl = div_assign_z(LOWER, to_lower, to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        UPPER, f_upper(y), f_info(y), yus);
      ru = div_assign_z(UPPER, upper(), to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        LOWER, f_lower(y), f_info(y), yls);
    }
    else if (xus <= 0) {
      rl = div_assign_z(LOWER, to_lower, to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        LOWER, f_lower(y), f_info(y), yls);
      ru = div_assign_z(UPPER, upper(), to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        UPPER, f_upper(y), f_info(y), yus);
    }
    else {
      rl = div_assign_z(LOWER, to_lower, to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        LOWER, f_lower(y), f_info(y), yls);
      ru = div_assign_z(UPPER, upper(), to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        LOWER, f_lower(y), f_info(y), yls);
    }
  }
  else if (yus <= 0) {
    if (xls >= 0) {
      rl = div_assign_z(LOWER, to_lower, to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        UPPER, f_upper(y), f_info(y), yus);
      ru = div_assign_z(UPPER, upper(), to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        LOWER, f_lower(y), f_info(y), yls);
    }
    else if (xus <= 0) {
      rl = div_assign_z(LOWER, to_lower, to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        LOWER, f_lower(y), f_info(y), yls);
      ru = div_assign_z(UPPER, upper(), to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        UPPER, f_upper(y), f_info(y), yus);
    }
    else {
      rl = div_assign_z(LOWER, to_lower, to_info,
                        UPPER, f_upper(x), f_info(x), xus,
                        UPPER, f_upper(y), f_info(y), yus);
      ru = div_assign_z(UPPER, upper(), to_info,
                        LOWER, f_lower(x), f_info(x), xls,
                        UPPER, f_upper(y), f_info(y), yus);
    }
  }
  else {
    return static_cast<I_Result>(assign(UNIVERSE) | I_SINGULARITIES);
  }
  assign_or_swap(lower(), to_lower);
  assign_or_swap(info(), to_info);
  PPL_ASSERT(OK());
  return combine(rl, ru);
}

template <typename B, typename Info, typename T>
inline typename Enable_If<Is_Singleton<T>::value, Interval<B, Info> >::type
operator+(const Interval<B, Info>& x, const T& y) {
  Interval<B, Info> z;
  z.add_assign(x, y);
  return z;
}

template <typename B, typename Info, typename T>
inline typename Enable_If<Is_Singleton<T>::value, Interval<B, Info> >::type
operator+(const T& x, const Interval<B, Info>& y) {
  Interval<B, Info> z;
  z.add_assign(x, y);
  return z;
}

template <typename B, typename Info>
inline Interval<B, Info>
operator+(const Interval<B, Info>& x, const Interval<B, Info>& y) {
  Interval<B, Info> z;
  z.add_assign(x, y);
  return z;
}

template <typename B, typename Info, typename T>
inline typename Enable_If<Is_Singleton<T>::value, Interval<B, Info> >::type
operator-(const Interval<B, Info>& x, const T& y) {
  Interval<B, Info> z;
  z.sub_assign(x, y);
  return z;
}

template <typename B, typename Info, typename T>
inline typename Enable_If<Is_Singleton<T>::value, Interval<B, Info> >::type
operator-(const T& x, const Interval<B, Info>& y) {
  Interval<B, Info> z;
  z.sub_assign(x, y);
  return z;
}

template <typename B, typename Info>
inline Interval<B, Info>
operator-(const Interval<B, Info>& x, const Interval<B, Info>& y) {
  Interval<B, Info> z;
  z.sub_assign(x, y);
  return z;
}

template <typename B, typename Info, typename T>
inline typename Enable_If<Is_Singleton<T>::value, Interval<B, Info> >::type
operator*(const Interval<B, Info>& x, const T& y) {
  Interval<B, Info> z;
  z.mul_assign(x, y);
  return z;
}

template <typename B, typename Info, typename T>
inline typename Enable_If<Is_Singleton<T>::value, Interval<B, Info> >::type
operator*(const T& x, const Interval<B, Info>& y) {
  Interval<B, Info> z;
  z.mul_assign(x, y);
  return z;
}

template <typename B, typename Info>
inline Interval<B, Info>
operator*(const Interval<B, Info>& x, const Interval<B, Info>& y) {
  Interval<B, Info> z;
  z.mul_assign(x, y);
  return z;
}

template <typename B, typename Info, typename T>
inline typename Enable_If<Is_Singleton<T>::value, Interval<B, Info> >::type
operator/(const Interval<B, Info>& x, const T& y) {
  Interval<B, Info> z;
  z.div_assign(x, y);
  return z;
}

template <typename B, typename Info, typename T>
inline typename Enable_If<Is_Singleton<T>::value, Interval<B, Info> >::type
operator/(const T& x, const Interval<B, Info>& y) {
  Interval<B, Info> z;
  z.div_assign(x, y);
  return z;
}

template <typename B, typename Info>
inline Interval<B, Info>
operator/(const Interval<B, Info>& x, const Interval<B, Info>& y) {
  Interval<B, Info> z;
  z.div_assign(x, y);
  return z;
}

template <typename Boundary, typename Info>
inline std::ostream&
operator<<(std::ostream& os, const Interval<Boundary, Info>& x) {
  if (check_empty_arg(x)) {
    return os << "[]";
  }
  if (x.is_singleton()) {
    output(os, x.lower(), Numeric_Format(), ROUND_NOT_NEEDED);
    return os;
  }
  os << (x.lower_is_open() ? "(" : "[");
  if (x.info().get_boundary_property(LOWER, SPECIAL)) {
    os << "-inf";
  }
  else {
    output(os, x.lower(), Numeric_Format(), ROUND_NOT_NEEDED);
  }
  os << ", ";
  if (x.info().get_boundary_property(UPPER, SPECIAL)) {
    os << "+inf";
  }
  else {
    output(os, x.upper(), Numeric_Format(), ROUND_NOT_NEEDED);
  }
  os << (x.upper_is_open() ? ")" : "]");
  return os;
}

template <typename Boundary, typename Info>
inline void
Interval<Boundary, Info>::ascii_dump(std::ostream& s) const {
  using Parma_Polyhedra_Library::ascii_dump;
  s << "info ";
  info().ascii_dump(s);
  s << " lower ";
  ascii_dump(s, lower());
  s << " upper ";
  ascii_dump(s, upper());
  s << '\n';
}

template <typename Boundary, typename Info>
inline bool
Interval<Boundary, Info>::ascii_load(std::istream& s) {
  using Parma_Polyhedra_Library::ascii_load;
  std::string str;
  if (!(s >> str) || str != "info") {
    return false;
  }
  if (!info().ascii_load(s)) {
    return false;
  }
  if (!(s >> str) || str != "lower") {
    return false;
  }
  if (!ascii_load(s, lower())) {
    return false;
  }
  if (!(s >> str) || str != "upper") {
    return false;
  }
  if (!ascii_load(s, upper())) {
    return false;
  }
  PPL_ASSERT(OK());
  return true;
}

/*! \brief
  Helper class to select the appropriate numerical type to perform
  boundary computations so as to reduce the chances of overflow without
  incurring too much overhead.
*/
template <typename Interval_Boundary_Type> struct Select_Temp_Boundary_Type;

template <typename Interval_Boundary_Type>
struct Select_Temp_Boundary_Type {
  typedef Interval_Boundary_Type type;
};

#if PPL_SUPPORTED_DOUBLE
template <>
struct Select_Temp_Boundary_Type<float> {
  typedef double type;
};
#endif

template <>
struct Select_Temp_Boundary_Type<char> {
  typedef signed long long type;
};

template <>
struct Select_Temp_Boundary_Type<signed char> {
  typedef signed long long type;
};

template <>
struct Select_Temp_Boundary_Type<unsigned char> {
  typedef signed long long type;
};

template <>
struct Select_Temp_Boundary_Type<signed short> {
  typedef signed long long type;
};

template <>
struct Select_Temp_Boundary_Type<unsigned short> {
  typedef signed long long type;
};

template <>
struct Select_Temp_Boundary_Type<signed int> {
  typedef signed long long type;
};

template <>
struct Select_Temp_Boundary_Type<unsigned int> {
  typedef signed long long type;
};

template <>
struct Select_Temp_Boundary_Type<signed long> {
  typedef signed long long type;
};

template <>
struct Select_Temp_Boundary_Type<unsigned long> {
  typedef signed long long type;
};

template <>
struct Select_Temp_Boundary_Type<unsigned long long> {
  typedef signed long long type;
};

/*! \relates Interval */
template <typename Boundary, typename Info>
inline void
swap(Interval<Boundary, Info>& x, Interval<Boundary, Info>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Interval_templates.hh line 1. */
/* Interval class implementation: non-inline template functions.
*/


#include <algorithm>

namespace Parma_Polyhedra_Library {

template <typename Boundary, typename Info>
template <typename C>
typename Enable_If<Is_Same_Or_Derived<I_Constraint_Base, C>::value, I_Result>::type
Interval<Boundary, Info>::lower_extend(const C& c) {
  PPL_ASSERT(OK());
  bool open;
  switch (c.rel()) {
  case V_LGE:
    return lower_extend();
  case V_NAN:
    return I_NOT_EMPTY | I_EXACT | I_UNCHANGED;
  case V_GT:
    open = true;
    break;
  case V_GE: // Fall through.
  case V_EQ:
    open = false;
    break;
  default:
    PPL_UNREACHABLE;
    return I_NOT_EMPTY | I_EXACT | I_UNCHANGED;
  }
  min_assign(LOWER, lower(), info(), LOWER, c.value(), f_info(c.value(), open));
  PPL_ASSERT(OK());
  return I_ANY;
}

template <typename Boundary, typename Info>
template <typename C>
typename Enable_If<Is_Same_Or_Derived<I_Constraint_Base, C>::value, I_Result>::type
Interval<Boundary, Info>::upper_extend(const C& c) {
  PPL_ASSERT(OK());
  bool open;
  switch (c.rel()) {
  case V_LGE:
    return lower_extend();
  case V_NAN:
    return I_NOT_EMPTY | I_EXACT | I_UNCHANGED;
  case V_LT:
    open = true;
    break;
  case V_LE: // Fall through.
  case V_EQ:
    open = false;
    break;
  default:
    PPL_UNREACHABLE;
    return I_NOT_EMPTY | I_EXACT | I_UNCHANGED;
  }
  max_assign(UPPER, upper(), info(), UPPER, c.value(), f_info(c.value(), open));
  PPL_ASSERT(OK());
  return I_ANY;
}

template <typename Boundary, typename Info>
template <typename From, typename Iterator>
typename Enable_If<Is_Interval<From>::value, void>::type
Interval<Boundary, Info>::CC76_widening_assign(const From& y,
                                               Iterator first,
                                               Iterator last) {
  // We assume that `y' is contained in or equal to `*this'.
  PPL_ASSERT(contains(y));
  Interval<Boundary, Info>& x = *this;

  // Upper bound.
  if (!x.upper_is_boundary_infinity()) {
    Boundary& x_ub = x.upper();
    const Boundary& y_ub = y.upper();
    PPL_ASSERT(!y.upper_is_boundary_infinity() && y_ub <= x_ub);
    if (y_ub < x_ub) {
      Iterator k = std::lower_bound(first, last, x_ub);
      if (k != last) {
        if (x_ub < *k) {
          x_ub = *k;
        }
      }
      else {
        x.upper_extend();
      }
    }
  }

  // Lower bound.
  if (!x.lower_is_boundary_infinity()) {
    Boundary& x_lb = x.lower();
    const Boundary& y_lb = y.lower();
    PPL_ASSERT(!y.lower_is_boundary_infinity() && y_lb >= x_lb);
    if (y_lb > x_lb) {
      Iterator k = std::lower_bound(first, last, x_lb);
      if (k != last) {
        if (x_lb < *k) {
          if (k != first) {
            x_lb = *--k;
          }
          else {
            x.lower_extend();
          }
        }
      }
      else {
        if (k != first) {
          x_lb = *--k;
        }
        else {
          x.lower_extend();
        }
      }
    }
  }
}

template <typename Boundary, typename Info>
Interval<Boundary, Info>::Interval(const char* s) {
  // Get the lower bound.
  Boundary lower_bound;
  Result lower_r = assign_r(lower_bound, s, ROUND_DOWN);
  if (lower_r == V_CVT_STR_UNK || lower_r == V_NAN) {
    throw std::invalid_argument("PPL::Interval(const char* s)"
                                " with s invalid");
  }
  lower_r = result_relation_class(lower_r);

  // Get the upper bound.
  Boundary upper_bound;
  Result upper_r = assign_r(upper_bound, s, ROUND_UP);
  PPL_ASSERT(upper_r != V_CVT_STR_UNK && upper_r != V_NAN);
  upper_r = result_relation_class(upper_r);

  // Build the interval.
  bool lower_open = false;
  bool upper_open = false;
  bool lower_boundary_infinity = false;
  bool upper_boundary_infinity = false;
  switch (lower_r) {
  case V_EQ: // Fall through.
  case V_GE:
    break;
  case V_GT:
    lower_open = true;
    break;
  case V_GT_MINUS_INFINITY:
    lower_open = true;
    // Fall through.
  case V_EQ_MINUS_INFINITY:
    lower_boundary_infinity = true;
    break;
  case V_EQ_PLUS_INFINITY: // Fall through.
  case V_LT_PLUS_INFINITY:
    if (upper_r == V_EQ_PLUS_INFINITY || upper_r == V_LT_PLUS_INFINITY) {
      assign(UNIVERSE);
    }
    else {
      assign(EMPTY);
    }
    break;
  default:
    PPL_UNREACHABLE;
    break;
  }
  switch (upper_r) {
  case V_EQ: // Fall through.
  case V_LE:
    break;
  case V_LT:
    upper_open = true;
    break;
  case V_EQ_MINUS_INFINITY: // Fall through.
  case V_GT_MINUS_INFINITY:
    if (lower_r == V_EQ_MINUS_INFINITY || lower_r == V_GT_MINUS_INFINITY) {
      assign(UNIVERSE);
    }
    else {
      assign(EMPTY);
    }
    break;
  case V_LT_PLUS_INFINITY:
    upper_open = true;
    // Fall through.
  case V_EQ_PLUS_INFINITY:
    upper_boundary_infinity = true;
    break;
  default:
    PPL_UNREACHABLE;
    break;
  }

  if (!lower_boundary_infinity
      && !upper_boundary_infinity
      && (lower_bound > upper_bound
          || (lower_open && lower_bound == upper_bound))) {
    assign(EMPTY);
  }
  else {
    if (lower_boundary_infinity) {
      set_minus_infinity(LOWER, lower(), info(), lower_open);
    }
    else {
      Boundary_NS::assign(LOWER, lower(), info(),
                          LOWER, lower_bound, SCALAR_INFO, lower_open);
    }
    if (upper_boundary_infinity) {
      set_plus_infinity(UPPER, upper(), info(), upper_open);
    }
    else {
      Boundary_NS::assign(UPPER, upper(), info(),
                          UPPER, upper_bound, SCALAR_INFO, upper_open);
    }
  }
}


template <typename Boundary, typename Info>
inline std::istream&
operator>>(std::istream& is, Interval<Boundary, Info>& x) {
  Boundary lower_bound;
  Boundary upper_bound;
  bool lower_boundary_infinity = false;
  bool upper_boundary_infinity = false;
  bool lower_open = false;
  bool upper_open = false;
  Result lower_r;
  Result upper_r;

  // Eat leading white space.
  char c;
  do {
    if (!is.get(c)) {
      goto fail;
    }
  } while (is_space(c));

  // Get the opening parenthesis and handle the empty interval case.
  if (c == '(') {
    lower_open = true;
  }
  else if (c == '[') {
    if (!is.get(c)) {
      goto fail;
    }
    if (c == ']') {
      // Empty interval.
      x.assign(EMPTY);
      return is;
    }
    else {
      is.unget();
    }
  }
  else {
    goto unexpected_char;
  }

  // Get the lower bound.
  lower_r = input(lower_bound, is, ROUND_DOWN);
  if (lower_r == V_CVT_STR_UNK || lower_r == V_NAN) {
    goto fail;
  }
  lower_r = result_relation_class(lower_r);

  // Match the comma separating the lower and upper bounds.
  do {
    if (!is.get(c)) {
      goto fail;
    }
  } while (is_space(c));
  if (c != ',') {
    goto unexpected_char;
  }

  // Get the upper bound.
  upper_r = input(upper_bound, is, ROUND_UP);
  if (upper_r == V_CVT_STR_UNK || upper_r == V_NAN) {
    goto fail;
  }
  upper_r = result_relation_class(upper_r);

  // Get the closing parenthesis.
  do {
    if (!is.get(c)) {
      goto fail;
    }
  } while (is_space(c));
  if (c == ')') {
    upper_open = true;
  }
  else if (c != ']') {
  unexpected_char:
    is.unget();
  fail:
    is.setstate(std::ios::failbit);
    return is;
  }

  // Build interval.
  switch (lower_r) {
  case V_EQ: // Fall through.
  case V_GE:
    break;
  case V_GT:
    lower_open = true;
    break;
  case V_GT_MINUS_INFINITY:
    lower_open = true;
    // Fall through.
  case V_EQ_MINUS_INFINITY:
    lower_boundary_infinity = true;
    break;
  case V_EQ_PLUS_INFINITY: // Fall through.
  case V_LT_PLUS_INFINITY:
    if (upper_r == V_EQ_PLUS_INFINITY || upper_r == V_LT_PLUS_INFINITY) {
      x.assign(UNIVERSE);
    }
    else {
      x.assign(EMPTY);
    }
    return is;
  default:
    PPL_UNREACHABLE;
    break;
  }
  switch (upper_r) {
  case V_EQ: // Fall through.
  case V_LE:
    break;
  case V_LT:
    upper_open = true;
    break;
  case V_GT_MINUS_INFINITY:
    upper_open = true;
    // Fall through.
  case V_EQ_MINUS_INFINITY:
    if (lower_r == V_EQ_MINUS_INFINITY || lower_r == V_GT_MINUS_INFINITY) {
      x.assign(UNIVERSE);
    }
    else {
      x.assign(EMPTY);
    }
    return is;
  case V_EQ_PLUS_INFINITY: // Fall through.
  case V_LT_PLUS_INFINITY:
    upper_boundary_infinity = true;
    break;
  default:
    PPL_UNREACHABLE;
    break;
  }

  if (!lower_boundary_infinity
      && !upper_boundary_infinity
      && (lower_bound > upper_bound
          || (lower_open && lower_bound == upper_bound))) {
    x.assign(EMPTY);
  }
  else {
    if (lower_boundary_infinity) {
      set_minus_infinity(LOWER, x.lower(), x.info(), lower_open);
    }
    else {
      assign(LOWER, x.lower(), x.info(),
             LOWER, lower_bound, SCALAR_INFO, lower_open);
    }
    if (upper_boundary_infinity) {
      set_plus_infinity(UPPER, x.upper(), x.info(), upper_open);
    }
    else {
      assign(UPPER, x.upper(), x.info(),
             UPPER, upper_bound, SCALAR_INFO, upper_open);
    }
  }
  return is;
}

template <typename Boundary, typename Info>
template <typename From>
typename Enable_If<Is_Interval<From>::value, bool>::type
Interval<Boundary, Info>::simplify_using_context_assign(const From& y) {
  // FIXME: the following code wrongly assumes that intervals are closed
  if (lt(UPPER, upper(), info(), LOWER, f_lower(y), f_info(y))) {
    lower_extend();
    return false;
  }
  if (gt(LOWER, lower(), info(), UPPER, f_upper(y), f_info(y))) {
    upper_extend();
    return false;
  }
  // Weakening the upper bound.
  if (!upper_is_boundary_infinity() && !y.upper_is_boundary_infinity()
      && y.upper() <= upper()) {
    upper_extend();
  }
  // Weakening the lower bound.
  if (!lower_is_boundary_infinity() && !y.lower_is_boundary_infinity()
      && y.lower() >= lower()) {
    lower_extend();
  }
  return true;
}

template <typename Boundary, typename Info>
template <typename From>
typename Enable_If<Is_Interval<From>::value, void>::type
Interval<Boundary, Info>::empty_intersection_assign(const From&) {
  // FIXME: write me.
  assign(EMPTY);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Interval_defs.hh line 780. */

/* Automatically generated from PPL source file ../src/Integer_Interval.hh line 28. */
#include <gmpxx.h>

namespace Parma_Polyhedra_Library {

struct Integer_Interval_Info_Policy {
  const_bool_nodef(store_special, true);
  const_bool_nodef(store_open, false);
  const_bool_nodef(cache_empty, true);
  const_bool_nodef(cache_singleton, true);
  const_int_nodef(next_bit, 0);
  const_bool_nodef(may_be_empty, true);
  const_bool_nodef(may_contain_infinity, false);
  const_bool_nodef(check_empty_result, false);
  const_bool_nodef(check_inexact, false);
};

typedef Interval_Info_Bitset<unsigned int, Integer_Interval_Info_Policy> Integer_Interval_Info;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! An interval with integral, necessarily closed boundaries.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
typedef Interval<mpz_class, Integer_Interval_Info> Integer_Interval;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/initializer.hh line 1. */
/* Nifty counter object for the initialization of the library.
*/


/* Automatically generated from PPL source file ../src/Init_defs.hh line 1. */
/* Init class declaration.
*/


/* Automatically generated from PPL source file ../src/Init_defs.hh line 29. */

namespace Parma_Polyhedra_Library {

/*! \brief
  Sets the FPU rounding mode so that the PPL abstractions based on
  floating point numbers work correctly.

  This is performed automatically at initialization-time.  Calling
  this function is needed only if restore_pre_PPL_rounding() has been
  previously called.
*/
void set_rounding_for_PPL();

/*! \brief
  Sets the FPU rounding mode as it was before initialization of the PPL.

  This is important if the application uses floating-point computations
  outside the PPL.  It is crucial when the application uses functions
  from a mathematical library that are not guaranteed to work correctly
  under all rounding modes.

  After calling this function it is absolutely necessary to call
  set_rounding_for_PPL() before using any PPL abstractions based on
  floating point numbers.
  This is performed automatically at finalization-time.
*/
void restore_pre_PPL_rounding();

} // namespace Parma_Polyhedra_Library

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Class for initialization and finalization.
/*! \ingroup PPL_CXX_interface
  <EM>Nifty Counter</EM> initialization class,
  ensuring that the library is initialized only once
  and before its first use.
  A count of the number of translation units using the library
  is maintained. A static object of Init type will be declared
  by each translation unit using the library.  As a result,
  only one of them will initialize and properly finalize
  the library.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
class Parma_Polyhedra_Library::Init {
public:
  //! Initializes the PPL.
  Init();

  //! Finalizes the PPL.
  ~Init();

private:
  /*! \brief
    Default precision parameter used for irrational calculations.

    The default is chosen to have a precision greater than most
    precise IEC 559 floating point (112 bits of mantissa).
  */
  static const unsigned DEFAULT_IRRATIONAL_PRECISION = 128U;

  //! Count the number of objects created.
  static unsigned int count;
  static fpu_rounding_direction_type old_rounding_direction;

  friend void set_rounding_for_PPL();
  friend void restore_pre_PPL_rounding();
};

/* Automatically generated from PPL source file ../src/Init_inlines.hh line 1. */
/* Init class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Init_inlines.hh line 29. */

namespace Parma_Polyhedra_Library {

inline void
set_rounding_for_PPL() {
#if PPL_CAN_CONTROL_FPU
    fpu_set_rounding_direction(round_fpu_dir(ROUND_DIRECT));
#endif
}

inline void
restore_pre_PPL_rounding() {
#if PPL_CAN_CONTROL_FPU
  fpu_set_rounding_direction(Init::old_rounding_direction);
#endif
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Init_defs.hh line 98. */


/* Automatically generated from PPL source file ../src/initializer.hh line 28. */

#ifndef PPL_NO_AUTOMATIC_INITIALIZATION

static Parma_Polyhedra_Library::Init Parma_Polyhedra_Library_initializer;

#else

namespace Parma_Polyhedra_Library {

namespace Implementation {

void initialize_aux();
void finalize_aux();

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

#endif

namespace Parma_Polyhedra_Library {

//! Initializes the library.
inline void
initialize() {
#ifdef PPL_NO_AUTOMATIC_INITIALIZATION
  Implementation::initialize_aux();
#endif
}

//! Finalizes the library.
inline void
finalize() {
#ifdef PPL_NO_AUTOMATIC_INITIALIZATION
  Implementation::finalize_aux();
#endif
}

} //namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Expression_Impl_defs.hh line 1. */
/* Linear_Expression_Impl class declaration.
*/


/* Automatically generated from PPL source file ../src/Linear_Expression_Impl_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename Row>
class Linear_Expression_Impl;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Expression_Interface_defs.hh line 1. */
/* Linear_Expression_Interface class declaration.
*/


/* Automatically generated from PPL source file ../src/Linear_Expression_Interface_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Linear_Expression_Interface;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Coefficient_defs.hh line 1. */
/* Coefficient class declaration.
*/


/* Automatically generated from PPL source file ../src/Coefficient_types.hh line 1. */


/* Automatically generated from PPL source file ../src/Coefficient_types.hh line 17. */

#ifdef PPL_GMP_INTEGERS
/* Automatically generated from PPL source file ../src/GMP_Integer_types.hh line 1. */


/* Automatically generated from PPL source file ../src/GMP_Integer_types.hh line 18. */
#include <gmpxx.h>

namespace Parma_Polyhedra_Library {

/*! \class Parma_Polyhedra_Library::GMP_Integer
  \brief
  Unbounded integers as provided by the GMP library.

  \ingroup PPL_CXX_interface
  GMP_Integer is an alias for the <CODE>mpz_class</CODE> type
  defined in the C++ interface of the GMP library.
  For more information, see <CODE>http://gmplib.org/</CODE>
*/
typedef mpz_class GMP_Integer;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Coefficient traits specialization for unbounded integers.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <>
struct Coefficient_traits_template<GMP_Integer> {
  //! The type used for references to const unbounded integers.
  typedef const GMP_Integer& const_reference;
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Coefficient_types.hh line 20. */
#endif

#if defined(PPL_CHECKED_INTEGERS) || defined(PPL_NATIVE_INTEGERS)

namespace Parma_Polyhedra_Library {

//! A policy for checked bounded integer coefficients.
/*! \ingroup PPL_CXX_interface */
struct Bounded_Integer_Coefficient_Policy {
  //! Check for overflowed result.
  const_bool_nodef(check_overflow, true);

  //! Do not check for attempts to add infinities with different sign.
  const_bool_nodef(check_inf_add_inf, false);

  //! Do not check for attempts to subtract infinities with same sign.
  const_bool_nodef(check_inf_sub_inf, false);

  //! Do not check for attempts to multiply infinities by zero.
  const_bool_nodef(check_inf_mul_zero, false);

  //! Do not check for attempts to divide by zero.
  const_bool_nodef(check_div_zero, false);

  //! Do not check for attempts to divide infinities.
  const_bool_nodef(check_inf_div_inf, false);

  //! Do not check for attempts to compute remainder of infinities.
  const_bool_nodef(check_inf_mod, false);

  //! Do not checks for attempts to take the square root of a negative number.
  const_bool_nodef(check_sqrt_neg, false);

  //! Do not handle not-a-number special value.
  const_bool_nodef(has_nan, false);

  //! Do not handle infinity special values.
  const_bool_nodef(has_infinity, false);

  /*! \brief
    The checked number can always be safely converted to the
    underlying type \p T and vice-versa.
  */
  const_bool_nodef(convertible, true);

  //! Do not honor requests to check for FPU inexact results.
  const_bool_nodef(fpu_check_inexact, false);

  //! Do not make extra checks to detect FPU NaN results.
  const_bool_nodef(fpu_check_nan_result, true);

  /*! \brief
    For constructors, by default use the same rounding used by
    underlying type.
  */
  static const Rounding_Dir ROUND_DEFAULT_CONSTRUCTOR = ROUND_NATIVE;

  /*! \brief
    For overloaded operators (operator+(), operator-(), ...), by
    default use the same rounding used by the underlying type.
  */
  static const Rounding_Dir ROUND_DEFAULT_OPERATOR = ROUND_NATIVE;

  /*! \brief
    For input functions, by default use the same rounding used by
    the underlying type.
  */
  static const Rounding_Dir ROUND_DEFAULT_INPUT = ROUND_NATIVE;

  /*! \brief
    For output functions, by default use the same rounding used by
    the underlying type.
  */
  static const Rounding_Dir ROUND_DEFAULT_OUTPUT = ROUND_NATIVE;

  /*! \brief
    For all other functions, by default use the same rounding used by
    the underlying type.
  */
  static const Rounding_Dir ROUND_DEFAULT_FUNCTION = ROUND_NATIVE;

  /*! \brief
    Handles \p r: called by all constructors, operators and functions that
    do not return a Result value.
  */
  static void handle_result(Result r);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Coefficient traits specialization for 8 bits checked integers.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Policy>
struct Coefficient_traits_template<Checked_Number<int8_t, Policy> > {
  //! The type used for references to const 8 bit checked integers.
  typedef Checked_Number<int8_t, Policy> const_reference;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Coefficient traits specialization for 16 bits checked integers.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Policy>
struct Coefficient_traits_template<Checked_Number<int16_t, Policy> > {
  //! The type used for references to const 16 bit checked integers.
  typedef Checked_Number<int16_t, Policy> const_reference;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Coefficient traits specialization for 32 bits checked integers.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Policy>
struct Coefficient_traits_template<Checked_Number<int32_t, Policy> > {
  //! The type used for references to const 32 bit checked integers.
  typedef Checked_Number<int32_t, Policy> const_reference;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Coefficient traits specialization for 64 bits checked integers.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Policy>
struct Coefficient_traits_template<Checked_Number<int64_t, Policy> > {
  //! The type used for references to const 64 bit checked integers.
  typedef const Checked_Number<int64_t, Policy>& const_reference;
};

} // namespace Parma_Polyhedra_Library

#endif // defined(PPL_CHECKED_INTEGERS) || defined(PPL_NATIVE_INTEGERS)

namespace Parma_Polyhedra_Library {

//! An alias for easily naming the type of PPL coefficients.
/*! \ingroup PPL_CXX_interface
  Objects of type Coefficient are used to implement the integral valued
  coefficients occurring in linear expressions, constraints, generators,
  intervals, bounding boxes and so on.  Depending on the chosen
  configuration options (see file <CODE>README.configure</CODE>),
  a Coefficient may actually be:
    - The GMP_Integer type, which in turn is an alias for the
      <CODE>mpz_class</CODE> type implemented by the C++ interface
      of the GMP library (this is the default configuration).
    - An instance of the Checked_Number class template: with the policy
      Bounded_Integer_Coefficient_Policy, this implements overflow
      detection on top of a native integral type (available template
      instances include checked integers having 8, 16, 32 or 64 bits);
      with the Checked_Number_Transparent_Policy, this is a wrapper
      for native integral types with no overflow detection
      (available template instances are as above).
*/
typedef PPL_COEFFICIENT_TYPE Coefficient;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! An alias for easily naming the coefficient traits.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
typedef Coefficient_traits_template<Coefficient> Coefficient_traits;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Coefficient_defs.hh line 28. */

#if defined(PPL_CHECKED_INTEGERS) || defined(PPL_NATIVE_INTEGERS)
/* Automatically generated from PPL source file ../src/Coefficient_defs.hh line 32. */
#endif

#ifdef PPL_GMP_INTEGERS
/* Automatically generated from PPL source file ../src/GMP_Integer_defs.hh line 1. */
/* GMP_Integer class declaration.
*/


/* Automatically generated from PPL source file ../src/GMP_Integer_defs.hh line 29. */
#include <cstddef>

namespace Parma_Polyhedra_Library {

//! \name Accessor Functions
//@{

//! Returns a const reference to the underlying integer value.
/*! \relates GMP_Integer */
const mpz_class& raw_value(const GMP_Integer& x);

//! Returns a reference to the underlying integer value.
/*! \relates GMP_Integer */
mpz_class& raw_value(GMP_Integer& x);

//@} // Accessor Functions

//! \name Arithmetic Operators
//@{

//! Assigns to \p x its negation.
/*! \relates GMP_Integer */
void neg_assign(GMP_Integer& x);

//! Assigns to \p x the negation of \p y.
/*! \relates GMP_Integer */
void neg_assign(GMP_Integer& x, const GMP_Integer& y);

//! Assigns to \p x its absolute value.
/*! \relates GMP_Integer */
void abs_assign(GMP_Integer& x);

//! Assigns to \p x the absolute value of \p y.
/*! \relates GMP_Integer */
void abs_assign(GMP_Integer& x, const GMP_Integer& y);

//! Assigns to \p x the remainder of the division of \p y by \p z.
/*! \relates GMP_Integer */
void rem_assign(GMP_Integer& x,
                const GMP_Integer& y, const GMP_Integer& z);

//! Assigns to \p x the greatest common divisor of \p y and \p z.
/*! \relates GMP_Integer */
void gcd_assign(GMP_Integer& x,
                const GMP_Integer& y, const GMP_Integer& z);

//! Extended GCD.
/*! \relates GMP_Integer
  Assigns to \p x the greatest common divisor of \p y and \p z, and to
  \p s and \p t the values such that \p y * \p s + \p z * \p t = \p x.
*/
void gcdext_assign(GMP_Integer& x, GMP_Integer& s, GMP_Integer& t,
                   const GMP_Integer& y, const GMP_Integer& z);

//! Assigns to \p x the least common multiple of \p y and \p z.
/*! \relates GMP_Integer */
void lcm_assign(GMP_Integer& x,
                const GMP_Integer& y, const GMP_Integer& z);

//! Assigns to \p x the value <CODE>x + y * z</CODE>.
/*! \relates GMP_Integer */
void add_mul_assign(GMP_Integer& x,
                    const GMP_Integer& y, const GMP_Integer& z);

//! Assigns to \p x the value <CODE>x - y * z</CODE>.
/*! \relates GMP_Integer */
void sub_mul_assign(GMP_Integer& x,
                    const GMP_Integer& y, const GMP_Integer& z);

//! Assigns to \p x the value \f$ y \cdot 2^\mathtt{exp} \f$.
/*! \relates GMP_Integer */
void mul_2exp_assign(GMP_Integer& x, const GMP_Integer& y, unsigned int exp);

//! Assigns to \p x the value \f$ y / 2^\mathtt{exp} \f$.
/*! \relates GMP_Integer */
void div_2exp_assign(GMP_Integer& x, const GMP_Integer& y, unsigned int exp);

/*! \brief
  If \p z divides \p y, assigns to \p x the quotient of the integer
  division of \p y and \p z.

  \relates GMP_Integer
  The behavior is undefined if \p z does not divide \p y.
*/
void exact_div_assign(GMP_Integer& x,
                      const GMP_Integer& y, const GMP_Integer& z);

//! Assigns to \p x the integer square root of \p y.
/*! \relates GMP_Integer */
void sqrt_assign(GMP_Integer& x, const GMP_Integer& y);

/*! \brief
  Returns a negative, zero or positive value depending on whether
  \p x is lower than, equal to or greater than \p y, respectively.

  \relates GMP_Integer
*/
int cmp(const GMP_Integer& x, const GMP_Integer& y);

//@} // Arithmetic Operators

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/GMP_Integer_inlines.hh line 1. */
/* GMP_Integer class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/GMP_Integer_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

inline void
neg_assign(GMP_Integer& x) {
  mpz_neg(x.get_mpz_t(), x.get_mpz_t());
}

inline void
neg_assign(GMP_Integer& x, const GMP_Integer& y) {
  mpz_neg(x.get_mpz_t(), y.get_mpz_t());
}

inline void
abs_assign(GMP_Integer& x) {
  mpz_abs(x.get_mpz_t(), x.get_mpz_t());
}

inline void
abs_assign(GMP_Integer& x, const GMP_Integer& y) {
  mpz_abs(x.get_mpz_t(), y.get_mpz_t());
}

inline void
gcd_assign(GMP_Integer& x, const GMP_Integer& y, const GMP_Integer& z) {
  mpz_gcd(x.get_mpz_t(), y.get_mpz_t(), z.get_mpz_t());
}

inline void
rem_assign(GMP_Integer& x, const GMP_Integer& y, const GMP_Integer& z) {
  mpz_tdiv_r(x.get_mpz_t(), y.get_mpz_t(), z.get_mpz_t());
}

inline void
gcdext_assign(GMP_Integer& x, GMP_Integer& s, GMP_Integer& t,
              const GMP_Integer& y, const GMP_Integer& z) {
  mpz_gcdext(x.get_mpz_t(),
             s.get_mpz_t(), t.get_mpz_t(),
             y.get_mpz_t(), z.get_mpz_t());
}

inline void
lcm_assign(GMP_Integer& x, const GMP_Integer& y, const GMP_Integer& z) {
  mpz_lcm(x.get_mpz_t(), y.get_mpz_t(), z.get_mpz_t());
}

inline void
add_mul_assign(GMP_Integer& x, const GMP_Integer& y, const GMP_Integer& z) {
  mpz_addmul(x.get_mpz_t(), y.get_mpz_t(), z.get_mpz_t());
}

inline void
sub_mul_assign(GMP_Integer& x, const GMP_Integer& y, const GMP_Integer& z) {
  mpz_submul(x.get_mpz_t(), y.get_mpz_t(), z.get_mpz_t());
}

inline void
mul_2exp_assign(GMP_Integer& x, const GMP_Integer& y, unsigned int exp) {
  mpz_mul_2exp(x.get_mpz_t(), y.get_mpz_t(), exp);
}

inline void
div_2exp_assign(GMP_Integer& x, const GMP_Integer& y, unsigned int exp) {
  mpz_tdiv_q_2exp(x.get_mpz_t(), y.get_mpz_t(), exp);
}

inline void
exact_div_assign(GMP_Integer& x, const GMP_Integer& y, const GMP_Integer& z) {
  PPL_ASSERT(y % z == 0);
  mpz_divexact(x.get_mpz_t(), y.get_mpz_t(), z.get_mpz_t());
}

inline void
sqrt_assign(GMP_Integer& x, const GMP_Integer& y) {
  mpz_sqrt(x.get_mpz_t(), y.get_mpz_t());
}

inline int
cmp(const GMP_Integer& x, const GMP_Integer& y) {
  return mpz_cmp(x.get_mpz_t(), y.get_mpz_t());
}

inline const mpz_class&
raw_value(const GMP_Integer& x) {
  return x;
}

inline mpz_class&
raw_value(GMP_Integer& x) {
  return x;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/GMP_Integer_defs.hh line 133. */

/* Automatically generated from PPL source file ../src/Coefficient_defs.hh line 36. */
#endif

#include <iosfwd>

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Initializes the Coefficient constants.
#endif
void Coefficient_constants_initialize();

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Finalizes the Coefficient constants.
#endif
void Coefficient_constants_finalize();

//! Returns a const reference to a Coefficient with value 0.
Coefficient_traits::const_reference Coefficient_zero();

//! Returns a const reference to a Coefficient with value 1.
Coefficient_traits::const_reference Coefficient_one();

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Coefficient_inlines.hh line 1. */
/* Coefficient class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

#ifdef PPL_CHECKED_INTEGERS
inline void
Bounded_Integer_Coefficient_Policy::handle_result(Result r) {
  // Note that the input functions can return VC_NAN.
  if (result_overflow(r) || result_class(r) == VC_NAN) {
    throw_result_exception(r);
  }
}
#endif // PPL_CHECKED_INTEGERS


#if defined(PPL_CHECKED_INTEGERS) || defined(PPL_NATIVE_INTEGERS)
inline Coefficient_traits::const_reference
Coefficient_zero() {
  // FIXME: is there a way to avoid this static variable?
  static Coefficient zero(0);
  return zero;
}

inline Coefficient_traits::const_reference
Coefficient_one() {
  // FIXME: is there a way to avoid this static variable?
  static Coefficient one(1);
  return one;
}
#endif // defined(PPL_CHECKED_INTEGERS) || defined(PPL_NATIVE_INTEGERS)

#ifdef PPL_GMP_INTEGERS
inline Coefficient_traits::const_reference
Coefficient_zero() {
  extern const Coefficient* Coefficient_zero_p;
  return *Coefficient_zero_p;
}

inline Coefficient_traits::const_reference
Coefficient_one() {
  extern const Coefficient* Coefficient_one_p;
  PPL_ASSERT(*Coefficient_one_p != 0);
  return *Coefficient_one_p;
}
#endif // PPL_GMP_INTEGERS

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Coefficient_defs.hh line 61. */

/* Automatically generated from PPL source file ../src/Variables_Set_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Variables_Set;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Dense_Row_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Dense_Row;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Sparse_Row_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Sparse_Row;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Expression_Interface_defs.hh line 33. */
#include <vector>
#include <set>
#include <cstddef>

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A linear expression.
/*! \ingroup PPL_CXX_interface
  An object of a class implementing Linear_Expression_Interface
  represents a linear expression
  \f[
    \sum_{i=0}^{n-1} a_i x_i + b
  \f]
  where \f$n\f$ is the dimension of the vector space,
  each \f$a_i\f$ is the integer coefficient
  of the \f$i\f$-th variable \f$x_i\f$
  and \f$b\f$ is the integer for the inhomogeneous term.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
class Parma_Polyhedra_Library::Linear_Expression_Interface {
public:
  virtual ~Linear_Expression_Interface();

  virtual bool OK() const = 0;

  //! Returns the current representation of this linear expression.
  virtual Representation representation() const = 0;

  //! An interface for const iterators on the expression (homogeneous)
  //! coefficients that are nonzero.
  /*!
    These iterators are invalidated by operations that modify the expression.
  */
  class const_iterator_interface {
  public:
    typedef std::bidirectional_iterator_tag iterator_category;
    typedef const Coefficient value_type;
    typedef std::ptrdiff_t difference_type;
    typedef value_type* pointer;
    typedef Coefficient_traits::const_reference reference;

    //! Returns a copy of *this.
    //! This returns a pointer to dynamic-allocated memory. The caller has the
    //! duty to free the memory when it's not needed anymore.
    virtual const_iterator_interface* clone() const = 0;

    virtual ~const_iterator_interface();

    //! Navigates to the next nonzero coefficient.
    //! Note that this method does *not* return a reference, to increase
    //! efficiency since it's virtual.
    virtual void operator++() = 0;

    //! Navigates to the previous nonzero coefficient.
    //! Note that this method does *not* return a reference, to increase
    //! efficiency since it's virtual.
    virtual void operator--() = 0;

    //! Returns the current element.
    virtual reference operator*() const = 0;

    //! Returns the variable of the coefficient pointed to by \c *this.
    /*!
      \returns the variable of the coefficient pointed to by \c *this.
    */
    virtual Variable variable() const = 0;

    //! Compares \p *this with x .
    /*!
      \param x
      The %iterator that will be compared with *this.
    */
    virtual bool operator==(const const_iterator_interface& x) const = 0;
  };

  //! This returns a pointer to dynamic-allocated memory. The caller has the
  //! duty to free the memory when it's not needed anymore.
  virtual const_iterator_interface* begin() const = 0;

  //! This returns a pointer to dynamic-allocated memory. The caller has the
  //! duty to free the memory when it's not needed anymore.
  virtual const_iterator_interface* end() const = 0;

  //! This returns a pointer to dynamic-allocated memory. The caller has the
  //! duty to free the memory when it's not needed anymore.
  //! Returns (a pointer to) an iterator that points to the first nonzero
  //! coefficient of a variable greater than or equal to v, or at end if no
  //! such coefficient exists.
  virtual const_iterator_interface* lower_bound(Variable v) const = 0;

  //! Returns the dimension of the vector space enclosing \p *this.
  virtual dimension_type space_dimension() const = 0;

  //! Sets the dimension of the vector space enclosing \p *this to \p n .
  virtual void set_space_dimension(dimension_type n) = 0;

  //! Returns the coefficient of \p v in \p *this.
  virtual Coefficient_traits::const_reference
  coefficient(Variable v) const = 0;

  //! Sets the coefficient of \p v in \p *this to \p n.
  virtual void
  set_coefficient(Variable v, Coefficient_traits::const_reference n) = 0;

  //! Returns the inhomogeneous term of \p *this.
  virtual Coefficient_traits::const_reference inhomogeneous_term() const = 0;

  //! Sets the inhomogeneous term of \p *this to \p n.
  virtual void
  set_inhomogeneous_term(Coefficient_traits::const_reference n) = 0;

  //! Linearly combines \p *this with \p y so that the coefficient of \p v
  //! is 0.
  /*!
    \param y
    The expression that will be combined with \p *this object;

    \param v
    The variable whose coefficient has to become \f$0\f$.

    Computes a linear combination of \p *this and \p y having
    the coefficient of variable \p v equal to \f$0\f$. Then it assigns
    the resulting expression to \p *this.

    \p *this and \p y must have the same space dimension.
  */
  virtual void
  linear_combine(const Linear_Expression_Interface& y, Variable v) = 0;

  //! Equivalent to <CODE>*this = *this * c1 + y * c2</CODE>, but assumes that
  //! \p *this and \p y have the same space dimension.
  virtual void linear_combine(const Linear_Expression_Interface& y,
                              Coefficient_traits::const_reference c1,
                              Coefficient_traits::const_reference c2) = 0;

  //! Equivalent to <CODE>*this = *this * c1 + y * c2</CODE>.
  //! c1 and c2 may be 0.
  virtual void linear_combine_lax(const Linear_Expression_Interface& y,
                                  Coefficient_traits::const_reference c1,
                                  Coefficient_traits::const_reference c2) = 0;

  //! Swaps the coefficients of the variables \p v1 and \p v2 .
  virtual void swap_space_dimensions(Variable v1, Variable v2) = 0;

  //! Removes all the specified dimensions from the expression.
  /*!
    The space dimension of the variable with the highest space
    dimension in \p vars must be at most the space dimension
    of \p this.
  */
  virtual void remove_space_dimensions(const Variables_Set& vars) = 0;

  //! Shift by \p n positions the coefficients of variables, starting from
  //! the coefficient of \p v. This increases the space dimension by \p n.
  virtual void shift_space_dimensions(Variable v, dimension_type n) = 0;

  //! Permutes the space dimensions of the expression.
  /*!
    \param cycle
    A vector representing a cycle of the permutation according to which the
    space dimensions must be rearranged.

    The \p cycle vector represents a cycle of a permutation of space
    dimensions.
    For example, the permutation
    \f$ \{ x_1 \mapsto x_2, x_2 \mapsto x_3, x_3 \mapsto x_1 \}\f$ can be
    represented by the vector containing \f$ x_1, x_2, x_3 \f$.
  */
  virtual void
  permute_space_dimensions(const std::vector<Variable>& cycle) = 0;

  //! Returns <CODE>true</CODE> if and only if \p *this is \f$0\f$.
  virtual bool is_zero() const = 0;

  /*! \brief
    Returns <CODE>true</CODE> if and only if all the homogeneous
    terms of \p *this are \f$0\f$.
  */
  virtual bool all_homogeneous_terms_are_zero() const = 0;

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  virtual memory_size_type total_memory_in_bytes() const = 0;

  //! Returns the size in bytes of the memory managed by \p *this.
  virtual memory_size_type external_memory_in_bytes() const = 0;

  //! Writes to \p s an ASCII representation of \p *this.
  virtual void ascii_dump(std::ostream& s) const = 0;

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  virtual bool ascii_load(std::istream& s) = 0;

  //! Returns \p true if *this is equal to \p x.
  //! Note that (*this == x) has a completely different meaning.
  virtual bool is_equal_to(const Linear_Expression_Interface& x) const = 0;

  //! Normalizes the modulo of the coefficients and of the inhomogeneous term
  //! so that they are mutually prime.
  /*!
    Computes the Greatest Common Divisor (GCD) among the coefficients
    and the inhomogeneous term and normalizes them by the GCD itself.
  */
  virtual void normalize() = 0;

  //! Ensures that the first nonzero homogeneous coefficient is positive,
  //! by negating the row if necessary.
  virtual void sign_normalize() = 0;

  /*! \brief
    Negates the elements from index \p first (included)
    to index \p last (excluded).
  */
  virtual void negate(dimension_type first, dimension_type last) = 0;

  virtual Linear_Expression_Interface&
  operator+=(Coefficient_traits::const_reference n) = 0;
  virtual Linear_Expression_Interface&
  operator-=(Coefficient_traits::const_reference n) = 0;

  //! The basic comparison function.
  /*! \relates Linear_Expression_Interface

    \returns -1 or -2 if x is less than y, 0 if they are equal and 1 or 2 is y
            is greater. The absolute value of the result is 1 if the difference
            is only in the inhomogeneous terms, 2 otherwise

    The order is a lexicographic. It starts comparing the variables'
    coefficient, starting from Variable(0), and at the end it compares
    the inhomogeneous terms.
  */
  virtual int compare(const Linear_Expression_Interface& y) const = 0;

  virtual Linear_Expression_Interface&
  operator+=(const Linear_Expression_Interface& e2) = 0;
  virtual Linear_Expression_Interface&
  operator+=(const Variable v) = 0;
  virtual Linear_Expression_Interface&
  operator-=(const Linear_Expression_Interface& e2) = 0;
  virtual Linear_Expression_Interface&
  operator-=(const Variable v) = 0;
  virtual Linear_Expression_Interface&
  operator*=(Coefficient_traits::const_reference n) = 0;
  virtual Linear_Expression_Interface&
  operator/=(Coefficient_traits::const_reference n) = 0;

  virtual void negate() = 0;

  virtual Linear_Expression_Interface&
  add_mul_assign(Coefficient_traits::const_reference n, const Variable v) = 0;

  virtual Linear_Expression_Interface&
  sub_mul_assign(Coefficient_traits::const_reference n, const Variable v) = 0;

  virtual void add_mul_assign(Coefficient_traits::const_reference factor,
                              const Linear_Expression_Interface& e2) = 0;

  virtual void sub_mul_assign(Coefficient_traits::const_reference factor,
                              const Linear_Expression_Interface& e2) = 0;

  virtual void print(std::ostream& s) const = 0;

  /*! \brief
    Returns <CODE>true</CODE> if the coefficient of each variable in
    \p vars[i] is \f$0\f$.
  */
  virtual bool all_zeroes(const Variables_Set& vars) const = 0;

  //! Returns true if there is a variable in [first,last) whose coefficient
  //! is nonzero in both *this and x.
  virtual bool have_a_common_variable(const Linear_Expression_Interface& x,
                                      Variable first, Variable last) const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns the i-th coefficient.
  virtual Coefficient_traits::const_reference get(dimension_type i) const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Sets the i-th coefficient to n.
  virtual void set(dimension_type i, Coefficient_traits::const_reference n) = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  /*! \brief
    Returns <CODE>true</CODE> if (*this)[i] is \f$0\f$, for each i in
    [start, end).
  */
  virtual bool all_zeroes(dimension_type start, dimension_type end) const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  /*! \brief
    Returns the number of zero coefficient in [start, end).
  */
  virtual dimension_type
  num_zeroes(dimension_type start, dimension_type end) const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  /*! \brief
    Returns the gcd of the nonzero coefficients in [start,end). If all the
    coefficients in this range are 0 returns 0.
  */
  virtual Coefficient gcd(dimension_type start, dimension_type end) const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  virtual void exact_div_assign(Coefficient_traits::const_reference c,
                                dimension_type start, dimension_type end) = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Equivalent to <CODE>(*this)[i] *= n</CODE>, for each i in [start, end).
  virtual void mul_assign(Coefficient_traits::const_reference n,
                          dimension_type start, dimension_type end) = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Linearly combines \p *this with \p y so that the coefficient of \p v
  //! is 0.
  /*!
    \param y
    The expression that will be combined with \p *this object;

    \param i
    The index of the coefficient that has to become \f$0\f$.

    Computes a linear combination of \p *this and \p y having
    the i-th coefficient equal to \f$0\f$. Then it assigns
    the resulting expression to \p *this.

    \p *this and \p y must have the same space dimension.
  */
  virtual void
  linear_combine(const Linear_Expression_Interface& y, dimension_type i) = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Equivalent to <CODE>(*this)[i] = (*this)[i] * c1 + y[i] * c2</CODE>,
  //! for each i in [start, end).
  virtual void linear_combine(const Linear_Expression_Interface& y,
                              Coefficient_traits::const_reference c1,
                              Coefficient_traits::const_reference c2,
                              dimension_type start, dimension_type end) = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Equivalent to <CODE>(*this)[i] = (*this)[i] * c1 + y[i] * c2</CODE>,
  //! for each i in [start, end). c1 and c2 may be zero.
  virtual void linear_combine_lax(const Linear_Expression_Interface& y,
                                  Coefficient_traits::const_reference c1,
                                  Coefficient_traits::const_reference c2,
                                  dimension_type start, dimension_type end) = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns the index of the last nonzero element, or 0 if there are no
  //! nonzero elements.
  virtual dimension_type last_nonzero() const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns the index of the last nonzero element in [first,last), or last
  //! if there are no nonzero elements.
  virtual dimension_type
  last_nonzero(dimension_type first, dimension_type last) const = 0;

  //! Returns the index of the first nonzero element, or \p last if there are no
  //! nonzero elements, considering only elements in [first,last).
  virtual dimension_type
  first_nonzero(dimension_type first, dimension_type last) const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  /*! \brief
    Returns <CODE>true</CODE> if each coefficient in [start,end) is *not* in
    \f$0\f$, disregarding coefficients of variables in \p vars.
  */
  virtual bool
  all_zeroes_except(const Variables_Set& vars,
                    dimension_type start, dimension_type end) const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Sets results to the sum of (*this)[i]*y[i], for each i in [start,end).
  virtual void
  scalar_product_assign(Coefficient& result,
                        const Linear_Expression_Interface& y,
                        dimension_type start, dimension_type end) const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Computes the sign of the sum of (*this)[i]*y[i],
  //! for each i in [start,end).
  virtual int
  scalar_product_sign(const Linear_Expression_Interface& y,
                      dimension_type start, dimension_type end) const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Removes from the set x all the indexes of nonzero elements of *this.
  virtual void
  has_a_free_dimension_helper(std::set<dimension_type>& x) const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns \p true if (*this)[i] is equal to x[i], for each i in [start,end).
  virtual bool is_equal_to(const Linear_Expression_Interface& x,
                           dimension_type start, dimension_type end) const = 0;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns \p true if (*this)[i]*c1 is equal to x[i]*c2, for each i in
  //! [start,end).
  virtual bool is_equal_to(const Linear_Expression_Interface& x,
                           Coefficient_traits::const_reference c1,
                           Coefficient_traits::const_reference c2,
                           dimension_type start, dimension_type end) const = 0;

  // NOTE: This method is public, but it is not exposed in
  // Linear_Expression, so that it can be used internally in the PPL,
  // by friends of Linear_Expression.
  //! Sets \p r to a copy of the row that implements \p *this.
  virtual void get_row(Dense_Row& r) const = 0;

  // NOTE: This method is public, but it is not exposed in
  // Linear_Expression, so that it can be used internally in the PPL,
  // by friends of Linear_Expression.
  //! Sets \p r to a copy of the row that implements \p *this.
  virtual void get_row(Sparse_Row& r) const = 0;
};

/* Automatically generated from PPL source file ../src/Variables_Set_defs.hh line 1. */
/* Variables_Set class declaration.
*/


/* Automatically generated from PPL source file ../src/Variables_Set_defs.hh line 30. */
#include <iosfwd>
#include <set>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Variables_Set */
std::ostream&
operator<<(std::ostream& s, const Variables_Set& vs);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library

//! An std::set of variables' indexes.
class Parma_Polyhedra_Library::Variables_Set
  : public std::set<dimension_type> {
private:
  typedef std::set<dimension_type> Base;

public:
  //! Builds the empty set of variable indexes.
  Variables_Set();

  //! Builds the singleton set of indexes containing <CODE>v.id()</CODE>;
  explicit Variables_Set(const Variable v);

  /*! \brief
    Builds the set of variables's indexes in the range from
    <CODE>v.id()</CODE> to <CODE>w.id()</CODE>.

    If <CODE>v.id() <= w.id()</CODE>, this constructor builds the
    set of variables' indexes
    <CODE>v.id()</CODE>, <CODE>v.id()+1</CODE>, ..., <CODE>w.id()</CODE>.
    The empty set is built otherwise.
  */
  Variables_Set(const Variable v, const Variable w);

  //! Returns the maximum space dimension a Variables_Set can handle.
  static dimension_type max_space_dimension();

  /*! \brief
    Returns the dimension of the smallest vector space enclosing all
    the variables whose indexes are in the set.
  */
  dimension_type space_dimension() const;

  //! Inserts the index of variable \p v into the set.
  void insert(Variable v);

  // The `insert' method above overloads (instead of hiding) the
  // other `insert' method of std::set.
  using Base::insert;

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS
};

/* Automatically generated from PPL source file ../src/Variables_Set_inlines.hh line 1. */
/* Variables_Set class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Variables_Set_inlines.hh line 28. */
#include <stdexcept>

namespace Parma_Polyhedra_Library {

inline
Variables_Set::Variables_Set()
  : Base() {
}

inline void
Variables_Set::insert(const Variable v) {
  insert(v.id());
}

inline
Variables_Set::Variables_Set(const Variable v)
  : Base() {
  insert(v);
}

inline dimension_type
Variables_Set::max_space_dimension() {
  return Variable::max_space_dimension();
}

inline dimension_type
Variables_Set::space_dimension() const {
  reverse_iterator i = rbegin();
  return (i == rend()) ? 0 : (*i + 1);
}

inline memory_size_type
Variables_Set::external_memory_in_bytes() const {
  // We assume sets are implemented by means of red-black trees that
  // require to store the color (we assume an enum) and three pointers
  // to the parent, left and right child, respectively.
  enum color { red, black };
  return size() * (sizeof(color) + 3*sizeof(void*) + sizeof(dimension_type));
}

inline memory_size_type
Variables_Set::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Variables_Set_defs.hh line 106. */

/* Automatically generated from PPL source file ../src/Dense_Row_defs.hh line 1. */
/* Dense_Row class declaration.
*/


/* Automatically generated from PPL source file ../src/Dense_Row_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/Dense_Row_defs.hh line 30. */

/* Automatically generated from PPL source file ../src/Dense_Row_defs.hh line 33. */
#include <memory>
#include <vector>
#include <limits>
#include <cstddef>

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A finite sequence of coefficients.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
class Parma_Polyhedra_Library::Dense_Row {
public:
  class iterator;
  class const_iterator;

  //! Constructs an empty row.
  Dense_Row();

  explicit Dense_Row(const Sparse_Row& row);

  //! Tight constructor: resizing may require reallocation.
  /*!
    Constructs a row with size and capacity \p sz.
  */
  Dense_Row(dimension_type sz);

  //! Sizing constructor with capacity.
  /*!
    \param sz
    The size of the row that will be constructed;

    \param capacity
    The capacity of the row that will be constructed;

    The row that is constructed has storage for \p capacity elements,
    \p sz of which are default-constructed now.
  */
  Dense_Row(dimension_type sz, dimension_type capacity);

  //! Ordinary copy constructor.
  Dense_Row(const Dense_Row& y);

  //! Copy constructor with specified capacity.
  /*!
    It is assumed that \p capacity is greater than or equal to
    the size of \p y.
  */
  Dense_Row(const Dense_Row& y, dimension_type capacity);

  //! Copy constructor with specified size and capacity.
  /*!
    It is assumed that \p sz is less than or equal to \p capacity.
  */
  Dense_Row(const Dense_Row& y, dimension_type sz, dimension_type capacity);

  //! Copy constructor with specified size and capacity from a Sparse_Row.
  /*!
    It is assumed that \p sz is less than or equal to \p capacity.
  */
  Dense_Row(const Sparse_Row& y, dimension_type sz, dimension_type capacity);

  //! Destructor.
  ~Dense_Row();

  //! Assignment operator.
  Dense_Row& operator=(const Dense_Row& y);

  //! Assignment operator.
  Dense_Row& operator=(const Sparse_Row& y);

  //! Swaps \p *this with \p y.
  void m_swap(Dense_Row& y);

  //! Resizes the row to \p sz.
  void resize(dimension_type sz);

  //! Resizes the row to \p sz, with capacity \p capacity.
  void resize(dimension_type sz, dimension_type capacity);

  //! Resets all the elements of this row.
  void clear();

  //! Adds \p n zeroes before index \p i.
  /*!
    \param n
    The number of zeroes that will be added to the row.

    \param i
    The index of the element before which the zeroes will be added.

    Existing elements with index greater than or equal to \p i are shifted
    to the right by \p n positions. The size is increased by \p n.

    Existing iterators are invalidated.
  */
  void add_zeroes_and_shift(dimension_type n, dimension_type i);

  //! Expands the row to size \p new_size.
  /*!
    Adds new positions to the implementation of the row
    obtaining a new row with size \p new_size.
    It is assumed that \p new_size is between the current size
    and capacity of the row.
  */
  void expand_within_capacity(dimension_type new_size);

  //! Shrinks the row by erasing elements at the end.
  /*!
    Destroys elements of the row implementation
    from position \p new_size to the end.
    It is assumed that \p new_size is not greater than the current size.
  */
  void shrink(dimension_type new_size);

  //! Returns the size() of the largest possible Dense_Row.
  static dimension_type max_size();

  //! Gives the number of coefficients currently in use.
  dimension_type size() const;

  //! \name Subscript operators
  //@{
  //! Returns a reference to the element of the row indexed by \p k.
  Coefficient& operator[](dimension_type k);

  //! Returns a constant reference to the element of the row indexed by \p k.
  Coefficient_traits::const_reference operator[](dimension_type k) const;
  //@} // Subscript operators

  //! Normalizes the modulo of coefficients so that they are mutually prime.
  /*!
    Computes the Greatest Common Divisor (GCD) among the elements of
    the row and normalizes them by the GCD itself.
  */
  void normalize();

  //! Swaps the i-th element with the j-th element.
  //! Provided for compatibility with Sparse_Row
  void swap_coefficients(dimension_type i, dimension_type j);

  //! Swaps the element pointed to by i with the element pointed to by j.
  //! Provided for compatibility with Sparse_Row
  void swap_coefficients(iterator i, iterator j);

  iterator begin();
  const_iterator begin() const;

  iterator end();
  const_iterator end() const;

  //! Resets the i-th element to 0.
  //! Provided for compatibility with Sparse_Row
  void reset(dimension_type i);

  //! Resets the elements [first,last) to 0.
  //! Provided for compatibility with Sparse_Row
  void reset(dimension_type first, dimension_type last);

  //! Resets the element pointed to by itr to 0.
  //! Provided for compatibility with Sparse_Row.
  iterator reset(iterator itr);

  //! Gets the i-th element.
  //! Provided for compatibility with Sparse_Row.
  Coefficient_traits::const_reference get(dimension_type i) const;

  //! Provided for compatibility with Sparse_Row.
  iterator find(dimension_type i);

  //! Provided for compatibility with Sparse_Row.
  const_iterator find(dimension_type i) const;

  //! Provided for compatibility with Sparse_Row.
  iterator find(iterator itr, dimension_type i);

  //! Provided for compatibility with Sparse_Row.
  const_iterator find(const_iterator itr, dimension_type i) const;

  //! Provided for compatibility with Sparse_Row.
  iterator lower_bound(dimension_type i);

  //! Provided for compatibility with Sparse_Row.
  const_iterator lower_bound(dimension_type i) const;

  //! Provided for compatibility with Sparse_Row.
  iterator lower_bound(iterator itr, dimension_type i);

  //! Provided for compatibility with Sparse_Row.
  const_iterator lower_bound(const_iterator itr, dimension_type i) const;

  //! Provided for compatibility with Sparse_Row.
  iterator insert(dimension_type i, Coefficient_traits::const_reference x);

  //! Provided for compatibility with Sparse_Row.
  iterator insert(dimension_type i);

  //! Provided for compatibility with Sparse_Row.
  iterator insert(iterator itr, dimension_type i,
                       Coefficient_traits::const_reference x);

  //! Provided for compatibility with Sparse_Row.
  iterator insert(iterator itr, dimension_type i);

  //! Calls g(x[i],y[i]), for each i.
  /*!
    \param y
    The row that will be combined with *this.

    \param f
    A functor that should take a Coefficient&.
    f(c1) must be equivalent to g(c1, 0).

    \param g
    A functor that should take a Coefficient& and a
    Coefficient_traits::const_reference.
    g(c1, c2) must do nothing when c1 is zero.

    This method takes \f$O(n)\f$ time.

    \note
    The functors will only be called when necessary, assuming the requested
    properties hold.

    \see combine_needs_second
    \see combine
  */
  template <typename Func1, typename Func2>
  void combine_needs_first(const Dense_Row& y,
                           const Func1& f, const Func2& g);

  //! Calls g(x[i],y[i]), for each i.
  /*!
    \param y
    The row that will be combined with *this.

    \param g
    A functor that should take a Coefficient& and a
    Coefficient_traits::const_reference.
    g(c1, 0) must do nothing, for every c1.

    \param h
    A functor that should take a Coefficient& and a
    Coefficient_traits::const_reference.
    h(c1, c2) must be equivalent to g(c1, c2) when c1 is zero.

    This method takes \f$O(n)\f$ time.

    \note
    The functors will only be called when necessary, assuming the requested
    properties hold.

    \see combine_needs_first
    \see combine
  */
  template <typename Func1, typename Func2>
  void combine_needs_second(const Dense_Row& y,
                            const Func1& g, const Func2& h);

  //! Calls g(x[i],y[i]), for each i.
  /*!
    \param y
    The row that will be combined with *this.

    \param f
    A functor that should take a Coefficient&.
    f(c1) must be equivalent to g(c1, 0).

    \param g
    A functor that should take a Coefficient& and a
    Coefficient_traits::const_reference.
    g(c1, c2) must do nothing when both c1 and c2 are zero.

    \param h
    A functor that should take a Coefficient& and a
    Coefficient_traits::const_reference.
    h(c1, c2) must be equivalent to g(c1, c2) when c1 is zero.

    This method takes \f$O(n)\f$ time.

    \note
    The functors will only be called when necessary, assuming the requested
    properties hold.

    \see combine_needs_first
    \see combine_needs_second
  */
  template <typename Func1, typename Func2, typename Func3>
  void combine(const Dense_Row& y,
               const Func1& f, const Func2& g, const Func3& h);

  //! Executes <CODE>(*this)[i] = (*this)[i]*coeff1 + y[i]*coeff2</CODE>, for
  //! each i.
  /*!
    \param y
    The row that will be combined with *this.

    \param coeff1
    The coefficient used for elements of *this.
    It must not be 0.

    \param coeff2
    The coefficient used for elements of y.
    It must not be 0.

    This method takes \f$O(n)\f$ time.

    \see combine_needs_first
    \see combine_needs_second
    \see combine
  */
  void linear_combine(const Dense_Row& y,
                      Coefficient_traits::const_reference coeff1,
                      Coefficient_traits::const_reference coeff2);

  //! Equivalent to <CODE>(*this)[i] = (*this)[i] * c1 + y[i] * c2</CODE>,
  //! for each i in [start, end).
  /*!
    This method detects when coeff1==1 and/or coeff2==1 or coeff2==-1 in
    order to save some work.

    coeff1 and coeff2 must not be 0.
  */
  void linear_combine(const Dense_Row& y,
                      Coefficient_traits::const_reference c1,
                      Coefficient_traits::const_reference c2,
                      dimension_type start, dimension_type end);

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  /*! \brief
    Returns a lower bound to the size in bytes of the memory
    managed by \p *this.
  */
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns the total size in bytes of the memory occupied by \p *this,
    provided the capacity of \p *this is given by \p capacity.
  */
  memory_size_type total_memory_in_bytes(dimension_type capacity) const;

  /*! \brief
    Returns the size in bytes of the memory managed by \p *this,
    provided the capacity of \p *this is given by \p capacity.
  */
  memory_size_type external_memory_in_bytes(dimension_type capacity) const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  /*! \brief
    Checks if all the invariants are satisfied and that the actual
    size matches the value provided as argument.
  */
  bool OK(dimension_type row_size) const;

private:
  void init(const Sparse_Row& row);

  void destroy();

  struct Impl {

    Impl();

    ~Impl();

    //! The number of coefficients in the row.
    dimension_type size;

    //! The capacity of the row.
    dimension_type capacity;

    //! The allocator used to allocate/deallocate vec.
    std::allocator<Coefficient> coeff_allocator;

    //! The vector of coefficients.
    //! An empty vector may be stored as NULL instead of using a valid pointer.
    Coefficient* vec;
  };

  Impl impl;

  //! Returns the capacity of the row.
  dimension_type capacity() const;
};

class Parma_Polyhedra_Library::Dense_Row::iterator {
public:

  typedef std::bidirectional_iterator_tag iterator_category;
  typedef Coefficient value_type;
  typedef std::ptrdiff_t difference_type;
  typedef value_type* pointer;
  typedef value_type& reference;

  iterator();
  iterator(Dense_Row& r, dimension_type i);

  Coefficient& operator*();
  Coefficient_traits::const_reference operator*() const;

  //! Returns the index of the element pointed to by \c *this.
  /*!
    If itr is a valid iterator for row, <CODE>row[itr.index()]</CODE> is
    equivalent to *itr.

    \returns the index of the element pointed to by \c *this.
  */
  dimension_type index() const;

  iterator& operator++();
  iterator operator++(int);

  iterator& operator--();
  iterator operator--(int);

  bool operator==(const iterator& x) const;
  bool operator!=(const iterator& x) const;

  operator const_iterator() const;

  bool OK() const;

private:
  Dense_Row* row;
  dimension_type idx;
};

class Parma_Polyhedra_Library::Dense_Row::const_iterator {
public:

  typedef const Coefficient value_type;
  typedef std::ptrdiff_t difference_type;
  typedef value_type* pointer;
  typedef Coefficient_traits::const_reference reference;

  const_iterator();
  const_iterator(const Dense_Row& r, dimension_type i);

  Coefficient_traits::const_reference operator*() const;

  //! Returns the index of the element pointed to by \c *this.
  /*!
    If itr is a valid iterator for row, <CODE>row[itr.index()]</CODE> is
    equivalent to *itr.

    \returns the index of the element pointed to by \c *this.
  */
  dimension_type index() const;

  const_iterator& operator++();
  const_iterator operator++(int);

  const_iterator& operator--();
  const_iterator operator--(int);

  bool operator==(const const_iterator& x) const;
  bool operator!=(const const_iterator& x) const;

  bool OK() const;

private:
  const Dense_Row* row;
  dimension_type idx;
};


namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
/*! \relates Dense_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void swap(Dense_Row& x, Dense_Row& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps objects referred by \p x and \p y.
/*! \relates Dense_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void iter_swap(std::vector<Dense_Row>::iterator x,
               std::vector<Dense_Row>::iterator y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are equal.
/*! \relates Dense_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator==(const Dense_Row& x, const Dense_Row& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are different.
/*! \relates Dense_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator!=(const Dense_Row& x, const Dense_Row& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Dense_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void linear_combine(Dense_Row& x, const Dense_Row& y,
                    Coefficient_traits::const_reference coeff1,
                    Coefficient_traits::const_reference coeff2);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Equivalent to <CODE>x[i] = x[i] * c1 + y[i] * c2</CODE>,
//! for each i in [start, end).
/*! \relates Dense_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void linear_combine(Dense_Row& x, const Dense_Row& y,
                    Coefficient_traits::const_reference c1,
                    Coefficient_traits::const_reference c2,
                    dimension_type start, dimension_type end);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Dense_Row_inlines.hh line 1. */
/* Dense_Row class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Dense_Row_inlines.hh line 28. */
#include <cstddef>
#include <limits>
#include <algorithm>

namespace Parma_Polyhedra_Library {

inline
Dense_Row::Impl::Impl()
  : size(0), capacity(0), coeff_allocator(), vec(0) {
}

inline
Dense_Row::Impl::~Impl() {
  while (size != 0) {
    --size;
    vec[size].~Coefficient();
  }
  coeff_allocator.deallocate(vec, capacity);
}

inline dimension_type
Dense_Row::max_size() {
  return std::numeric_limits<size_t>::max() / sizeof(Coefficient);
}

inline dimension_type
Dense_Row::size() const {
  return impl.size;
}

inline dimension_type
Dense_Row::capacity() const {
  return impl.capacity;
}

inline
Dense_Row::Dense_Row()
  : impl() {

  PPL_ASSERT(OK());
}

inline
Dense_Row::Dense_Row(const dimension_type sz,
                     const dimension_type capacity)
  : impl() {

  resize(sz, capacity);

  PPL_ASSERT(size() == sz);
  PPL_ASSERT(impl.capacity == capacity);
  PPL_ASSERT(OK());
}

inline
Dense_Row::Dense_Row(const dimension_type sz)
  : impl() {

  resize(sz);

  PPL_ASSERT(size() == sz);
  PPL_ASSERT(OK());
}

inline
Dense_Row::Dense_Row(const Dense_Row& y)
  : impl() {
  impl.coeff_allocator = y.impl.coeff_allocator;
  if (y.impl.vec != 0) {
    impl.capacity = y.capacity();
    impl.vec = impl.coeff_allocator.allocate(impl.capacity);
    while (impl.size != y.size()) {
      new(&impl.vec[impl.size]) Coefficient(y[impl.size]);
      ++impl.size;
    }
  }
  PPL_ASSERT(size() == y.size());
  PPL_ASSERT(capacity() == y.capacity());
  PPL_ASSERT(OK());
}

inline
Dense_Row::Dense_Row(const Dense_Row& y,
                     const dimension_type capacity)
  : impl() {
  PPL_ASSERT(y.size() <= capacity);
  PPL_ASSERT(capacity <= max_size());

  impl.capacity = capacity;
  impl.coeff_allocator = y.impl.coeff_allocator;
  impl.vec = impl.coeff_allocator.allocate(impl.capacity);

  if (y.impl.vec != 0) {
    while (impl.size != y.size()) {
      new(&impl.vec[impl.size]) Coefficient(y[impl.size]);
      ++impl.size;
    }
  }

  PPL_ASSERT(size() == y.size());
  PPL_ASSERT(impl.capacity == capacity);
  PPL_ASSERT(OK());
}

inline
Dense_Row::Dense_Row(const Dense_Row& y,
                     const dimension_type sz,
                     const dimension_type capacity)
  : impl() {
  PPL_ASSERT(sz <= capacity);
  PPL_ASSERT(capacity <= max_size());
  PPL_ASSERT(capacity != 0);

  impl.capacity = capacity;
  impl.coeff_allocator = y.impl.coeff_allocator;
  impl.vec = impl.coeff_allocator.allocate(impl.capacity);

  const dimension_type n = std::min(sz, y.size());
  while (impl.size != n) {
    new(&impl.vec[impl.size]) Coefficient(y[impl.size]);
    ++impl.size;
  }
  while (impl.size != sz) {
    new(&impl.vec[impl.size]) Coefficient();
    ++impl.size;
  }

  PPL_ASSERT(size() == sz);
  PPL_ASSERT(impl.capacity == capacity);
  PPL_ASSERT(OK());
}

inline
Dense_Row::~Dense_Row() {
  // The `impl' field will be destroyed automatically.
}

inline void
Dense_Row::destroy() {
  resize(0);
  impl.coeff_allocator.deallocate(impl.vec, impl.capacity);
}

inline void
Dense_Row::m_swap(Dense_Row& y) {
  using std::swap;
  swap(impl.size, y.impl.size);
  swap(impl.capacity, y.impl.capacity);
  swap(impl.coeff_allocator, y.impl.coeff_allocator);
  swap(impl.vec, y.impl.vec);
  PPL_ASSERT(OK());
  PPL_ASSERT(y.OK());
}

inline Dense_Row&
Dense_Row::operator=(const Dense_Row& y) {

  if (this != &y && size() == y.size()) {
    // Avoid reallocation.

    for (dimension_type i = size(); i-- > 0; ) {
      (*this)[i] = y[i];
    }

    return *this;
  }

  Dense_Row x(y);
  swap(*this, x);

  return *this;
}

inline Coefficient&
Dense_Row::operator[](const dimension_type k) {
  PPL_ASSERT(impl.vec != 0);
  PPL_ASSERT(k < size());
  return impl.vec[k];
}

inline Coefficient_traits::const_reference
Dense_Row::operator[](const dimension_type k) const {
  PPL_ASSERT(impl.vec != 0);
  PPL_ASSERT(k < size());
  return impl.vec[k];
}

inline void
Dense_Row::swap_coefficients(dimension_type i, dimension_type j) {
  std::swap((*this)[i], (*this)[j]);
}

inline void
Dense_Row::swap_coefficients(iterator i, iterator j) {
  std::swap(*i, *j);
}

inline void
Dense_Row::reset(dimension_type i) {
  (*this)[i] = 0;
}

inline Dense_Row::iterator
Dense_Row::reset(iterator itr) {
  *itr = 0;
  ++itr;
  return itr;
}

inline Dense_Row::iterator
Dense_Row::begin() {
  return iterator(*this, 0);
}

inline Dense_Row::const_iterator
Dense_Row::begin() const {
  return const_iterator(*this, 0);
}

inline Dense_Row::iterator
Dense_Row::end() {
  return iterator(*this, size());
}

inline Dense_Row::const_iterator
Dense_Row::end() const {
  return const_iterator(*this, size());
}

inline Coefficient_traits::const_reference
Dense_Row::get(dimension_type i) const {
  return (*this)[i];
}

inline Dense_Row::iterator
Dense_Row::find(dimension_type i) {
  return iterator(*this, i);
}

inline Dense_Row::const_iterator
Dense_Row::find(dimension_type i) const {
  return const_iterator(*this, i);
}

inline Dense_Row::iterator
Dense_Row::find(iterator itr, dimension_type i) {
  (void)itr;
  return iterator(*this, i);
}

inline Dense_Row::const_iterator
Dense_Row::find(const_iterator itr, dimension_type i) const {
  (void)itr;
  return const_iterator(*this, i);
}

inline Dense_Row::iterator
Dense_Row::lower_bound(dimension_type i) {
  return find(i);
}

inline Dense_Row::const_iterator
Dense_Row::lower_bound(dimension_type i) const {
  return find(i);
}

inline Dense_Row::iterator
Dense_Row::lower_bound(iterator itr, dimension_type i) {
  return find(itr, i);
}

inline Dense_Row::const_iterator
Dense_Row::lower_bound(const_iterator itr, dimension_type i) const {
  return find(itr, i);
}

inline Dense_Row::iterator
Dense_Row::insert(dimension_type i,
                  Coefficient_traits::const_reference x) {
  (*this)[i] = x;
  return find(i);
}

inline Dense_Row::iterator
Dense_Row::insert(dimension_type i) {
  return find(i);
}

inline Dense_Row::iterator
Dense_Row::insert(iterator itr, dimension_type i,
                  Coefficient_traits::const_reference x) {
  (void)itr;
  (*this)[i] = x;
  return find(i);
}

inline Dense_Row::iterator
Dense_Row::insert(iterator itr, dimension_type i) {
  (void)itr;
  return find(i);
}

inline memory_size_type
Dense_Row::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

inline memory_size_type
Dense_Row::total_memory_in_bytes(dimension_type capacity) const {
  return sizeof(*this) + external_memory_in_bytes(capacity);
}

/*! \relates Dense_Row */
inline bool
operator!=(const Dense_Row& x, const Dense_Row& y) {
  return !(x == y);
}


inline
Dense_Row::iterator::iterator()
  : row(NULL), idx(0) {
  PPL_ASSERT(OK());
}

inline
Dense_Row::iterator::iterator(Dense_Row& r, dimension_type i)
  : row(&r), idx(i) {
  PPL_ASSERT(OK());
}

inline Coefficient&
Dense_Row::iterator::operator*() {
  PPL_ASSERT(idx < row->size());
  return (*row)[idx];
}

inline Coefficient_traits::const_reference
Dense_Row::iterator::operator*() const {
  PPL_ASSERT(idx < row->size());
  return (*row)[idx];
}

inline dimension_type
Dense_Row::iterator::index() const {
  return idx;
}

inline Dense_Row::iterator&
Dense_Row::iterator::operator++() {
  PPL_ASSERT(idx < row->size());
  ++idx;
  PPL_ASSERT(OK());
  return *this;
}

inline Dense_Row::iterator
Dense_Row::iterator::operator++(int) {
  iterator tmp(*this);
  ++(*this);
  return tmp;
}

inline Dense_Row::iterator&
Dense_Row::iterator::operator--() {
  PPL_ASSERT(idx > 0);
  --idx;
  PPL_ASSERT(OK());
  return *this;
}

inline Dense_Row::iterator
Dense_Row::iterator::operator--(int) {
  iterator tmp(*this);
  --(*this);
  return tmp;
}

inline bool
Dense_Row::iterator::operator==(const iterator& x) const {
  return (row == x.row) && (idx == x.idx);
}

inline bool
Dense_Row::iterator::operator!=(const iterator& x) const {
  return !(*this == x);
}

inline
Dense_Row::iterator::operator const_iterator() const {
  return const_iterator(*row, idx);
}

inline bool
Dense_Row::iterator::OK() const {
  if (row == NULL) {
    return true;
  }
  // i can be equal to row.size() for past-the-end iterators
  return (idx <= row->size());
}


inline
Dense_Row::const_iterator::const_iterator()
  : row(NULL), idx(0) {
  PPL_ASSERT(OK());
}

inline
Dense_Row::const_iterator::const_iterator(const Dense_Row& r,
                                          dimension_type i)
  : row(&r), idx(i) {
  PPL_ASSERT(OK());
}

inline Coefficient_traits::const_reference
Dense_Row::const_iterator::operator*() const {
  PPL_ASSERT(idx < row->size());
  return (*row)[idx];
}

inline dimension_type
Dense_Row::const_iterator::index() const {
  return idx;
}

inline Dense_Row::const_iterator&
Dense_Row::const_iterator::operator++() {
  PPL_ASSERT(idx < row->size());
  ++idx;
  PPL_ASSERT(OK());
  return *this;
}

inline Dense_Row::const_iterator
Dense_Row::const_iterator::operator++(int) {
  const_iterator tmp(*this);
  ++(*this);
  return tmp;
}

inline Dense_Row::const_iterator&
Dense_Row::const_iterator::operator--() {
  PPL_ASSERT(idx > 0);
  --idx;
  PPL_ASSERT(OK());
  return *this;
}

inline Dense_Row::const_iterator
Dense_Row::const_iterator::operator--(int) {
  const_iterator tmp(*this);
  --(*this);
  return tmp;
}

inline bool
Dense_Row::const_iterator::operator==(const const_iterator& x) const {
  return (row == x.row) && (idx == x.idx);
}

inline bool
Dense_Row::const_iterator::operator!=(const const_iterator& x) const {
  return !(*this == x);
}

inline bool
Dense_Row::const_iterator::OK() const {
  if (row == NULL) {
    return true;
  }
  // i can be equal to row.size() for past-the-end iterators
  return (idx <= row->size());
}

inline void
linear_combine(Dense_Row& x, const Dense_Row& y,
               Coefficient_traits::const_reference coeff1,
               Coefficient_traits::const_reference coeff2) {
  x.linear_combine(y, coeff1, coeff2);
}

inline void
linear_combine(Dense_Row& x, const Dense_Row& y,
               Coefficient_traits::const_reference c1,
               Coefficient_traits::const_reference c2,
               dimension_type start, dimension_type end) {
  x.linear_combine(y, c1, c2, start, end);
}

/*! \relates Dense_Row */
inline void
swap(Dense_Row& x, Dense_Row& y) {
  x.m_swap(y);
}

/*! \relates Dense_Row */
inline void
iter_swap(std::vector<Dense_Row>::iterator x,
          std::vector<Dense_Row>::iterator y) {
  swap(*x, *y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Dense_Row_templates.hh line 1. */
/* Dense_Row class implementation: non-inline template functions.
*/


namespace Parma_Polyhedra_Library {


template <typename Func1, typename Func2>
void
Dense_Row::combine_needs_first(const Dense_Row& y, const Func1& /* f */,
                               const Func2& g) {
  for (dimension_type i = size(); i-- > 0; ) {
    g((*this)[i], y[i]);
  }
}

template <typename Func1, typename Func2>
void
Dense_Row::combine_needs_second(const Dense_Row& y, const Func1& g,
                                const Func2& /* h */) {
  for (dimension_type i = size(); i-- > 0; ) {
    g((*this)[i], y[i]);
  }
}

template <typename Func1, typename Func2, typename Func3>
void
Dense_Row::combine(const Dense_Row& y, const Func1& /* f */, const Func2& g,
                   const Func3& /* h */) {
  for (dimension_type i = size(); i-- > 0; ) {
    g((*this)[i], y[i]);
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Dense_Row_defs.hh line 561. */

/* Automatically generated from PPL source file ../src/Sparse_Row_defs.hh line 1. */
/* Sparse_Row class declaration.
*/


/* Automatically generated from PPL source file ../src/Sparse_Row_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/CO_Tree_defs.hh line 1. */
/* CO_Tree class declaration.
*/


/* Automatically generated from PPL source file ../src/CO_Tree_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class CO_Tree;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/CO_Tree_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/CO_Tree_defs.hh line 30. */
#include <memory>
#include <cstddef>

#ifndef PPL_CO_TREE_EXTRA_DEBUG
#ifdef PPL_ABI_BREAKING_EXTRA_DEBUG
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*!
  \brief
  Enables extra debugging information for class CO_Tree.

  \ingroup PPL_CXX_interface
  When <CODE>PPL_CO_TREE_EXTRA_DEBUG</CODE> evaluates to <CODE>true</CODE>,
  each CO_Tree iterator and const_iterator carries a pointer to the associated
  tree; this enables extra consistency checks to be performed.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
#define PPL_CO_TREE_EXTRA_DEBUG 1
#else // !defined(PPL_ABI_BREAKING_EXTRA_DEBUG)
#define PPL_CO_TREE_EXTRA_DEBUG 0
#endif // !defined(PPL_ABI_BREAKING_EXTRA_DEBUG)
#endif // !defined(PPL_CO_TREE_EXTRA_DEBUG)


namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A cache-oblivious binary search tree of pairs.
/*! \ingroup PPL_CXX_interface
  This class implements a binary search tree with keys of dimension_type type
  and data of Coefficient type, laid out in a dynamically-sized array.

  The array-based layout saves calls to new/delete (to insert \f$n\f$ elements
  only \f$O(\log n)\f$ allocations are performed) and, more importantly, is
  much more cache-friendly than a standard (pointer-based) tree, because the
  elements are stored sequentially in memory (leaving some holes to allow
  fast insertion of new elements).
  The downside of this representation is that all iterators are invalidated
  when an element is added or removed, because the array could have been
  enlarged or shrunk. This is partially addressed by providing references to
  internal end iterators that are updated when needed.

  B-trees are cache-friendly too, but the cache size is fixed (usually at
  compile-time). This raises two problems: firstly the cache size must be
  known in advance and those data structures do not perform well with other
  cache sizes and, secondly, even if the cache size is known, the
  optimizations target only one level of cache. This kind of data structures
  are called cache aware. This implementation, instead, is cache oblivious:
  it performs well with every cache size, and thus exploits all of the
  available caches.

  Assuming \p n is the number of elements in the tree and \p B is the number
  of (dimension_type, Coefficient) pairs that fit in a cache line, the
  time and cache misses complexities are the following:

  - Insertions/Queries/Deletions: \f$O(\log^2 n)\f$ time,
                                  \f$O(\log \frac{n}{B}))\f$ cache misses.
  - Tree traversal from begin() to end(), using an %iterator: \f$O(n)\f$ time,
         \f$O(\frac{n}{B})\f$  cache misses.
  - Queries with a hint: \f$O(\log k)\f$ time and \f$O(\log \frac{k}{B})\f$
    cache misses, where k is the distance between the given %iterator and the
    searched element (or the position where it would have been).

  The binary search tree is embedded in a (slightly bigger) complete tree,
  that is enlarged and shrunk when needed. The complete tree is laid out
  in an in-order DFS layout in two arrays: one for the keys and one for the
  associated data.
  The indexes and values are stored in different arrays to reduce
  cache-misses during key queries.

  The tree can store up to \f$(-(dimension_type)1)/100\f$ elements.
  This limit allows faster density computations, but can be removed if needed.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
class CO_Tree {

public:
  class const_iterator;
  class iterator;

private:
  //! This is used for node heights and depths in the tree.
  typedef unsigned height_t;

  PPL_COMPILE_TIME_CHECK(C_Integer<height_t>::max
                         >= sizeof_to_bits(sizeof(dimension_type)),
                         "height_t is too small to store depths.");

  class tree_iterator;

  // This must be declared here, because it is a friend of const_iterator.
  //! Returns the index of the current element in the DFS layout of the
  //! complete tree.
  /*!
    \return the index of the current element in the DFS layout of the complete
            tree.

    \param itr the iterator that points to the desired element.
  */
  dimension_type dfs_index(const_iterator itr) const;

  // This must be declared here, because it is a friend of iterator.
  //! Returns the index of the current element in the DFS layout of the
  //! complete tree.
  /*!
    \return the index of the current element in the DFS layout of the complete
            tree.

    \param itr the iterator that points to the desired element.
  */
  dimension_type dfs_index(iterator itr) const;

public:

  //! The type of the data elements associated with keys.
  /*!
    If this is changed, occurrences of Coefficient_zero() in the CO_Tree
    implementation have to be replaced with constants of the correct type.
  */
  typedef Coefficient data_type;
  typedef Coefficient_traits::const_reference data_type_const_reference;

  //! A const %iterator on the tree elements, ordered by key.
  /*!
    Iterator increment and decrement operations are \f$O(1)\f$ time.
    These iterators are invalidated by operations that add or remove elements
    from the tree.
  */
  class const_iterator {
  private:
  public:

    typedef std::bidirectional_iterator_tag iterator_category;
    typedef const data_type value_type;
    typedef std::ptrdiff_t difference_type;
    typedef value_type* pointer;
    typedef data_type_const_reference reference;

    //! Constructs an invalid const_iterator.
    /*!
      This constructor takes \f$O(1)\f$ time.
    */
    explicit const_iterator();

    //! Constructs an %iterator pointing to the first element of the tree.
    /*!
      \param tree
      The tree that the new %iterator will point to.

      This constructor takes \f$O(1)\f$ time.
    */
    explicit const_iterator(const CO_Tree& tree);

    //! Constructs a const_iterator pointing to the i-th node of the tree.
    /*!
      \param tree
      The tree that the new %iterator will point to.

      \param i
      The index of the element in \p tree to which the %iterator will point
      to.

      The i-th node must be a node with a value or end().

      This constructor takes \f$O(1)\f$ time.
    */
    const_iterator(const CO_Tree& tree, dimension_type i);

    //! The copy constructor.
    /*!
      \param itr
      The %iterator that will be copied.

      This constructor takes \f$O(1)\f$ time.
    */
    const_iterator(const const_iterator& itr);

    //! Converts an iterator into a const_iterator.
    /*!
      \param itr
      The iterator that will be converted into a const_iterator.

      This constructor takes \f$O(1)\f$ time.
    */
    const_iterator(const iterator& itr);

    //! Swaps itr with *this.
    /*!
      \param itr
      The %iterator that will be swapped with *this.

      This method takes \f$O(1)\f$ time.
    */
    void m_swap(const_iterator& itr);

    //! Assigns \p itr to *this .
    /*!
      \param itr
      The %iterator that will be assigned into *this.

      This method takes \f$O(1)\f$ time.
    */
    const_iterator& operator=(const const_iterator& itr);

    //! Assigns \p itr to *this .
    /*!
      \param itr
      The %iterator that will be assigned into *this.

      This method takes \f$O(1)\f$ time.
    */
    const_iterator& operator=(const iterator& itr);

    //! Navigates to the next element.
    /*!
      This method takes \f$O(1)\f$ time.
    */
    const_iterator& operator++();

    //! Navigates to the previous element.
    /*!
      This method takes \f$O(1)\f$ time.
    */
    const_iterator& operator--();

    //! Navigates to the next element.
    /*!
      This method takes \f$O(1)\f$ time.
    */
    const_iterator operator++(int);

    //! Navigates to the previous element.
    /*!
      This method takes \f$O(1)\f$ time.
    */
    const_iterator operator--(int);

    //! Returns the current element.
    data_type_const_reference operator*() const;

    //! Returns the index of the element pointed to by \c *this.
    /*!
      \returns the index of the element pointed to by \c *this.
    */
    dimension_type index() const;

    //! Compares \p *this with x .
    /*!
      \param x
      The %iterator that will be compared with *this.
    */
    bool operator==(const const_iterator& x) const;

    //! Compares \p *this with x .
    /*!
      \param x
      The %iterator that will be compared with *this.
    */
    bool operator!=(const const_iterator& x) const;

  private:
    //! Checks the internal invariants, in debug mode only.
    bool OK() const;

    //! A pointer to the corresponding element of the tree's indexes[] array.
    const dimension_type* current_index;

    //! A pointer to the corresponding element of the tree's data[] array.
    const data_type* current_data;

#if PPL_CO_TREE_EXTRA_DEBUG
    //! A pointer to the corresponding tree, used for debug purposes only.
    const CO_Tree* tree;
#endif

    friend dimension_type CO_Tree::dfs_index(const_iterator itr) const;
  };

  //! An %iterator on the tree elements, ordered by key.
  /*!
    Iterator increment and decrement operations are \f$O(1)\f$ time.
    These iterators are invalidated by operations that add or remove elements
    from the tree.
  */
  class iterator {
  public:

    typedef std::bidirectional_iterator_tag iterator_category;
    typedef data_type value_type;
    typedef std::ptrdiff_t difference_type;
    typedef value_type* pointer;
    typedef value_type& reference;

    //! Constructs an invalid iterator.
    /*!
      This constructor takes \f$O(1)\f$ time.
    */
    iterator();

    //! Constructs an %iterator pointing to first element of the tree.
    /*!
      \param tree
      The tree to which the new %iterator will point to.

      This constructor takes \f$O(1)\f$ time.
    */
    explicit iterator(CO_Tree& tree);

    //! Constructs an %iterator pointing to the i-th node.
    /*!
      \param tree
      The tree to which the new %iterator will point to.

      \param i
      The index of the element in \p tree to which the new %iterator will
      point to.

      The i-th node must be a node with a value or end().

      This constructor takes \f$O(1)\f$ time.
    */
    iterator(CO_Tree& tree, dimension_type i);

    //! The constructor from a tree_iterator.
    /*!
      \param itr
      The tree_iterator that will be converted into an iterator.

      This is meant for use by CO_Tree only.
      This is not private to avoid the friend declaration.

      This constructor takes \f$O(1)\f$ time.
    */
    explicit iterator(const tree_iterator& itr);

    //! The copy constructor.
    /*!
      \param itr
      The %iterator that will be copied.

      This constructor takes \f$O(1)\f$ time.
    */
    iterator(const iterator& itr);

    //! Swaps itr with *this.
    /*!
      \param itr
      The %iterator that will be swapped with *this.

      This method takes \f$O(1)\f$ time.
    */
    void m_swap(iterator& itr);

    //! Assigns \p itr to *this .
    /*!
      \param itr
      The %iterator that will be assigned into *this.

      This method takes \f$O(1)\f$ time.
    */
    iterator& operator=(const iterator& itr);

    //! Assigns \p itr to *this .
    /*!
      \param itr
      The %iterator that will be assigned into *this.

      This method takes \f$O(1)\f$ time.
    */
    iterator& operator=(const tree_iterator& itr);

    //! Navigates to the next element in the tree.
    /*!
      This method takes \f$O(1)\f$ time.
    */
    iterator& operator++();

    //! Navigates to the previous element in the tree.
    /*!
      This method takes \f$O(1)\f$ time.
    */
    iterator& operator--();

    //! Navigates to the next element in the tree.
    /*!
      This method takes \f$O(1)\f$ time.
    */
    iterator operator++(int);

    //! Navigates to the previous element in the tree.
    /*!
      This method takes \f$O(1)\f$ time.
    */
    iterator operator--(int);

    //! Returns the current element.
    data_type& operator*();

    //! Returns the current element.
    data_type_const_reference operator*() const;

    //! Returns the index of the element pointed to by \c *this.
    /*!
      \returns the index of the element pointed to by \c *this.
    */
    dimension_type index() const;

    //! Compares \p *this with x .
    /*!
      \param x
      The %iterator that will be compared with *this.
    */
    bool operator==(const iterator& x) const;

    //! Compares \p *this with x .
    /*!
      \param x
      The %iterator that will be compared with *this.
    */
    bool operator!=(const iterator& x) const;

  private:
    //! Checks the internal invariants, in debug mode only.
    bool OK() const;

    //! A pointer to the corresponding element of the tree's indexes[] array.
    const dimension_type* current_index;

    //! A pointer to the corresponding element of the tree's data[] array.
    data_type* current_data;

#if PPL_CO_TREE_EXTRA_DEBUG
    //! A pointer to the corresponding tree, used for debug purposes only.
    CO_Tree* tree;
#endif

    friend const_iterator& const_iterator::operator=(const iterator&);
    friend dimension_type CO_Tree::dfs_index(iterator itr) const;
  };

  //! Constructs an empty tree.
  /*!
    This constructor takes \f$O(1)\f$ time.
  */
  CO_Tree();

  //! The copy constructor.
  /*!
    \param y
    The tree that will be copied.

    This constructor takes \f$O(n)\f$ time.
  */
  CO_Tree(const CO_Tree& y);

  //! A constructor from a sequence of \p n elements.
  /*!
    \param i
    An iterator that points to the first element of the sequence.

    \param n
    The number of elements in the [i, i_end) sequence.

    i must be an input iterator on a sequence of data_type elements,
    sorted by index.
    Objects of Iterator type must have an index() method that returns the
    index with which the element pointed to by the iterator must be inserted.

    This constructor takes \f$O(n)\f$ time, so it is more efficient than
    the construction of an empty tree followed by n insertions, that would
    take \f$O(n*\log^2 n)\f$ time.
  */
  template <typename Iterator>
  CO_Tree(Iterator i, dimension_type n);

  //! The assignment operator.
  /*!
    \param y
    The tree that will be assigned to *this.

    This method takes \f$O(n)\f$ time.
  */
  CO_Tree& operator=(const CO_Tree& y);

  //! Removes all elements from the tree.
  /*!
    This method takes \f$O(n)\f$ time.
  */
  void clear();

  //! The destructor.
  /*!
    This destructor takes \f$O(n)\f$ time.
  */
  ~CO_Tree();

  //! Returns \p true if the tree has no elements.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  bool empty() const;

  //! Returns the number of elements stored in the tree.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  dimension_type size() const;

  //! Returns the size() of the largest possible CO_Tree.
  static dimension_type max_size();

  //! Dumps the tree to stdout, for debugging purposes.
  void dump_tree() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  /*!
    This method takes \f$O(n)\f$ time.
  */
  dimension_type external_memory_in_bytes() const;

  //! Inserts an element in the tree.
  /*!
    \returns
    An %iterator that points to the inserted pair.

    \param key
    The key that will be inserted into the tree, associated with the default
    data.

    If such a pair already exists, an %iterator pointing to that pair is
    returned.

    This operation invalidates existing iterators.

    This method takes \f$O(\log n)\f$ time if the element already exists, and
    \f$O(\log^2 n)\f$ amortized time otherwise.
  */
  iterator insert(dimension_type key);

  //! Inserts an element in the tree.
  /*!
    \returns
    An %iterator that points to the inserted element.

    \param key
    The key that will be inserted into the tree..

    \param data
    The data that will be inserted into the tree.

    If an element with the specified key already exists, its associated data
    is set to \p data and an %iterator pointing to that pair is returned.

    This operation invalidates existing iterators.

    This method takes \f$O(\log n)\f$ time if the element already exists, and
    \f$O(\log^2 n)\f$ amortized time otherwise.amortized
  */
  iterator insert(dimension_type key, data_type_const_reference data);

  //! Inserts an element in the tree.
  /*!
    \return
    An %iterator that points to the inserted element.

    \param itr
    The %iterator used as hint

    \param key
    The key that will be inserted into the tree, associated with the default
    data.

    This will be faster if \p itr points near to the place where the new
    element will be inserted (or where is already stored).
    However, the value of \p itr does not affect the result of this
    method, as long it is a valid %iterator for this tree. \p itr may even be
    end().

    If an element with the specified key already exists, an %iterator pointing
    to that pair is returned.

    This operation invalidates existing iterators.

    This method takes \f$O(\log n)\f$ time if the element already exists, and
    \f$O(\log^2 n)\f$ amortized time otherwise.
  */
  iterator insert(iterator itr, dimension_type key);

  //! Inserts an element in the tree.
  /*!
    \return
    An iterator that points to the inserted element.

    \param itr
    The iterator used as hint

    \param key
    The key that will be inserted into the tree.

    \param data
    The data that will be inserted into the tree.

    This will be faster if \p itr points near to the place where the new
    element will be inserted (or where is already stored).
    However, the value of \p itr does not affect the result of this
    method, as long it is a valid iterator for this tree. \p itr may even be
    end().

    If an element with the specified key already exists, its associated data
    is set to \p data and an iterator pointing to that pair is returned.

    This operation invalidates existing iterators.

    This method takes \f$O(\log n)\f$ time if the element already exists,
    and \f$O(\log^2 n)\f$ amortized time otherwise.
  */
  iterator insert(iterator itr, dimension_type key,
                  data_type_const_reference data);

  //! Erases the element with key \p key from the tree.
  /*!
    This operation invalidates existing iterators.

    \returns an iterator to the next element (or end() if there are no
             elements with key greater than \p key ).

    \param key
    The key of the element that will be erased from the tree.

    This method takes \f$O(\log n)\f$ time if the element already exists,
    and \f$O(\log^2 n)\f$ amortized time otherwise.
  */
  iterator erase(dimension_type key);

  //! Erases the element pointed to by \p itr from the tree.
  /*!
    This operation invalidates existing iterators.

    \returns an iterator to the next element (or end() if there are no
             elements with key greater than \p key ).

    \param itr
    An iterator pointing to the element that will be erased from the tree.

    This method takes \f$O(\log n)\f$ time if the element already exists, and
    \f$O(\log^2 n)\f$ amortized time otherwise.
  */
  iterator erase(iterator itr);

  /*!
    \brief Removes the element with key \p key (if it exists) and decrements
           by 1 all elements' keys that were greater than \p key.

    \param key
    The key of the element that will be erased from the tree.

    This operation invalidates existing iterators.

    This method takes \f$O(k+\log^2 n)\f$ expected time, where k is the number
    of elements with keys greater than \p key.
  */
  void erase_element_and_shift_left(dimension_type key);

  //! Adds \p n to all keys greater than or equal to \p key.
  /*!
    \param key
    The key of the first element whose key will be increased.

    \param n
    Specifies how much the keys will be increased.

    This method takes \f$O(k+\log n)\f$ expected time, where k is the number
    of elements with keys greater than or equal to \p key.
  */
  void increase_keys_from(dimension_type key, dimension_type n);

  //! Sets to \p i the key of *itr. Assumes that i<=itr.index() and that there
  //! are no elements with keys in [i,itr.index()).
  /*!
    All existing iterators remain valid.

    This method takes \f$O(1)\f$ time.
  */
  void fast_shift(dimension_type i, iterator itr);

  //! Swaps x with *this.
  /*!
    \param x
    The tree that will be swapped with *this.

    This operation invalidates existing iterators.

    This method takes \f$O(1)\f$ time.
  */
  void m_swap(CO_Tree& x);

  //! Returns an iterator that points at the first element.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  iterator begin();

  //! Returns an iterator that points after the last element.
  /*!
    This method always returns a reference to the same internal %iterator,
    that is updated at each operation that modifies the structure.
    Client code can keep a const reference to that %iterator instead of
    keep updating a local %iterator.

    This method takes \f$O(1)\f$ time.
  */
  const iterator& end();

  //! Equivalent to cbegin().
  const_iterator begin() const;

  //! Equivalent to cend().
  const const_iterator& end() const;

  //! Returns a const_iterator that points at the first element.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  const_iterator cbegin() const;

  //! Returns a const_iterator that points after the last element.
  /*!
    This method always returns a reference to the same internal %iterator,
    that is updated at each operation that modifies the structure.
    Client code can keep a const reference to that %iterator instead of
    keep updating a local %iterator.

    This method takes \f$O(1)\f$ time.
  */
  const const_iterator& cend() const;

  //! Searches an element with key \p key using bisection.
  /*!
    \param key
    The key that will be searched for.

    If the element is found, an %iterator pointing to that element is
    returned; otherwise, the returned %iterator refers to the immediately
    preceding or succeeding value.
    If the tree is empty, end() is returned.

    This method takes \f$O(\log n)\f$ time.
  */
  iterator bisect(dimension_type key);

  //! Searches an element with key \p key using bisection.
  /*!
    \param key
    The key that will be searched for.

    If the element is found, an %iterator pointing to that element is
    returned; otherwise, the returned %iterator refers to the immediately
    preceding or succeeding value.
    If the tree is empty, end() is returned.

    This method takes \f$O(\log n)\f$ time.
  */
  const_iterator bisect(dimension_type key) const;

  //! Searches an element with key \p key in [first, last] using bisection.
  /*!
    \param first
    An %iterator pointing to the first element in the range.
    It must not be end().

    \param last
    An %iterator pointing to the last element in the range.
    Note that this is included in the search.
    It must not be end().

    \param key
    The key that will be searched for.

    \return
    If the specified key is found, an %iterator pointing to that element is
    returned; otherwise, the returned %iterator refers to the immediately
    preceding or succeeding value.
    If the tree is empty, end() is returned.

    This method takes \f$O(\log(last - first + 1))\f$ time.
  */
  iterator bisect_in(iterator first, iterator last, dimension_type key);

  //! Searches an element with key \p key in [first, last] using bisection.
  /*!
    \param first
    An %iterator pointing to the first element in the range.
    It must not be end().

    \param last
    An %iterator pointing to the last element in the range.
    Note that this is included in the search.
    It must not be end().

    \param key
    The key that will be searched for.

    \return
    If the specified key is found, an %iterator pointing to that element is
    returned; otherwise, the returned %iterator refers to the immediately
    preceding or succeeding value.
    If the tree is empty, end() is returned.

    This method takes \f$O(\log(last - first + 1))\f$ time.
  */
  const_iterator bisect_in(const_iterator first, const_iterator last,
                           dimension_type key) const;

  //! Searches an element with key \p key near \p hint.
  /*!
    \param hint
    An %iterator used as a hint.

    \param key
    The key that will be searched for.

    If the element is found, the returned %iterator points to that element;
    otherwise, it points to the immediately preceding or succeeding value.
    If the tree is empty, end() is returned.

    The value of \p itr does not affect the result of this method, as long it
    is a valid %iterator for this tree. \p itr may even be end().

    This method takes \f$O(\log n)\f$ time. If the distance between the
    returned position and \p hint is \f$O(1)\f$ it takes \f$O(1)\f$ time.
  */
  iterator bisect_near(iterator hint, dimension_type key);

  //! Searches an element with key \p key near \p hint.
  /*!
    \param hint
    An %iterator used as a hint.

    \param key
    The key that will be searched for.

    If the element is found, the returned %iterator points to that element;
    otherwise, it points to the immediately preceding or succeeding value.
    If the tree is empty, end() is returned.

    The value of \p itr does not affect the result of this method, as long it
    is a valid %iterator for this tree. \p itr may even be end().

    This method takes \f$O(\log n)\f$ time. If the distance between the
    returned position and \p hint is \f$O(1)\f$ it takes \f$O(1)\f$ time.
  */
  const_iterator bisect_near(const_iterator hint, dimension_type key) const;

private:

  //! Searches an element with key \p key in [first, last] using bisection.
  /*!
    \param first
    The index of the first element in the range.
    It must be the index of an element with a value.

    \param last
    The index of the last element in the range.
    It must be the index of an element with a value.
    Note that this is included in the search.

    \param key
    The key that will be searched for.

    \return
    If the element is found, the index of that element is returned; otherwise,
    the returned index refers to the immediately preceding or succeeding
    value.

    This method takes \f$O(\log n)\f$ time.
  */
  dimension_type bisect_in(dimension_type first, dimension_type last,
                           dimension_type key) const;

  //! Searches an element with key \p key near \p hint.
  /*!
    \param hint
    An index used as a hint.
    It must be the index of an element with a value.

    \param key
    The key that will be searched for.

    \return
    If the element is found, the index of that element is returned; otherwise,
    the returned index refers to the immediately preceding or succeeding
    value.

    This uses a binary progression and then a bisection, so this method is
    \f$O(\log n)\f$, and it is \f$O(1)\f$ if the distance between the returned
    position and \p hint is \f$O(1)\f$.

    This method takes \f$O(\log n)\f$ time. If the distance between the
    returned position and \p hint is \f$O(1)\f$ it takes \f$O(1)\f$ time.
  */
  dimension_type bisect_near(dimension_type hint, dimension_type key) const;

  //! Inserts an element in the tree.
  /*!
    If there is already an element with key \p key in the tree, its
    associated data is set to \p data.

    This operation invalidates existing iterators.

    \return
    An %iterator that points to the inserted element.

    \param key
    The key that will be inserted into the tree.

    \param data
    The data that will be associated with \p key.

    \param itr
    It must point to the element in the tree with key \p key or, if no such
    element exists, it must point to the node that would be his parent.

    This method takes \f$O(1)\f$ time if the element already exists, and
    \f$O(\log^2 n)\f$ amortized time otherwise.
  */
  tree_iterator insert_precise(dimension_type key,
                               data_type_const_reference data,
                               tree_iterator itr);

  //! Helper for \c insert_precise.
  /*!
    This helper method takes the same arguments as \c insert_precise,
    but besides assuming that \p itr is a correct hint, it also assumes
    that \p key and \p data are not in the tree; namely, a proper
    insertion has to be done and the insertion can not invalidate \p data.
  */
  tree_iterator insert_precise_aux(dimension_type key,
                                   data_type_const_reference data,
                                   tree_iterator itr);

  //! Inserts an element in the tree.
  /*!

    \param key
    The key that will be inserted into the tree.

    \param data
    The data that will be associated with \p key.

    The tree must be empty.

    This operation invalidates existing iterators.

    This method takes \f$O(1)\f$ time.
  */
  void insert_in_empty_tree(dimension_type key,
                            data_type_const_reference data);

  //! Erases from the tree the element pointed to by \p itr .
  /*!
    This operation invalidates existing iterators.

    \returns
    An %iterator to the next element (or end() if there are no elements with
    key greater than \p key ).

    \param itr
    An %iterator pointing to the element that will be erased.

    This method takes \f$O(\log^2 n)\f$ amortized time.
  */
  iterator erase(tree_iterator itr);

  //! Initializes a tree with reserved size at least \p n .
  /*!
    \param n
    A lower bound on the tree's desired reserved size.

    This method takes \f$O(n)\f$ time.
  */
  void init(dimension_type n);

  //! Deallocates the tree's dynamic arrays.
  /*!
    After this call, the tree fields are uninitialized, so init() must be
    called again before using the tree.

    This method takes \f$O(n)\f$ time.
  */
  void destroy();

  //! Checks the internal invariants, but not the densities.
  bool structure_OK() const;

  //! Checks the internal invariants.
  bool OK() const;

  //! Returns the floor of the base-2 logarithm of \p n .
  /*!
    \param n
    It must be greater than zero.

    This method takes \f$O(\log n)\f$ time.
  */
  static unsigned integer_log2(dimension_type n);

  //! Compares the fractions numer/denom with ratio/100.
  /*!
    \returns Returns true if the fraction numer/denom is less
    than the fraction ratio/100.

    \param ratio
    It must be less than or equal to 100.

    \param numer
    The numerator of the fraction.

    \param denom
    The denominator of the fraction.

    This method takes \f$O(1)\f$ time.
  */
  static bool is_less_than_ratio(dimension_type numer, dimension_type denom,
                                 dimension_type ratio);

  //! Compares the fractions numer/denom with ratio/100.
  /*!
    \returns
    Returns true if the fraction numer/denom is greater than the fraction
    ratio/100.

    \param ratio
    It must be less than or equal to 100.

    \param numer
    The numerator of the fraction.

    \param denom
    The denominator of the fraction.

    This method takes \f$O(1)\f$ time.
  */
  static bool is_greater_than_ratio(dimension_type numer, dimension_type denom,
                                    dimension_type ratio);

  //! Dumps the subtree rooted at \p itr to stdout, for debugging purposes.
  /*!
    \param itr
    A tree_iterator pointing to the root of the desired subtree.
  */
  static void dump_subtree(tree_iterator itr);

  //! Increases the tree's reserved size.
  /*!
    This is called when the density is about to exceed the maximum density
    (specified by max_density_percent).

    This method takes \f$O(n)\f$ time.
  */
  void rebuild_bigger_tree();

  //! Decreases the tree's reserved size.
  /*!
    This is called when the density is about to become less than the minimum
    allowed density (specified by min_density_percent).

    \p reserved_size must be greater than 3 (otherwise the tree can just be
    cleared).

    This method takes \f$O(n)\f$ time.
  */
  void rebuild_smaller_tree();

  //! Re-initializes the cached iterators.
  /*!
    This method must be called when the indexes[] and data[] vector are
    reallocated.

    This method takes \f$O(1)\f$ time.
  */
  void refresh_cached_iterators();

  //! Rebalances the tree.
  /*!
    For insertions, it adds the pair (key, value) in the process.

    This operation invalidates existing iterators that point to nodes in the
    rebalanced subtree.

    \returns an %iterator pointing to the root of the subtree that was
             rebalanced.

    \param itr
    It points to the node where the new element has to be inserted or where an
    element has just been deleted.

    \param key
    The index that will be inserted in the tree (for insertions only).

    \param value
    The value that will be inserted in the tree (for insertions only).

    This method takes \f$O(\log^2 n)\f$ amortized time.
  */
  tree_iterator rebalance(tree_iterator itr, dimension_type key,
                          data_type_const_reference value);

  //! Moves all elements of a subtree to the rightmost end.
  /*!
    \returns
    The index of the rightmost unused node in the subtree after the process.

    \param last_in_subtree
    It is the index of the last element in the subtree.

    \param subtree_size
    It is the number of valid elements in the subtree.
    It must be greater than zero.

    \param key
    The key that may be added to the tree if add_element is \c true.

    \param value
    The value that may be added to the tree if add_element is \c true.

    \param add_element
    If it is true, it tries to add an element with key \p key and value
    \p value in the process (but it may not).

    This method takes \f$O(k)\f$ time, where k is \p subtree_size.
  */
  dimension_type compact_elements_in_the_rightmost_end(
    dimension_type last_in_subtree, dimension_type subtree_size,
    dimension_type key, data_type_const_reference value,
    bool add_element);

  //! Redistributes the elements in the subtree rooted at \p root_index.
  /*!
    The subtree's elements must be compacted to the rightmost end.

    \param root_index
    The index of the subtree's root node.

    \param subtree_size
    It is the number of used elements in the subtree.
    It must be greater than zero.

    \param last_used
    It points to the leftmost element with a value in the subtree.

    \param add_element
    If it is true, this method adds an element with the specified key and
    value in the process.

    \param key
    The key that will be added to the tree if \p add_element is \c true.

    \param value
    The data that will be added to the tree if \p add_element is \c true.

    This method takes \f$O(k)\f$ time, where k is \p subtree_size.
  */
  void redistribute_elements_in_subtree(dimension_type root_index,
                                        dimension_type subtree_size,
                                        dimension_type last_used,
                                        dimension_type key,
                                        data_type_const_reference value,
                                        bool add_element);

  //! Moves all data in the tree \p tree into *this.
  /*!
    \param tree
    The tree from which the element will be moved into *this.

    *this must be empty and big enough to contain all of tree's data without
    exceeding max_density.

    This method takes \f$O(n)\f$ time.
  */
  void move_data_from(CO_Tree& tree);

  //! Copies all data in the tree \p tree into *this.
  /*!
    \param tree
    The tree from which the element will be copied into *this.

    *this must be empty and must have the same reserved size of \p tree.
    this->OK() may return false before this method is called, but
    this->structure_OK() must return true.

    This method takes \f$O(n)\f$ time.
  */
  void copy_data_from(const CO_Tree& tree);

  //! Counts the number of used elements in the subtree rooted at itr.
  /*!
    \param itr
    An %iterator pointing to the root of the desired subtree.

    This method takes \f$O(k)\f$ time, where k is the number of elements in
    the subtree.
  */
  static dimension_type count_used_in_subtree(tree_iterator itr);

  //! Moves the value of \p from in \p to .
  /*!
    \param from
    It must be a valid value.

    \param to
    It must be a non-constructed chunk of memory.

    After the move, \p from becomes a non-constructed chunk of memory and
    \p to gets the value previously stored by \p from.

    The implementation of this method assumes that data_type values do not
    keep pointers to themselves nor to their fields.

    This method takes \f$O(1)\f$ time.
  */
  static void move_data_element(data_type& to, data_type& from);

  //! The maximum density of used nodes.
  /*!
    This must be greater than or equal to 50 and lower than 100.
  */
  static const dimension_type max_density_percent = 91;

  //! The minimum density of used nodes.
  /*!
    Must be strictly lower than the half of max_density_percent.
  */
  static const dimension_type min_density_percent = 38;

  //! The minimum density at the leaves' depth.
  /*!
    Must be greater than zero and strictly lower than min_density_percent.

    Increasing the value is safe but leads to time inefficiencies
    (measured against ppl_lpsol on 24 August 2010), because it forces trees to
    be more balanced, increasing the cost of rebalancing.
  */
  static const dimension_type min_leaf_density_percent = 1;

  //! An index used as a marker for unused nodes in the tree.
  /*!
    This must not be used as a key.
  */
  static const dimension_type unused_index = C_Integer<dimension_type>::max;

  //! The %iterator returned by end().
  /*!
    It is updated when needed, to keep it valid.
  */
  iterator cached_end;

  //! The %iterator returned by the const version of end().
  /*!
    It is updated when needed, to keep it valid.
  */
  const_iterator cached_const_end;

  //! The depth of the leaves in the complete tree.
  height_t max_depth;

  //! The vector that contains the keys in the tree.
  /*!
    If an element of this vector is \p unused_index , it means that that
    element and the corresponding element of data[] are not used.

    Its size is reserved_size + 2, because the first and the last elements
    are used as markers for iterators.
  */
  dimension_type* indexes;

  //! The allocator used to allocate/deallocate data.
  std::allocator<data_type> data_allocator;

  //! The vector that contains the data of the keys in the tree.
  /*!
    If index[i] is \p unused_index, data[i] is unused.
    Otherwise, data[i] contains the data associated to the indexes[i] key.

    Its size is reserved_size + 1, because the first element is not used (to
    allow using the same index in both indexes[] and data[] instead of
    adding 1 to access data[]).
  */
  data_type* data;

  //! The number of nodes in the complete tree.
  /*!
    It is one less than a power of 2.
    If this is 0, data and indexes are set to NULL.
  */
  dimension_type reserved_size;

  //! The number of values stored in the tree.
  dimension_type size_;
};

class CO_Tree::tree_iterator {

public:

  /*!
    \brief Constructs a tree_iterator pointing at the root node of the
           specified tree

    \param tree
    The tree to which the new %iterator will point to.
    It must not be empty.
  */
  explicit tree_iterator(CO_Tree& tree);

  //! Constructs a tree_iterator pointing at the specified node of the tree.
  /*!
    \param tree
    The tree to which the new %iterator will point to.
    It must not be empty.

    \param i
    The index of the element in \p tree to which the new %iterator will point
    to.
  */
  tree_iterator(CO_Tree& tree, dimension_type i);

  //! Constructs a tree_iterator from an iterator.
  /*!
    \param itr
    The iterator that will be converted into a tree_iterator.
    It must not be end().

    \param tree
    The tree to which the new %iterator will point to.
    It must not be empty.
  */
  tree_iterator(const iterator& itr, CO_Tree& tree);

  //! The assignment operator.
  /*!
    \param itr
    The %iterator that will be assigned into *this.
  */
  tree_iterator& operator=(const tree_iterator& itr);

  //! The assignment operator from an iterator.
  /*!
    \param itr
    The iterator that will be assigned into *this.
  */
  tree_iterator& operator=(const iterator& itr);

  //! Compares *this with \p itr.
  /*!
    \param itr
    The %iterator that will compared with *this.
  */
  bool operator==(const tree_iterator& itr) const;

  //! Compares *this with \p itr.
  /*!
    \param itr
    The %iterator that will compared with *this.
  */
  bool operator!=(const tree_iterator& itr) const;

  //! Makes the %iterator point to the root of \p tree.
  /*!
    The values of all fields (beside tree) are overwritten.

    This method takes \f$O(1)\f$ time.
  */
  void get_root();

  //! Makes the %iterator point to the left child of the current node.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  void get_left_child();

  //! Makes the %iterator point to the right child of the current node.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  void get_right_child();

  //! Makes the %iterator point to the parent of the current node.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  void get_parent();

  /*!
    \brief Searches for an element with key \p key in the subtree rooted at
           \p *this.

    \param key
    The searched for key.

    After this method, *this points to the found node (if it exists) or to
    the node that would be his parent (otherwise).

    This method takes \f$O(\log n)\f$ time.
  */
  void go_down_searching_key(dimension_type key);

  /*!
    \brief Follows left children with a value, until it arrives at a leaf or at
           a node with no value.

    This method takes \f$O(1)\f$ time.
  */
  void follow_left_children_with_value();

  /*!
    \brief Follows right children with a value, until it arrives at a leaf or at
           a node with no value.

    This method takes \f$O(1)\f$ time.
  */
  void follow_right_children_with_value();

  //! Returns true if the pointed node is the root node.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  bool is_root() const;

  //! Returns true if the pointed node has a parent and is its right child.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  bool is_right_child() const;

  //! Returns true if the pointed node is a leaf of the complete tree.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  bool is_leaf() const;

  //! Returns the key and value of the current node.
  data_type& operator*();

  //! Returns the key and value of the current node.
  Coefficient_traits::const_reference operator*() const;

  //! Returns a reference to the index of the element pointed to by \c *this.
  /*!
    \returns a reference to the index of the element pointed to by \c *this.
  */
  dimension_type& index();

  //! Returns the index of the element pointed to by \c *this.
  /*!
    \returns the index of the element pointed to by \c *this.
  */
  dimension_type index() const;

  //! Returns the index of the node pointed to by \c *this.
  /*!
    \returns the key of the node pointed to by \c *this, or unused_index if
             the current node does not contain a valid element.
  */
  dimension_type key() const;

  //! The tree containing the element pointed to by this %iterator.
  CO_Tree& tree;

  /*!
    \brief Returns the index of the current node in the DFS layout of the
           complete tree.
  */
  dimension_type dfs_index() const;

  /*!
    \brief Returns 2^h, with h the height of the current node in the tree,
           counting from 0.

    Thus leaves have offset 1.
    This is faster than depth(), so it is useful to compare node depths.

    This method takes \f$O(1)\f$ time.
  */
  dimension_type get_offset() const;

  //! Returns the depth of the current node in the complete tree.
  /*!
    This method takes \f$O(\log n)\f$ time.
  */
  height_t depth() const;

private:
  //! Checks the internal invariant.
  bool OK() const;

  //! The index of the current node in the DFS layout of the complete tree.
  dimension_type i;

  /*!
    \brief This is 2^h, with h the height of the current node in the tree,
           counting from 0.

    Thus leaves have offset 1.
    This is equal to (i & -i), and is only stored to increase performance.
  */
  dimension_type offset;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
/*! \relates CO_Tree */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void swap(CO_Tree& x, CO_Tree& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
/*! \relates CO_Tree::const_iterator */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void swap(CO_Tree::const_iterator& x, CO_Tree::const_iterator& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
/*! \relates CO_Tree::iterator */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void swap(CO_Tree::iterator& x, CO_Tree::iterator& y);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/CO_Tree_inlines.hh line 1. */
/* CO_Tree class implementation: inline functions.
*/


#include <cstddef>

namespace Parma_Polyhedra_Library {

inline dimension_type
CO_Tree::dfs_index(const_iterator itr) const {
  PPL_ASSERT(itr.current_index != 0);
  PPL_ASSERT(itr.current_index >= indexes + 1);
  PPL_ASSERT(itr.current_index <= indexes + reserved_size);
  const std::ptrdiff_t index = itr.current_index - indexes;
  return static_cast<dimension_type>(index);
}

inline dimension_type
CO_Tree::dfs_index(iterator itr) const {
  PPL_ASSERT(itr.current_index != 0);
  PPL_ASSERT(itr.current_index >= indexes + 1);
  PPL_ASSERT(itr.current_index <= indexes + reserved_size);
  const std::ptrdiff_t index = itr.current_index - indexes;
  return static_cast<dimension_type>(index);
}

inline
CO_Tree::CO_Tree() {
  init(0);
  PPL_ASSERT(OK());
}

inline
CO_Tree::CO_Tree(const CO_Tree& y) {
  PPL_ASSERT(y.OK());
  data_allocator = y.data_allocator;
  init(y.reserved_size);
  copy_data_from(y);
}

inline CO_Tree&
CO_Tree::operator=(const CO_Tree& y) {
  if (this != &y) {
    destroy();
    data_allocator = y.data_allocator;
    init(y.reserved_size);
    copy_data_from(y);
  }
  return *this;
}

inline void
CO_Tree::clear() {
  *this = CO_Tree();
}

inline
CO_Tree::~CO_Tree() {

  destroy();
}

inline bool
CO_Tree::empty() const {
  return size_ == 0;
}

inline dimension_type
CO_Tree::size() const {
  return size_;
}

inline dimension_type
CO_Tree::max_size() {
  return C_Integer<dimension_type>::max/100;
}

inline void
CO_Tree::dump_tree() const {
  if (empty()) {
    std::cout << "(empty tree)" << std::endl;
  }
  else {
    dump_subtree(tree_iterator(*const_cast<CO_Tree*>(this)));
  }
}

inline CO_Tree::iterator
CO_Tree::insert(const dimension_type key) {
  if (empty()) {
    return insert(key, Coefficient_zero());
  }
  else {
    tree_iterator itr(*this);
    itr.go_down_searching_key(key);
    if (itr.index() == key) {
      return iterator(itr);
    }
    else {
      return iterator(insert_precise(key, Coefficient_zero(), itr));
    }
  }
}

inline CO_Tree::iterator
CO_Tree::insert(dimension_type key, data_type_const_reference data1) {
  if (empty()) {
    insert_in_empty_tree(key, data1);
    tree_iterator itr(*this);
    PPL_ASSERT(itr.index() != unused_index);
    return iterator(itr);
  }
  else {
    tree_iterator itr(*this);
    itr.go_down_searching_key(key);
    return iterator(insert_precise(key, data1, itr));
  }
}

inline CO_Tree::iterator
CO_Tree::erase(dimension_type key) {
  PPL_ASSERT(key != unused_index);

  if (empty()) {
    return end();
  }

  tree_iterator itr(*this);
  itr.go_down_searching_key(key);

  if (itr.index() == key) {
    return erase(itr);
  }

  iterator result(itr);
  if (result.index() < key) {
    ++result;
  }

  PPL_ASSERT(result == end() || result.index() > key);
#ifndef NDEBUG
  iterator last = end();
  --last;
  PPL_ASSERT((result == end()) == (last.index() < key));
#endif

  return result;
}

inline CO_Tree::iterator
CO_Tree::erase(iterator itr) {
  PPL_ASSERT(itr != end());
  return erase(tree_iterator(itr, *this));
}

inline void
CO_Tree::m_swap(CO_Tree& x) {
  using std::swap;
  swap(max_depth, x.max_depth);
  swap(indexes, x.indexes);
  swap(data_allocator, x.data_allocator);
  swap(data, x.data);
  swap(reserved_size, x.reserved_size);
  swap(size_, x.size_);
  // Cached iterators have been invalidated by the swap,
  // they must be refreshed here.
  refresh_cached_iterators();
  x.refresh_cached_iterators();
  PPL_ASSERT(structure_OK());
  PPL_ASSERT(x.structure_OK());
}

inline CO_Tree::iterator
CO_Tree::begin() {
  return iterator(*this);
}

inline const CO_Tree::iterator&
CO_Tree::end() {
  return cached_end;
}

inline CO_Tree::const_iterator
CO_Tree::begin() const {
  return const_iterator(*this);
}

inline const CO_Tree::const_iterator&
CO_Tree::end() const {
  return cached_const_end;
}

inline CO_Tree::const_iterator
CO_Tree::cbegin() const {
  return const_iterator(*this);
}

inline const CO_Tree::const_iterator&
CO_Tree::cend() const {
  return cached_const_end;
}

inline CO_Tree::iterator
CO_Tree::bisect(dimension_type key) {
  if (empty()) {
    return end();
  }
  iterator last = end();
  --last;
  return bisect_in(begin(), last, key);
}

inline CO_Tree::const_iterator
CO_Tree::bisect(dimension_type key) const {
  if (empty()) {
    return end();
  }
  const_iterator last = end();
  --last;
  return bisect_in(begin(), last, key);
}

inline CO_Tree::iterator
CO_Tree::bisect_in(iterator first, iterator last, dimension_type key) {
  PPL_ASSERT(first != end());
  PPL_ASSERT(last != end());
  const dimension_type index
    = bisect_in(dfs_index(first), dfs_index(last), key);
  return iterator(*this, index);
}

inline CO_Tree::const_iterator
CO_Tree::bisect_in(const_iterator first, const_iterator last,
                   dimension_type key) const {
  PPL_ASSERT(first != end());
  PPL_ASSERT(last != end());
  const dimension_type index
    = bisect_in(dfs_index(first), dfs_index(last), key);
  return const_iterator(*this, index);
}

inline CO_Tree::iterator
CO_Tree::bisect_near(iterator hint, dimension_type key) {
  if (hint == end()) {
    return bisect(key);
  }
  const dimension_type index
    = bisect_near(dfs_index(hint), key);
  return iterator(*this, index);
}

inline CO_Tree::const_iterator
CO_Tree::bisect_near(const_iterator hint, dimension_type key) const {
  if (hint == end()) {
    return bisect(key);
  }
  const dimension_type index = bisect_near(dfs_index(hint), key);
  return const_iterator(*this, index);
}

inline void
CO_Tree::fast_shift(dimension_type i, iterator itr) {
  PPL_ASSERT(itr != end());
  PPL_ASSERT(i <= itr.index());
  indexes[dfs_index(itr)] = i;
  PPL_ASSERT(OK());
}

inline void
CO_Tree::insert_in_empty_tree(dimension_type key,
                              data_type_const_reference data1) {
  PPL_ASSERT(empty());
  rebuild_bigger_tree();
  tree_iterator itr(*this);
  PPL_ASSERT(itr.index() == unused_index);
  new(&(*itr)) data_type(data1);
  // Set the index afterwards, so that if the constructor above throws
  // the tree's structure is consistent.
  itr.index() = key;
  ++size_;

  PPL_ASSERT(OK());
}

inline bool
CO_Tree::is_less_than_ratio(dimension_type numer, dimension_type denom,
                            dimension_type ratio) {
  PPL_ASSERT(ratio <= 100);
  // If these are true, no overflows are possible.
  PPL_ASSERT(denom <= unused_index/100);
  PPL_ASSERT(numer <= unused_index/100);
  return 100*numer < ratio*denom;
}

inline bool
CO_Tree::is_greater_than_ratio(dimension_type numer, dimension_type denom,
                               dimension_type ratio) {
  PPL_ASSERT(ratio <= 100);
  // If these are true, no overflows are possible.
  PPL_ASSERT(denom <= unused_index/100);
  PPL_ASSERT(numer <= unused_index/100);
  return 100*numer > ratio*denom;
}

inline void
CO_Tree::rebuild_smaller_tree() {
  PPL_ASSERT(reserved_size > 3);
  CO_Tree new_tree;
  new_tree.init(reserved_size / 2);
  new_tree.move_data_from(*this);
  m_swap(new_tree);
  PPL_ASSERT(new_tree.structure_OK());
  PPL_ASSERT(structure_OK());
}

inline void
CO_Tree::refresh_cached_iterators() {
  cached_end = iterator(*this, reserved_size + 1);
  cached_const_end = const_iterator(*this, reserved_size + 1);
}

inline void
CO_Tree::move_data_element(data_type& to, data_type& from) {
  /*
    The following code is equivalent (but slower):

    <CODE>
      new(&to) data_type(from);
      from.~data_type();
    </CODE>
  */
  std::memcpy(&to, &from, sizeof(data_type));
}


inline
CO_Tree::const_iterator::const_iterator()
  : current_index(0), current_data(0) {
#if PPL_CO_TREE_EXTRA_DEBUG
  tree = 0;
#endif
  PPL_ASSERT(OK());
}

inline
CO_Tree::const_iterator::const_iterator(const CO_Tree& tree1)
  : current_index(&(tree1.indexes[1])), current_data(&(tree1.data[1])) {
#if PPL_CO_TREE_EXTRA_DEBUG
  tree = &tree1;
#endif
  if (!tree1.empty()) {
    while (*current_index == unused_index) {
      ++current_index;
      ++current_data;
    }
  }
  PPL_ASSERT(OK());
}

inline
CO_Tree::const_iterator::const_iterator(const CO_Tree& tree1,
                                        dimension_type i)
  : current_index(&(tree1.indexes[i])), current_data(&(tree1.data[i])) {
#if PPL_CO_TREE_EXTRA_DEBUG
  tree = &tree1;
#endif
  PPL_ASSERT(i != 0);
  PPL_ASSERT(i <= tree1.reserved_size + 1);
  PPL_ASSERT(tree1.empty() || tree1.indexes[i] != unused_index);
  PPL_ASSERT(OK());
}

inline
CO_Tree::const_iterator::const_iterator(const const_iterator& itr2) {
  (*this) = itr2;
  PPL_ASSERT(OK());
}

inline
CO_Tree::const_iterator::const_iterator(const iterator& itr2) {
  (*this) = itr2;
  PPL_ASSERT(OK());
}

inline void
CO_Tree::const_iterator::m_swap(const_iterator& itr) {
  using std::swap;
  swap(current_data, itr.current_data);
  swap(current_index, itr.current_index);
#if PPL_CO_TREE_EXTRA_DEBUG
  swap(tree, itr.tree);
#endif
  PPL_ASSERT(OK());
  PPL_ASSERT(itr.OK());
}

inline CO_Tree::const_iterator&
CO_Tree::const_iterator::operator=(const const_iterator& itr2) {
  current_index = itr2.current_index;
  current_data = itr2.current_data;
#if PPL_CO_TREE_EXTRA_DEBUG
  tree = itr2.tree;
#endif
  PPL_ASSERT(OK());
  return *this;
}

inline CO_Tree::const_iterator&
CO_Tree::const_iterator::operator=(const iterator& itr2) {
  current_index = itr2.current_index;
  current_data = itr2.current_data;
#if PPL_CO_TREE_EXTRA_DEBUG
  tree = itr2.tree;
#endif
  PPL_ASSERT(OK());
  return *this;
}

inline CO_Tree::const_iterator&
CO_Tree::const_iterator::operator++() {
  PPL_ASSERT(current_index != 0);
  PPL_ASSERT(current_data != 0);
#if PPL_CO_TREE_EXTRA_DEBUG
  PPL_ASSERT(current_index != &(tree->indexes[tree->reserved_size + 1]));
#endif
  ++current_index;
  ++current_data;
  while (*current_index == unused_index) {
    ++current_index;
    ++current_data;
  }
  PPL_ASSERT(OK());
  return *this;
}

inline CO_Tree::const_iterator&
CO_Tree::const_iterator::operator--() {
  PPL_ASSERT(current_index != 0);
  PPL_ASSERT(current_data != 0);
  --current_index;
  --current_data;
  while (*current_index == unused_index) {
    --current_index;
    --current_data;
  }
  PPL_ASSERT(OK());
  return *this;
}

inline CO_Tree::const_iterator
CO_Tree::const_iterator::operator++(int) {
  const_iterator itr(*this);
  ++(*this);
  return itr;
}

inline CO_Tree::const_iterator
CO_Tree::const_iterator::operator--(int) {
  const_iterator itr(*this);
  --(*this);
  return itr;
}

inline Coefficient_traits::const_reference
CO_Tree::const_iterator::operator*() const {
  PPL_ASSERT(current_index != 0);
  PPL_ASSERT(current_data != 0);
  PPL_ASSERT(OK());
#if PPL_CO_TREE_EXTRA_DEBUG
  PPL_ASSERT(current_index != &(tree->indexes[tree->reserved_size + 1]));
#endif
  return *current_data;
}

inline dimension_type
CO_Tree::const_iterator::index() const {
  PPL_ASSERT(current_index != 0);
  PPL_ASSERT(current_data != 0);
  PPL_ASSERT(OK());
#if PPL_CO_TREE_EXTRA_DEBUG
  PPL_ASSERT(current_index != &(tree->indexes[tree->reserved_size + 1]));
#endif
  return *current_index;
}

inline bool
CO_Tree::const_iterator::operator==(const const_iterator& x) const {
  PPL_ASSERT((current_index == x.current_index)
             == (current_data == x.current_data));
  PPL_ASSERT(OK());
  return (current_index == x.current_index);
}

inline bool
CO_Tree::const_iterator::operator!=(const const_iterator& x) const {
  return !(*this == x);
}


inline
CO_Tree::iterator::iterator()
  : current_index(0), current_data(0) {
#if PPL_CO_TREE_EXTRA_DEBUG
  tree = 0;
#endif
  PPL_ASSERT(OK());
}

inline
CO_Tree::iterator::iterator(CO_Tree& tree1)
  : current_index(&(tree1.indexes[1])), current_data(&(tree1.data[1])) {
#if PPL_CO_TREE_EXTRA_DEBUG
  tree = &tree1;
#endif
  if (!tree1.empty()) {
    while (*current_index == unused_index) {
      ++current_index;
      ++current_data;
    }
  }
  PPL_ASSERT(OK());
}

inline
CO_Tree::iterator::iterator(CO_Tree& tree1, dimension_type i)
  : current_index(&(tree1.indexes[i])), current_data(&(tree1.data[i])) {
#if PPL_CO_TREE_EXTRA_DEBUG
  tree = &tree1;
#endif
  PPL_ASSERT(i != 0);
  PPL_ASSERT(i <= tree1.reserved_size + 1);
  PPL_ASSERT(tree1.empty() || tree1.indexes[i] != unused_index);
  PPL_ASSERT(OK());
}

inline
CO_Tree::iterator::iterator(const tree_iterator& itr) {
  *this = itr;
  PPL_ASSERT(OK());
}

inline
CO_Tree::iterator::iterator(const iterator& itr2) {
  (*this) = itr2;
  PPL_ASSERT(OK());
}

inline void
CO_Tree::iterator::m_swap(iterator& itr) {
  using std::swap;
  swap(current_data, itr.current_data);
  swap(current_index, itr.current_index);
#if PPL_CO_TREE_EXTRA_DEBUG
  swap(tree, itr.tree);
#endif
  PPL_ASSERT(OK());
  PPL_ASSERT(itr.OK());
}

inline CO_Tree::iterator&
CO_Tree::iterator::operator=(const tree_iterator& itr) {
  current_index = &(itr.tree.indexes[itr.dfs_index()]);
  current_data = &(itr.tree.data[itr.dfs_index()]);
#if PPL_CO_TREE_EXTRA_DEBUG
  tree = &(itr.tree);
#endif
  PPL_ASSERT(OK());
  return *this;
}

inline CO_Tree::iterator&
CO_Tree::iterator::operator=(const iterator& itr2) {
  current_index = itr2.current_index;
  current_data = itr2.current_data;
#if PPL_CO_TREE_EXTRA_DEBUG
  tree = itr2.tree;
#endif
  PPL_ASSERT(OK());
  return *this;
}

inline CO_Tree::iterator&
CO_Tree::iterator::operator++() {
  PPL_ASSERT(current_index != 0);
  PPL_ASSERT(current_data != 0);
#if PPL_CO_TREE_EXTRA_DEBUG
  PPL_ASSERT(current_index != &(tree->indexes[tree->reserved_size + 1]));
#endif
  ++current_index;
  ++current_data;
  while (*current_index == unused_index) {
    ++current_index;
    ++current_data;
  }

  PPL_ASSERT(OK());
  return *this;
}

inline CO_Tree::iterator&
CO_Tree::iterator::operator--() {
  PPL_ASSERT(current_index != 0);
  PPL_ASSERT(current_data != 0);
  --current_index;
  --current_data;
  while (*current_index == unused_index) {
    --current_index;
    --current_data;
  }

  PPL_ASSERT(OK());
  return *this;
}

inline CO_Tree::iterator
CO_Tree::iterator::operator++(int) {
  iterator itr(*this);
  ++(*this);
  return itr;
}

inline CO_Tree::iterator
CO_Tree::iterator::operator--(int) {
  iterator itr(*this);
  --(*this);
  return itr;
}

inline CO_Tree::data_type&
CO_Tree::iterator::operator*() {
  PPL_ASSERT(current_index != 0);
  PPL_ASSERT(current_data != 0);
  PPL_ASSERT(OK());
#if PPL_CO_TREE_EXTRA_DEBUG
  PPL_ASSERT(current_index != &(tree->indexes[tree->reserved_size + 1]));
#endif
  return *current_data;
}

inline Coefficient_traits::const_reference
CO_Tree::iterator::operator*() const {
  PPL_ASSERT(current_index != 0);
  PPL_ASSERT(current_data != 0);
  PPL_ASSERT(OK());
#if PPL_CO_TREE_EXTRA_DEBUG
  PPL_ASSERT(current_index != &(tree->indexes[tree->reserved_size + 1]));
#endif
  return *current_data;
}

inline dimension_type
CO_Tree::iterator::index() const {
  PPL_ASSERT(current_index != 0);
  PPL_ASSERT(current_data != 0);
  PPL_ASSERT(OK());
#if PPL_CO_TREE_EXTRA_DEBUG
  PPL_ASSERT(current_index != &(tree->indexes[tree->reserved_size + 1]));
#endif
  return *current_index;
}

inline bool
CO_Tree::iterator::operator==(const iterator& x) const {
  PPL_ASSERT((current_index == x.current_index)
             == (current_data == x.current_data));
  PPL_ASSERT(OK());
  return (current_index == x.current_index);
}

inline bool
CO_Tree::iterator::operator!=(const iterator& x) const {
  return !(*this == x);
}


inline
CO_Tree::tree_iterator::tree_iterator(CO_Tree& tree1)
  : tree(tree1) {
  PPL_ASSERT(tree.reserved_size != 0);
  get_root();
  PPL_ASSERT(OK());
}

inline
CO_Tree::tree_iterator::tree_iterator(CO_Tree& tree1, dimension_type i1)
  : tree(tree1) {
  PPL_ASSERT(tree.reserved_size != 0);
  PPL_ASSERT(i1 <= tree.reserved_size + 1);
  i = i1;
  offset = least_significant_one_mask(i);
  PPL_ASSERT(OK());
}

inline
CO_Tree::tree_iterator::tree_iterator(const iterator& itr, CO_Tree& tree1)
  : tree(tree1) {
  PPL_ASSERT(tree.reserved_size != 0);
  *this = itr;
  PPL_ASSERT(OK());
}

inline CO_Tree::tree_iterator&
CO_Tree::tree_iterator::operator=(const tree_iterator& itr) {
  PPL_ASSERT(&tree == &(itr.tree));
  i = itr.i;
  offset = itr.offset;
  return *this;
}

inline CO_Tree::tree_iterator&
CO_Tree::tree_iterator::operator=(const iterator& itr) {
  PPL_ASSERT(itr != tree.end());
  i = tree.dfs_index(itr);
  offset = least_significant_one_mask(i);
  return *this;
}

inline bool
CO_Tree::tree_iterator::operator==(const tree_iterator& itr) const {
  return i == itr.i;
}

inline bool
CO_Tree::tree_iterator::operator!=(const tree_iterator& itr) const {
  return !(*this == itr);
}

inline void
CO_Tree::tree_iterator::get_root() {
  i = tree.reserved_size / 2 + 1;
  offset = i;
  PPL_ASSERT(OK());
}

inline void
CO_Tree::tree_iterator::get_left_child() {
  PPL_ASSERT(offset != 0);
  PPL_ASSERT(offset != 1);
  offset /= 2;
  i -= offset;
  PPL_ASSERT(OK());
}

inline void
CO_Tree::tree_iterator::get_right_child() {
  PPL_ASSERT(offset != 0);
  PPL_ASSERT(offset != 1);
  offset /= 2;
  i += offset;
  PPL_ASSERT(OK());
}

inline void
CO_Tree::tree_iterator::get_parent() {
  PPL_ASSERT(!is_root());
  PPL_ASSERT(offset != 0);
  i &= ~offset;
  offset *= 2;
  i |= offset;
  PPL_ASSERT(OK());
}

inline void
CO_Tree::tree_iterator::follow_left_children_with_value() {
  PPL_ASSERT(index() != unused_index);
  const dimension_type* p = tree.indexes;
  p += i;
  p -= (offset - 1);
  while (*p == unused_index) {
    ++p;
  }
  const std::ptrdiff_t distance = p - tree.indexes;
  PPL_ASSERT(distance >= 0);
  i = static_cast<dimension_type>(distance);
  offset = least_significant_one_mask(i);
  PPL_ASSERT(OK());
}

inline void
CO_Tree::tree_iterator::follow_right_children_with_value() {
  PPL_ASSERT(index() != unused_index);
  const dimension_type* p = tree.indexes;
  p += i;
  p += (offset - 1);
  while (*p == unused_index) {
    --p;
  }
  const std::ptrdiff_t distance = p - tree.indexes;
  PPL_ASSERT(distance >= 0);
  i = static_cast<dimension_type>(distance);
  offset = least_significant_one_mask(i);
  PPL_ASSERT(OK());
}

inline bool
CO_Tree::tree_iterator::is_root() const {
  // This is implied by OK(), it is here for reference only.
  PPL_ASSERT(offset <= (tree.reserved_size / 2 + 1));
  return offset == (tree.reserved_size / 2 + 1);
}

inline bool
CO_Tree::tree_iterator::is_right_child() const {
  if (is_root()) {
    return false;
  }
  return (i & (2*offset)) != 0;
}

inline bool
CO_Tree::tree_iterator::is_leaf() const {
  return offset == 1;
}

inline CO_Tree::data_type&
CO_Tree::tree_iterator::operator*() {
  return tree.data[i];
}

inline Coefficient_traits::const_reference
CO_Tree::tree_iterator::operator*() const {
  return tree.data[i];
}

inline dimension_type&
CO_Tree::tree_iterator::index() {
  return tree.indexes[i];
}

inline dimension_type
CO_Tree::tree_iterator::index() const {
  return tree.indexes[i];
}

inline dimension_type
CO_Tree::tree_iterator::dfs_index() const {
  return i;
}

inline dimension_type
CO_Tree::tree_iterator::get_offset() const {
  return offset;
}

inline CO_Tree::height_t
CO_Tree::tree_iterator::depth() const {
  return integer_log2((tree.reserved_size + 1) / offset);
}

inline void
swap(CO_Tree& x, CO_Tree& y) {
  x.m_swap(y);
}

inline void
swap(CO_Tree::const_iterator& x, CO_Tree::const_iterator& y) {
  x.m_swap(y);
}

inline void
swap(CO_Tree::iterator& x, CO_Tree::iterator& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/CO_Tree_templates.hh line 1. */
/* CO_Tree class implementation: non-inline template functions.
*/


namespace Parma_Polyhedra_Library {

template <typename Iterator>
CO_Tree::CO_Tree(Iterator i, dimension_type n) {

  if (n == 0) {
    init(0);
    PPL_ASSERT(OK());
    return;
  }

  const dimension_type new_max_depth = integer_log2(n) + 1;
  reserved_size = (static_cast<dimension_type>(1) << new_max_depth) - 1;

  if (is_greater_than_ratio(n, reserved_size, max_density_percent)
      && reserved_size != 3) {
    reserved_size = reserved_size*2 + 1;
  }

  init(reserved_size);

  tree_iterator root(*this);

  // This is static and with static allocation, to improve performance.
  // sizeof_to_bits(sizeof(dimension_type)) is the maximum k such that
  // 2^k-1 is a dimension_type, so it is the maximum tree height.
  // For each node level, the stack may contain up to 4 elements: two elements
  // with operation 0, one element with operation 2 and one element
  // with operation 3. An additional element with operation 1 can be at the
  // top of the tree.
  static std::pair<dimension_type, signed char>
    stack[4U * sizeof_to_bits(sizeof(dimension_type)) + 1U];

  dimension_type stack_first_empty = 0;

  // A pair (n, operation) in the stack means:
  //
  // * Go to the parent, if operation is 0.
  // * Go to the left child, then fill the current tree with n elements, if
  //   operation is 1.
  // * Go to the right child, then fill the current tree with n elements, if
  //   operation is 2.
  // * Fill the current tree with n elements, if operation is 3.

  stack[0].first = n;
  stack[0].second = 3;
  ++stack_first_empty;

  while (stack_first_empty != 0) {

    // Implement
    //
    // <CODE>
    //   top_n         = stack.top().first;
    //   top_operation = stack.top().second;
    // </CODE>
    const dimension_type top_n = stack[stack_first_empty - 1].first;
    const signed char top_operation = stack[stack_first_empty - 1].second;

    switch (top_operation) {

    case 0:
      root.get_parent();
      --stack_first_empty;
      continue;

    case 1:
      root.get_left_child();
      break;

    case 2:
      root.get_right_child();
      break;
#ifndef NDEBUG
    case 3:
      break;

    default:
      // We should not be here
      PPL_UNREACHABLE;
#endif
    }

    // We now visit the current tree

    if (top_n == 0) {
      --stack_first_empty;
    }
    else {
      if (top_n == 1) {
        PPL_ASSERT(root.index() == unused_index);
        root.index() = i.index();
        new(&(*root)) data_type(*i);
        ++i;
        --stack_first_empty;
      }
      else {
        PPL_ASSERT(stack_first_empty + 3 < sizeof(stack)/sizeof(stack[0]));

        const dimension_type half = (top_n + 1) / 2;
        stack[stack_first_empty - 1].second = 0;
        stack[stack_first_empty    ] = std::make_pair(top_n - half, 2);
        stack[stack_first_empty + 1] = std::make_pair(1, 3);
        stack[stack_first_empty + 2].second = 0;
        stack[stack_first_empty + 3] = std::make_pair(half - 1, 1);
        stack_first_empty += 4;
      }
    }
  }
  size_ = n;
  PPL_ASSERT(OK());
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/CO_Tree_defs.hh line 1559. */

/* Automatically generated from PPL source file ../src/Sparse_Row_defs.hh line 32. */

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A finite sparse sequence of coefficients.
/*! \ingroup PPL_CXX_interface
  This class is implemented using a CO_Tree. See the documentation of CO_Tree
  for details on the implementation and the performance.

  This class is a drop-in replacement of Dense_Row, meaning that code
  using Dense_Row can be ported to Sparse_Row changing only the type.
  The resulting code will work, but probably needs more CPU and memory (it
  does not exploit the sparse representation yet).

  To take advantage of the sparse representation, the client code must then be
  modified to use methods which can have a faster implementation on sparse
  data structures.

  The main changes are the replacement of calls to operator[] with calls to
  find(), lower_bound() or insert(), using hint iterators when possible.
  Sequential scanning of rows should probably be implemented using iterators
  rather than indexes, to improve performance.
  reset() should be called to zero elements.

  \see Sparse_Matrix
  \see CO_Tree
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
class Parma_Polyhedra_Library::Sparse_Row {

public:

  //! An %iterator on the row elements
  /*!
    This %iterator skips non-stored zeroes.
    \see CO_Tree::iterator
  */
  typedef CO_Tree::iterator iterator;

  //! A const %iterator on the row elements
  /*!
    This %iterator skips non-stored zeroes.
    \see CO_Tree::const_iterator
  */
  typedef CO_Tree::const_iterator const_iterator;

  //! Constructs a row with the specified size.
  /*!
    \param n
    The size for the new row.

    The row will contain only non-stored zeroes.

    This constructor takes \f$O(1)\f$ time.
  */
  explicit Sparse_Row(dimension_type n = 0);

  //! Constructs a row with the specified size.
  /*!
    \param n
    The size for the new row.

    \param capacity
    It is ignored. This parameter is needed for compatibility with Dense_Row.

    The row will contain only non-stored zeroes.

    This constructor takes \f$O(1)\f$ time.
  */
  Sparse_Row(dimension_type n, dimension_type capacity);

  //! Copy constructor with specified capacity.
  /*!
    It is assumed that \p capacity is greater than or equal to
    the size of \p y.
  */
  Sparse_Row(const Sparse_Row& y, dimension_type capacity);

  //! Copy constructor with specified size and capacity.
  /*!
    It is assumed that \p sz is less than or equal to \p capacity.
  */
  Sparse_Row(const Sparse_Row& y, dimension_type sz, dimension_type capacity);

  //! Constructor from a Dense_Row.
  /*!
    \param row
    The row that will be copied into *this.

    This constructor takes \f$O(n)\f$ time. Note that constructing of a row of
    zeroes and then inserting n elements costs \f$O(n*\log^2 n)\f$ time.
  */
  explicit Sparse_Row(const Dense_Row& row);

  //! Copy constructor from a Dense_Row with specified size and capacity.
  /*!
    It is assumed that \p sz is less than or equal to \p capacity.
  */
  Sparse_Row(const Dense_Row& y, dimension_type sz, dimension_type capacity);

  Sparse_Row& operator=(const Dense_Row& row);

  //! Swaps *this and x.
  /*!
    \param x
    The row that will be swapped with *this.

    This method takes \f$O(1)\f$ time.
  */
  void m_swap(Sparse_Row& x);

  //! Returns the size of the row.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  dimension_type size() const;

  //! Returns the number of elements explicitly stored in the row.
  /*!
    This is equivalent to std::distance(begin(), end()), but it's much faster.

    This method takes \f$O(1)\f$ time.
  */
  dimension_type num_stored_elements() const;

  //! Resizes the row to the specified size.
  /*!
    \param n
    The new size for the row.

    This method takes \f$O(k*\log^2 n)\f$ amortized time when shrinking the
    row and removing the trailing k elements.
    It takes \f$O(1)\f$ time when enlarging the row.
  */
  void resize(dimension_type n);

  //! Resizes the row to size \p n.
  /*!
    \param n
    The new size for the row.

    This method, with this signature, is needed for compatibility with
    Dense_Row.

    This method takes \f$O(1)\f$ time.
  */
  void expand_within_capacity(dimension_type n);

  //! Resizes the row to size \p n.
  /*!
    \param n
    The new size for the row.

    This method, with this signature, is needed for compatibility with
    Dense_Row.

    This method takes \f$O(k*\log^2 n)\f$ amortized time where k is the number
    of removed elements.
  */
  void shrink(dimension_type n);

  /*!
    \brief Deletes the i-th element from the row, shifting the next elements
           to the left.

    \param i
    The index of the element that will be deleted.

    The size of the row is decreased by 1.

    This operation invalidates existing iterators.

    This method takes \f$O(k+\log^2 n)\f$ amortized time, where k is the
    number of elements with index greater than i.
  */
  void delete_element_and_shift(dimension_type i);

  //! Adds \p n zeroes before index \p i.
  /*!
    \param n
    The number of non-stored zeroes that will be added to the row.

    \param i
    The index of the element before which the zeroes will be added.

    Existing elements with index greater than or equal to \p i are shifted to
    the right by \p n positions. The size is increased by \p n.

    Existing iterators are not invalidated, but are shifted to the right
    by \p n if they pointed at or after index \p i (i.e., they point to
    the same, possibly shifted, values as before).

    This method takes \f$O(k + \log m)\f$ expected time, where \f$k\f$ is
    the number of elements with index greater than or equal to \p i and
    \f$m\f$ the number of stored elements.
  */
  void add_zeroes_and_shift(dimension_type n, dimension_type i);

  //! Returns an %iterator that points at the first stored element.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  iterator begin();

  //! Returns an %iterator that points after the last stored element.
  /*!
    This method always returns a reference to the same internal %iterator,
    that is kept valid.
    Client code can keep a const reference to that %iterator instead of
    keep updating a local %iterator.

    This method takes \f$O(1)\f$ time.
  */
  const iterator& end();

  //! Equivalent to <CODE>cbegin()</CODE>.
  const_iterator begin() const;

  //! Equivalent to <CODE>cend()</CODE>.
  const const_iterator& end() const;

  //! Returns an %iterator that points at the first element.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  const_iterator cbegin() const;

  //! Returns an %iterator that points after the last element.
  /*!
    This method always returns a reference to the same internal %iterator,
    that is updated at each operation that modifies the structure.
    Client code can keep a const reference to that %iterator instead of
    keep updating a local %iterator.

    This method takes \f$O(1)\f$ time.
  */
  const const_iterator& cend() const;

  //! Returns the size() of the largest possible Sparse_Row.
  static dimension_type max_size();

  //! Resets all the elements of this row.
  /*!
    This method takes \f$O(n)\f$ time.
  */
  void clear();

  //! Gets a reference to the i-th element.
  /*!
    \param i
    The index of the desired element.

    For read-only access it's better to use get(), that avoids allocating
    space for zeroes.

    If possible, use the insert(), find() or lower_bound() methods with
    a hint instead of this, to improve performance.

    This operation invalidates existing iterators.

    This method takes \f$O(\log n)\f$ amortized time when there is already an
    element with index \p i, and \f$O(\log^2 n)\f$ otherwise.
  */
  Coefficient& operator[](dimension_type i);

  //! Equivalent to <CODE>get(i)</CODE>, provided for convenience.
  /*!
    This method takes \f$O(\log n)\f$ time.
  */
  Coefficient_traits::const_reference operator[](dimension_type i) const;

  //! Gets the i-th element in the sequence.
  /*!
    \param i
    The index of the desired element.

    If possible, use the insert(), find() or lower_bound() methods with
    a hint instead of this, to improve performance.

    This method takes \f$O(\log n)\f$ time.
  */
  Coefficient_traits::const_reference get(dimension_type i) const;

  //! Looks for an element with index i.
  /*!
    \param i
    The index of the desired element.

    If possible, use the find() method that takes a hint %iterator, to improve
    performance.

    This method takes \f$O(\log n)\f$ time.
  */
  iterator find(dimension_type i);

  //! Looks for an element with index i.
  /*!
    \param i
    The index of the desired element.

    \param itr
    It is used as a hint. This method will be faster if the searched element
    is near to \p itr.

    The value of \p itr does not affect the result of this method, as long it
    is a valid %iterator for this row. \p itr may even be end().

    This method takes \f$O(\log n)\f$ time.
    If the distance between \p itr and the searched position is \f$O(1)\f$,
    this method takes \f$O(1)\f$ time.
  */
  iterator find(iterator itr, dimension_type i);

  //! Looks for an element with index i.
  /*!
    \param i
    The index of the desired element.

    If possible, use the find() method that takes a hint %iterator, to improve
    performance.

    This method takes \f$O(\log n)\f$ time.
  */
  const_iterator find(dimension_type i) const;

  //! Looks for an element with index i.
  /*!
    \param i
    The index of the desired element.

    \param itr
    It is used as a hint. This method will be faster if the searched element
    is near to \p itr.

    The value of \p itr does not affect the result of this method, as long it
    is a valid %iterator for this row. \p itr may even be end().

    This method takes \f$O(\log n)\f$ time.
    If the distance between \p itr and the searched position is \f$O(1)\f$,
    this method takes \f$O(1)\f$ time.
  */
  const_iterator find(const_iterator itr, dimension_type i) const;

  //! Lower bound of index i.
  /*!
    \param i
    The index of the desired element.

    \returns an %iterator to the first element with index greater than or
             equal to i.
             If there are no such elements, returns end().

    If possible, use the find() method that takes a hint %iterator, to improve
    performance.

    This method takes \f$O(\log n)\f$ time.
  */
  iterator lower_bound(dimension_type i);

  //! Lower bound of index i.
  /*!
    \param i
    The index of the desired element.

    \param itr
    It is used as a hint. This method will be faster if the searched element
    is near to \p itr.

    \returns an %iterator to the first element with index greater than or
             equal to i.
             If there are no such elements, returns end().

    The value of \p itr does not affect the result of this method, as long it
    is a valid %iterator for this row. \p itr may even be end().

    This method takes \f$O(\log n)\f$ time.
    If the distance between \p itr and the searched position is \f$O(1)\f$,
    this method takes \f$O(1)\f$ time.
  */
  iterator lower_bound(iterator itr, dimension_type i);

  //! Lower bound of index i.
  /*!

    \param i
    The index of the desired element.

    \returns an %iterator to the first element with index greater than or
             equal to i.
             If there are no such elements, returns end().

    If possible, use the find() method that takes a hint %iterator, to improve
    performance.

    This method takes \f$O(\log n)\f$ time.
  */
  const_iterator lower_bound(dimension_type i) const;

  //! Lower bound of index i.
  /*!
    \param i
    The index of the desired element.

    \param itr
    It is used as a hint. This method will be faster if the searched element
    is near to \p itr.

    \returns an %iterator to the first element with index greater than or
             equal to i.
             If there are no such elements, returns end().

    The value of \p itr does not affect the result of this method, as long it
    is a valid %iterator for this row. \p itr may even be end().

    This method takes \f$O(\log n)\f$ time.
    If the distance between \p itr and the searched position is \f$O(1)\f$,
    this method takes \f$O(1)\f$ time.
  */
  const_iterator lower_bound(const_iterator itr, dimension_type i) const;

  //! Equivalent to <CODE>(*this)[i] = x; find(i)</CODE>, but faster.
  /*!
    \param i
    The index of the desired element.

    \param x
    The value that will be associated to the element.

    If possible, use versions of this method that take a hint, to improve
    performance.

    This operation invalidates existing iterators.

    This method takes \f$O(\log^2 n)\f$ amortized time.
  */
  iterator insert(dimension_type i, Coefficient_traits::const_reference x);

  //! Equivalent to <CODE>(*this)[i] = x; find(i)</CODE>, but faster.
  /*!
    \param i
    The index of the desired element.

    \param x
    The value that will be associated to the element.

    \param itr
    It is used as a hint. This method will be faster if the searched element
    is near to \p itr, even faster than <CODE>(*this)[i] = x</CODE>.

    The value of \p itr does not affect the result of this method, as long it
    is a valid %iterator for this row. \p itr may even be end().

    This operation invalidates existing iterators.

    This method takes \f$O(\log^2 n)\f$ amortized time. If the distance
    between \p itr and the searched position is \f$O(1)\f$ and the row already
    contains an element with this index, this method takes \f$O(1)\f$ time.
  */
  iterator insert(iterator itr, dimension_type i,
                  Coefficient_traits::const_reference x);

  //! Equivalent to <CODE>(*this)[i]; find(i)</CODE>, but faster.
  /*!
    \param i
    The index of the desired element.

    If possible, use versions of this method that take a hint, to improve
    performance.

    This operation invalidates existing iterators.

    This method takes \f$O(\log^2 n)\f$ amortized time.
  */
  iterator insert(dimension_type i);

  //! Equivalent to <CODE>(*this)[i]; find(i)</CODE>, but faster.
  /*!
    \param i
    The index of the desired element.

    \param itr
    It is used as a hint. This method will be faster if the searched element
    is near to \p itr, even faster than <CODE>(*this)[i]</CODE>.

    The value of \p itr does not affect the result of this method, as long it
    is a valid %iterator for this row. \p itr may even be end().

    This operation invalidates existing iterators.

    This method takes \f$O(\log^2 n)\f$ amortized time. If the distance
    between \p itr and the searched position is \f$O(1)\f$ and the row already
    contains an element with this index, this method takes \f$O(1)\f$ time.
  */
  iterator insert(iterator itr, dimension_type i);

  //! Swaps the i-th element with the j-th element.
  /*!
    \param i
    The index of an element.

    \param j
    The index of another element.

    This operation invalidates existing iterators.

    This method takes \f$O(\log^2 n)\f$ amortized time.
  */
  void swap_coefficients(dimension_type i, dimension_type j);

  //! Equivalent to swap(i,itr.index()), but it assumes that
  //! lower_bound(i)==itr.
  /*!
    Iterators that pointed to the itr.index()-th element remain valid
    but now point to the i-th element. Other iterators are unaffected.

    This method takes \f$O(1)\f$ time.
  */
  void fast_swap(dimension_type i, iterator itr);

  //! Swaps the element pointed to by i with the element pointed to by j.
  /*!
    \param i
    An %iterator pointing to an element.

    \param j
    An %iterator pointing to another element.

    This method takes \f$O(1)\f$ time.
  */
  void swap_coefficients(iterator i, iterator j);

  //! Resets to zero the value pointed to by i.
  /*!
    \param i
    An %iterator pointing to the element that will be reset (not stored
    anymore).

    By calling this method instead of getting a reference to the value and
    setting it to zero, the element will no longer be stored.

    This operation invalidates existing iterators.

    This method takes \f$O(\log^2 n)\f$ amortized time.
  */
  iterator reset(iterator i);

  //! Resets to zero the values in the range [first,last).
  /*!
    \param first
    An %iterator pointing to the first element to reset.

    \param last
    An %iterator pointing after the last element to reset.

    By calling this method instead of getting a reference to the values and
    setting them to zero, the elements will no longer be stored.

    This operation invalidates existing iterators.

    This method takes \f$O(k*\log^2 n)\f$ amortized time, where k is the
    number of elements in [first,last).
  */
  iterator reset(iterator first, iterator last);

  //! Resets to zero the i-th element.
  /*!
    \param i
    The index of the element to reset.

    By calling this method instead of getting a reference to the value and
    setting it to zero, the element will no longer be stored.

    This operation invalidates existing iterators.

    This method takes \f$O(\log^2 n)\f$ amortized time.
  */
  void reset(dimension_type i);

  //! Resets to zero the elements with index greater than or equal to i.
  /*!
    \param i
    The index of the first element to reset.

    By calling this method instead of getting a reference to the values and
    setting them to zero, the elements will no longer be stored.

    This operation invalidates existing iterators.

    This method takes \f$O(k*\log^2 n)\f$ amortized time, where k is the
    number of elements with index greater than or equal to i.
  */
  void reset_after(dimension_type i);

  //! Normalizes the modulo of coefficients so that they are mutually prime.
  /*!
    Computes the Greatest Common Divisor (GCD) among the elements of the row
    and normalizes them by the GCD itself.

    This method takes \f$O(n)\f$ time.
  */
  void normalize();

  //! Calls g(x[i],y[i]), for each i.
  /*!
    \param y
    The row that will be combined with *this.

    \param f
    A functor that should take a Coefficient&.
    f(c1) must be equivalent to g(c1, 0).

    \param g
    A functor that should take a Coefficient& and a
    Coefficient_traits::const_reference.
    g(c1, c2) must do nothing when c1 is zero.

    This method takes \f$O(n*\log^2 n)\f$ time.

    \note
    The functors will only be called when necessary, assuming the requested
    properties hold.

    \see combine_needs_second
    \see combine
  */
  template <typename Func1, typename Func2>
  void combine_needs_first(const Sparse_Row& y,
                           const Func1& f, const Func2& g);

  //! Calls g(x[i],y[i]), for each i.
  /*!
    \param y
    The row that will be combined with *this.

    \param g
    A functor that should take a Coefficient& and a
    Coefficient_traits::const_reference.
    g(c1, 0) must do nothing, for every c1.

    \param h
    A functor that should take a Coefficient& and a
    Coefficient_traits::const_reference.
    h(c1, c2) must be equivalent to g(c1, c2) when c1 is zero.

    This method takes \f$O(n*\log^2 n)\f$ time.

    \note
    The functors will only be called when necessary, assuming the requested
    properties hold.

    \see combine_needs_first
    \see combine
  */
  template <typename Func1, typename Func2>
  void combine_needs_second(const Sparse_Row& y,
                            const Func1& g, const Func2& h);

  //! Calls g(x[i],y[i]), for each i.
  /*!
    \param y
    The row that will be combined with *this.

    \param f
    A functor that should take a Coefficient&.
    f(c1) must be equivalent to g(c1, 0).

    \param g
    A functor that should take a Coefficient& and a
    Coefficient_traits::const_reference.
    g(c1, c2) must do nothing when both c1 and c2 are zero.

    \param h
    A functor that should take a Coefficient& and a
    Coefficient_traits::const_reference.
    h(c1, c2) must be equivalent to g(c1, c2) when c1 is zero.

    This method takes \f$O(n*\log^2 n)\f$ time.

    \note
    The functors will only be called when necessary, assuming the requested
    properties hold.

    \see combine_needs_first
    \see combine_needs_second
  */
  template <typename Func1, typename Func2, typename Func3>
  void combine(const Sparse_Row& y,
               const Func1& f, const Func2& g, const Func3& h);

  //! Executes <CODE>(*this)[i] = (*this)[i]*coeff1 + y[i]*coeff2</CODE>, for
  //! each i.
  /*!
    \param y
    The row that will be combined with *this.

    \param coeff1
    The coefficient used for elements of *this.
    This must not be 0.

    \param coeff2
    The coefficient used for elements of y.
    This must not be 0.

    This method takes \f$O(n*\log^2 n)\f$ time.

    \note
    The functors will only be called when necessary.
    This method can be implemented in user code, too. It is provided for
    convenience only.

    \see combine_needs_first
    \see combine_needs_second
    \see combine
  */
  void linear_combine(const Sparse_Row& y,
                      Coefficient_traits::const_reference coeff1,
                      Coefficient_traits::const_reference coeff2);

  //! Equivalent to <CODE>(*this)[i] = (*this)[i] * c1 + y[i] * c2</CODE>,
  //! for each i in [start, end).
  /*!
    This method, unlike the other linear_combine() method, detects when
    coeff1==1 and/or coeff2==1 or coeff2==-1 in order to save some work.
  */
  void linear_combine(const Sparse_Row& y,
                      Coefficient_traits::const_reference c1,
                      Coefficient_traits::const_reference c2,
                      dimension_type start, dimension_type end);

  PPL_OUTPUT_DECLARATIONS

  //! Loads the row from an ASCII representation generated using ascii_dump().
  /*!
    \param s
    The stream from which the ASCII representation will be loaded.
  */
  bool ascii_load(std::istream& s);

  //! Returns the size in bytes of the memory managed by \p *this.
  /*!
    This method takes \f$O(n)\f$ time.
  */
  memory_size_type external_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  /*!
    This method is provided for compatibility with Dense_Row.

    This method takes \f$O(n)\f$ time.

    \param capacity
    This parameter is ignored.
  */
  memory_size_type external_memory_in_bytes(dimension_type capacity) const;

  //! Returns the size in bytes of the memory managed by \p *this.
  /*!
    This method takes \f$O(n)\f$ time.
  */
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  /*!
    This method is provided for compatibility with Dense_Row.

    This method takes \f$O(n)\f$ time.

    \param capacity
    This parameter is ignored.
  */
  memory_size_type total_memory_in_bytes(dimension_type capacity) const;

  //! Checks the invariant.
  bool OK() const;

  //! Checks the invariant.
  /*!
    This method is provided for compatibility with Dense_Row.

    \param capacity
    This parameter is ignored.
  */
  bool OK(dimension_type capacity) const;

private:
  //! The tree used to store the elements.
  CO_Tree tree;

  //! The size of the row.
  /*!
    The elements contained in this row have indexes that are less than size_.
  */
  dimension_type size_;
};


namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
/*! \relates Sparse_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void swap(Parma_Polyhedra_Library::Sparse_Row& x,
          Parma_Polyhedra_Library::Sparse_Row& y);

void swap(Parma_Polyhedra_Library::Sparse_Row& x,
          Parma_Polyhedra_Library::Dense_Row& y);

void swap(Parma_Polyhedra_Library::Dense_Row& x,
          Parma_Polyhedra_Library::Sparse_Row& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are equal.
/*! \relates Sparse_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator==(const Sparse_Row& x, const Sparse_Row& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are different.
/*! \relates Sparse_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator!=(const Sparse_Row& x, const Sparse_Row& y);

bool operator==(const Dense_Row& x, const Sparse_Row& y);
bool operator!=(const Dense_Row& x, const Sparse_Row& y);

bool operator==(const Sparse_Row& x, const Dense_Row& y);
bool operator!=(const Sparse_Row& x, const Dense_Row& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Equivalent to <CODE>x[i] = x[i] * c1 + y[i] * c2</CODE>,
//! for each i in [start, end).
/*! \relates Sparse_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void linear_combine(Sparse_Row& x, const Dense_Row& y,
                    Coefficient_traits::const_reference coeff1,
                    Coefficient_traits::const_reference coeff2);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Equivalent to <CODE>x[i] = x[i] * c1 + y[i] * c2</CODE>,
//! for each i in [start, end).
/*! \relates Sparse_Row
  This function detects when coeff1==1 and/or coeff2==1 or coeff2==-1 in
  order to save some work.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void linear_combine(Sparse_Row& x, const Dense_Row& y,
                    Coefficient_traits::const_reference c1,
                    Coefficient_traits::const_reference c2,
                    dimension_type start, dimension_type end);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Equivalent to <CODE>x[i] = x[i] * c1 + y[i] * c2</CODE>,
//! for each i in [start, end).
/*! \relates Sparse_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void linear_combine(Dense_Row& x, const Sparse_Row& y,
                    Coefficient_traits::const_reference coeff1,
                    Coefficient_traits::const_reference coeff2);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Equivalent to <CODE>x[i] = x[i] * c1 + y[i] * c2</CODE>,
//! for each i in [start, end).
/*! \relates Sparse_Row
  This function detects when coeff1==1 and/or coeff2==1 or coeff2==-1 in
  order to save some work.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void linear_combine(Dense_Row& x, const Sparse_Row& y,
                    Coefficient_traits::const_reference c1,
                    Coefficient_traits::const_reference c2,
                    dimension_type start, dimension_type end);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Equivalent to <CODE>x[i] = x[i] * c1 + y[i] * c2</CODE>,
//! for each i in [start, end).
/*! \relates Sparse_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void linear_combine(Sparse_Row& x, const Sparse_Row& y,
                    Coefficient_traits::const_reference coeff1,
                    Coefficient_traits::const_reference coeff2);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Equivalent to <CODE>x[i] = x[i] * c1 + y[i] * c2</CODE>,
//! for each i in [start, end).
/*! \relates Sparse_Row
  This function detects when coeff1==1 and/or coeff2==1 or coeff2==-1 in
  order to save some work.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void linear_combine(Sparse_Row& x, const Sparse_Row& y,
                    Coefficient_traits::const_reference c1,
                    Coefficient_traits::const_reference c2,
                    dimension_type start, dimension_type end);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Sparse_Row_inlines.hh line 1. */
/* Sparse_Row class implementation: inline functions.
*/


#include <algorithm>

namespace Parma_Polyhedra_Library {

inline
Sparse_Row::Sparse_Row(dimension_type n)
  : size_(n) {
  PPL_ASSERT(OK());
}

inline
Sparse_Row::Sparse_Row(dimension_type n, dimension_type)
  : size_(n) {
  PPL_ASSERT(OK());
}

inline
Sparse_Row::Sparse_Row(const Sparse_Row& y, dimension_type)
  : tree(y.tree), size_(y.size_) {
}

inline
Sparse_Row::Sparse_Row(const Sparse_Row& y, dimension_type sz, dimension_type)
  : tree(y.begin(),
         std::distance(y.begin(), y.lower_bound(std::min(y.size(), sz)))),
    size_(sz) {
  PPL_ASSERT(OK());
}

inline void
Sparse_Row::m_swap(Sparse_Row& x) {
  using std::swap;
  swap(tree, x.tree);
  swap(size_, x.size_);
  PPL_ASSERT(OK());
  PPL_ASSERT(x.OK());
}

inline dimension_type
Sparse_Row::size() const {
  return size_;
}

inline dimension_type
Sparse_Row::num_stored_elements() const {
  return tree.size();
}

inline void
Sparse_Row::resize(dimension_type n) {
  if (n < size_) {
    reset_after(n);
  }
  size_ = n;
  PPL_ASSERT(OK());
}

inline void
Sparse_Row::shrink(dimension_type n) {
  PPL_ASSERT(size() >= n);
  resize(n);
}

inline void
Sparse_Row::expand_within_capacity(dimension_type n) {
  PPL_ASSERT(size() <= n);
  resize(n);
}

inline void
Sparse_Row::delete_element_and_shift(dimension_type i) {
  PPL_ASSERT(i < size_);
  tree.erase_element_and_shift_left(i);
  --size_;
  PPL_ASSERT(OK());
}

inline void
Sparse_Row::add_zeroes_and_shift(dimension_type n, dimension_type i) {
  PPL_ASSERT(i <= size_);
  tree.increase_keys_from(i, n);
  size_ += n;
  PPL_ASSERT(OK());
}

inline Sparse_Row::iterator
Sparse_Row::begin() {
  return tree.begin();
}

inline const Sparse_Row::iterator&
Sparse_Row::end() {
  return tree.end();
}

inline Sparse_Row::const_iterator
Sparse_Row::begin() const {
  return tree.cbegin();
}

inline const Sparse_Row::const_iterator&
Sparse_Row::end() const {
  return tree.cend();
}

inline Sparse_Row::const_iterator
Sparse_Row::cbegin() const {
  return tree.cbegin();
}

inline const Sparse_Row::const_iterator&
Sparse_Row::cend() const {
  return tree.cend();
}

inline dimension_type
Sparse_Row::max_size() {
  return CO_Tree::max_size();
}

inline void
Sparse_Row::clear() {
  tree.clear();
}

inline Coefficient&
Sparse_Row::operator[](dimension_type i) {
  PPL_ASSERT(i < size_);
  iterator itr = insert(i);
  return *itr;
}

inline Coefficient_traits::const_reference
Sparse_Row::operator[](dimension_type i) const {
  return get(i);
}

inline Coefficient_traits::const_reference
Sparse_Row::get(dimension_type i) const {
  PPL_ASSERT(i < size_);
  if (tree.empty()) {
    return Coefficient_zero();
  }
  const_iterator itr = find(i);
  if (itr != end()) {
    return *itr;
  }
  else {
    return Coefficient_zero();
  }
}

inline Sparse_Row::iterator
Sparse_Row::find(dimension_type i) {
  PPL_ASSERT(i < size());

  iterator itr = tree.bisect(i);

  if (itr != end() && itr.index() == i) {
    return itr;
  }
  return end();
}

inline Sparse_Row::iterator
Sparse_Row::find(iterator hint, dimension_type i) {
  PPL_ASSERT(i < size());

  iterator itr = tree.bisect_near(hint, i);

  if (itr != end() && itr.index() == i) {
    return itr;
  }
  return end();
}

inline Sparse_Row::const_iterator
Sparse_Row::find(dimension_type i) const {
  PPL_ASSERT(i < size());

  const_iterator itr = tree.bisect(i);

  if (itr != end() && itr.index() == i) {
    return itr;
  }

  return end();
}

inline Sparse_Row::const_iterator
Sparse_Row::find(const_iterator hint, dimension_type i) const {
  PPL_ASSERT(i < size());

  const_iterator itr = tree.bisect_near(hint, i);

  if (itr != end() && itr.index() == i) {
    return itr;
  }
  return end();
}

inline Sparse_Row::iterator
Sparse_Row::lower_bound(dimension_type i) {
  PPL_ASSERT(i <= size());

  iterator itr = tree.bisect(i);

  if (itr == end()) {
    return end();
  }

  if (itr.index() < i) {
    ++itr;
  }

  PPL_ASSERT(itr == end() || itr.index() >= i);

  return itr;
}

inline Sparse_Row::iterator
Sparse_Row::lower_bound(iterator hint, dimension_type i) {
  PPL_ASSERT(i <= size());

  iterator itr = tree.bisect_near(hint, i);

  if (itr == end()) {
    return end();
  }

  if (itr.index() < i) {
    ++itr;
  }

  PPL_ASSERT(itr == end() || itr.index() >= i);

  return itr;
}

inline Sparse_Row::const_iterator
Sparse_Row::lower_bound(dimension_type i) const {
  PPL_ASSERT(i <= size());

  const_iterator itr = tree.bisect(i);

  if (itr == end()) {
    return end();
  }

  if (itr.index() < i) {
    ++itr;
  }

  PPL_ASSERT(itr == end() || itr.index() >= i);

  return itr;
}

inline Sparse_Row::const_iterator
Sparse_Row::lower_bound(const_iterator hint, dimension_type i) const {
  PPL_ASSERT(i <= size());

  const_iterator itr = tree.bisect_near(hint, i);

  if (itr == end()) {
    return end();
  }

  if (itr.index() < i) {
    ++itr;
  }

  PPL_ASSERT(itr == end() || itr.index() >= i);

  return itr;
}

inline Sparse_Row::iterator
Sparse_Row::insert(dimension_type i, Coefficient_traits::const_reference x) {
  PPL_ASSERT(i < size_);
  return tree.insert(i, x);
}

inline Sparse_Row::iterator
Sparse_Row::insert(iterator itr, dimension_type i,
                   Coefficient_traits::const_reference x) {
  PPL_ASSERT(i < size_);
  return tree.insert(itr, i, x);
}

inline Sparse_Row::iterator
Sparse_Row::insert(dimension_type i) {
  PPL_ASSERT(i < size_);
  return tree.insert(i);
}

inline Sparse_Row::iterator
Sparse_Row::insert(iterator itr, dimension_type i) {
  PPL_ASSERT(i < size_);
  return tree.insert(itr, i);
}

inline void
Sparse_Row::swap_coefficients(iterator i, iterator j) {
  PPL_ASSERT(i != end());
  PPL_ASSERT(j != end());
  using std::swap;
  swap(*i, *j);
  PPL_ASSERT(OK());
}

inline void
Sparse_Row::fast_swap(dimension_type i, iterator itr) {
  PPL_ASSERT(lower_bound(i) == itr);
  PPL_ASSERT(itr != end());
  tree.fast_shift(i, itr);
  PPL_ASSERT(OK());
}

inline Sparse_Row::iterator
Sparse_Row::reset(iterator i) {
  iterator res = tree.erase(i);
  PPL_ASSERT(OK());
  return res;
}

inline void
Sparse_Row::reset(dimension_type i) {
  PPL_ASSERT(i < size());

  tree.erase(i);
  PPL_ASSERT(OK());
}

inline memory_size_type
Sparse_Row::external_memory_in_bytes() const {
  return tree.external_memory_in_bytes();
}

inline memory_size_type
Sparse_Row::external_memory_in_bytes(dimension_type /* capacity */) const {
  return external_memory_in_bytes();
}

inline memory_size_type
Sparse_Row::total_memory_in_bytes() const {
  return external_memory_in_bytes() + sizeof(*this);
}

inline memory_size_type
Sparse_Row::total_memory_in_bytes(dimension_type /* capacity */) const {
  return total_memory_in_bytes();
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Sparse_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
inline void
swap(Sparse_Row& x, Sparse_Row& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Sparse_Row_templates.hh line 1. */
/* Sparse_Row class implementation: non-inline template functions.
*/


namespace Parma_Polyhedra_Library {


template <typename Func1, typename Func2>
void
Sparse_Row::combine_needs_first(const Sparse_Row& y,
                                const Func1& f, const Func2& g) {
  if (this == &y) {
    for (iterator i = begin(), i_end = end(); i != i_end; ++i) {
      g(*i, *i);
    }
  }
  else {
    iterator i = begin();
    // This is a const reference to an internal iterator, that is kept valid.
    // If we just stored a copy, that would be invalidated by the calls to
    // reset().
    const iterator& i_end = end();
    const_iterator j = y.begin();
    const_iterator j_end = y.end();
    while (i != i_end && j != j_end) {
      if (i.index() == j.index()) {
        g(*i, *j);
        if (*i == 0) {
          i = reset(i);
        }
        else {
          ++i;
        }
        ++j;
      }
      else
        if (i.index() < j.index()) {
          f(*i);
          if (*i == 0) {
            i = reset(i);
          }
          else {
            ++i;
          }
        }
        else {
          j = y.lower_bound(j, i.index());
        }
    }
    while (i != i_end) {
      f(*i);
      if (*i == 0) {
        i = reset(i);
      }
      else {
        ++i;
      }
    }
  }
}

template <typename Func1, typename Func2>
void
Sparse_Row::combine_needs_second(const Sparse_Row& y,
                                 const Func1& g,
                                 const Func2& /* h */) {
  iterator i = begin();
  for (const_iterator j = y.begin(), j_end = y.end(); j != j_end; ++j) {
    i = insert(i, j.index());
    g(*i, *j);
    if (*i == 0) {
      i = reset(i);
    }
  }
}

template <typename Func1, typename Func2, typename Func3>
void
Sparse_Row::combine(const Sparse_Row& y, const Func1& f,
                    const Func2& g, const Func3& h) {
  if (this == &y) {
    for (iterator i = begin(), i_end = end(); i != i_end; ++i) {
      g(*i, *i);
    }
  }
  else {
    iterator i = begin();
    // This is a const reference to an internal iterator, that is kept valid.
    // If we just stored a copy, that would be invalidated by the calls to
    // reset() and insert().
    const iterator& i_end = end();
    const_iterator j = y.begin();
    const_iterator j_end = y.end();
    while (i != i_end && j != j_end) {
      if (i.index() == j.index()) {
        g(*i, *j);
        if (*i == 0) {
          i = reset(i);
        }
        else {
          ++i;
        }
        ++j;
      }
      else
        if (i.index() < j.index()) {
          f(*i);
          if (*i == 0) {
            i = reset(i);
          }
          else {
            ++i;
          }
        }
        else {
          PPL_ASSERT(i.index() > j.index());
          i = insert(i, j.index());
          h(*i, *j);
          if (*i == 0) {
            i = reset(i);
          }
          else {
            ++i;
          }
          ++j;
        }
    }
    PPL_ASSERT(i == i_end || j == j_end);
    while (i != i_end) {
      f(*i);
      if (*i == 0) {
        i = reset(i);
      }
      else {
        ++i;
      }
    }
    while (j != j_end) {
      i = insert(i, j.index());
      h(*i, *j);
      if (*i == 0) {
        i = reset(i);
      }
      ++j;
    }
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Sparse_Row_defs.hh line 929. */

/* Automatically generated from PPL source file ../src/Linear_Expression_Impl_defs.hh line 34. */
#include <cstddef>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Output operator.
/*! \relates Parma_Polyhedra_Library::Linear_Expression_Impl */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Row>
std::ostream&
operator<<(std::ostream& s, const Linear_Expression_Impl<Row>& e);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A linear expression.
/*! \ingroup PPL_CXX_interface
  An object of the class Linear_Expression_Impl represents the linear
  expression
  \f[
    \sum_{i=0}^{n-1} a_i x_i + b
  \f]
  where \f$n\f$ is the dimension of the vector space,
  each \f$a_i\f$ is the integer coefficient
  of the \f$i\f$-th variable \f$x_i\f$
  and \f$b\f$ is the integer for the inhomogeneous term.

  \par How to build a linear expression.

  Linear expressions are the basic blocks for defining
  both constraints (i.e., linear equalities or inequalities)
  and generators (i.e., lines, rays, points and closure points).
  A full set of functions is defined to provide a convenient interface
  for building complex linear expressions starting from simpler ones
  and from objects of the classes Variable and Coefficient:
  available operators include unary negation,
  binary addition and subtraction,
  as well as multiplication by a Coefficient.
  The space dimension of a linear expression is defined as the maximum
  space dimension of the arguments used to build it:
  in particular, the space dimension of a Variable <CODE>x</CODE>
  is defined as <CODE>x.id()+1</CODE>,
  whereas all the objects of the class Coefficient have space dimension zero.

  \par Example
  The following code builds the linear expression \f$4x - 2y - z + 14\f$,
  having space dimension \f$3\f$:
  \code
  Linear_Expression_Impl e = 4*x - 2*y - z + 14;
  \endcode
  Another way to build the same linear expression is:
  \code
  Linear_Expression_Impl e1 = 4*x;
  Linear_Expression_Impl e2 = 2*y;
  Linear_Expression_Impl e3 = z;
  Linear_Expression_Impl e = Linear_Expression_Impl(14);
  e += e1 - e2 - e3;
  \endcode
  Note that \p e1, \p e2 and \p e3 have space dimension 1, 2 and 3,
  respectively; also, in the fourth line of code, \p e is created
  with space dimension zero and then extended to space dimension 3
  in the fifth line.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Row>
class Parma_Polyhedra_Library::Linear_Expression_Impl
  : public Linear_Expression_Interface {
public:
  //! Default constructor: returns a copy of Linear_Expression_Impl::zero().
  Linear_Expression_Impl();

  //! Ordinary copy constructor.
  Linear_Expression_Impl(const Linear_Expression_Impl& e);

  //! Copy constructor for other row types.
  template <typename Row2>
  Linear_Expression_Impl(const Linear_Expression_Impl<Row2>& e);

  //! Copy constructor from any implementation of Linear_Expression_Interface.
  Linear_Expression_Impl(const Linear_Expression_Interface& e);

  //! Destructor.
  virtual ~Linear_Expression_Impl();

  //! Checks if all the invariants are satisfied.
  virtual bool OK() const;

  /*! \brief
    Builds the linear expression corresponding
    to the inhomogeneous term \p n.
  */
  explicit Linear_Expression_Impl(Coefficient_traits::const_reference n);

  //! Builds the linear expression corresponding to the variable \p v.
  /*!
    \exception std::length_error
    Thrown if the space dimension of \p v exceeds
    <CODE>Linear_Expression_Impl::max_space_dimension()</CODE>.
  */
  Linear_Expression_Impl(Variable v);

  //! Returns the current representation of this linear expression.
  virtual Representation representation() const;

  //! An interface for const iterators on the expression (homogeneous)
  //! coefficients that are nonzero.
  /*!
    These iterators are invalidated by operations that modify the expression.
  */
  class const_iterator: public const_iterator_interface {
  public:
    explicit const_iterator(const Row& row, dimension_type i);

    //! Returns a copy of *this.
    //! This returns a pointer to dynamic-allocated memory. The caller has the
    //! duty to free the memory when it's not needed anymore.
    virtual const_iterator_interface* clone() const;

    //! Navigates to the next nonzero coefficient.
    //! Note that this method does *not* return a reference, to increase
    //! efficiency since it's virtual.
    virtual void operator++();

    //! Navigates to the previous nonzero coefficient.
    //! Note that this method does *not* return a reference, to increase
    //! efficiency since it's virtual.
    virtual void operator--();

    //! Returns the current element.
    virtual reference operator*() const;

    //! Returns the variable of the coefficient pointed to by \c *this.
    /*!
      \returns the variable of the coefficient pointed to by \c *this.
    */
    virtual Variable variable() const;

    //! Compares \p *this with x .
    /*!
      \param x
      The %iterator that will be compared with *this.
    */
    virtual bool operator==(const const_iterator_interface& x) const;

  private:

    void skip_zeroes_forward();
    void skip_zeroes_backward();

    const Row* row;
    typename Row::const_iterator itr;
  };

  //! This returns a pointer to dynamic-allocated memory. The caller has the
  //! duty to free the memory when it's not needed anymore.
  virtual const_iterator_interface* begin() const;

  //! This returns a pointer to dynamic-allocated memory. The caller has the
  //! duty to free the memory when it's not needed anymore.
  virtual const_iterator_interface* end() const;

  //! This returns a pointer to dynamic-allocated memory. The caller has the
  //! duty to free the memory when it's not needed anymore.
  //! Returns (a pointer to) an iterator that points to the first nonzero
  //! coefficient of a variable greater than or equal to v, or at end if no
  //! such coefficient exists.
  virtual const_iterator_interface* lower_bound(Variable v) const;

  //! Returns the maximum space dimension a Linear_Expression_Impl can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  virtual dimension_type space_dimension() const;

  //! Sets the dimension of the vector space enclosing \p *this to \p n .
  virtual void set_space_dimension(dimension_type n);

  //! Returns the coefficient of \p v in \p *this.
  virtual Coefficient_traits::const_reference coefficient(Variable v) const;

  //! Sets the coefficient of \p v in \p *this to \p n.
  virtual void set_coefficient(Variable v,
                               Coefficient_traits::const_reference n);

  //! Returns the inhomogeneous term of \p *this.
  virtual Coefficient_traits::const_reference inhomogeneous_term() const;

  //! Sets the inhomogeneous term of \p *this to \p n.
  virtual void set_inhomogeneous_term(Coefficient_traits::const_reference n);

  //! Linearly combines \p *this with \p y so that the coefficient of \p v
  //! is 0.
  /*!
    \param y
    The expression that will be combined with \p *this object;

    \param v
    The variable whose coefficient has to become \f$0\f$.

    Computes a linear combination of \p *this and \p y having
    the coefficient of variable \p v equal to \f$0\f$. Then it assigns
    the resulting expression to \p *this.

    \p *this and \p y must have the same space dimension.
  */
  virtual void linear_combine(const Linear_Expression_Interface& y, Variable v);

  //! Equivalent to <CODE>*this = *this * c1 + y * c2</CODE>, but assumes that
  //! \p *this and \p y have the same space dimension.
  virtual void linear_combine(const Linear_Expression_Interface& y,
                              Coefficient_traits::const_reference c1,
                              Coefficient_traits::const_reference c2);

  //! Equivalent to <CODE>*this = *this * c1 + y * c2</CODE>.
  //! c1 and c2 may be 0.
  virtual void linear_combine_lax(const Linear_Expression_Interface& y,
                                  Coefficient_traits::const_reference c1,
                                  Coefficient_traits::const_reference c2);

  //! Swaps the coefficients of the variables \p v1 and \p v2 .
  virtual void swap_space_dimensions(Variable v1, Variable v2);

  //! Removes all the specified dimensions from the expression.
  /*!
    The space dimension of the variable with the highest space
    dimension in \p vars must be at most the space dimension
    of \p this.
  */
  virtual void remove_space_dimensions(const Variables_Set& vars);

  //! Shift by \p n positions the coefficients of variables, starting from
  //! the coefficient of \p v. This increases the space dimension by \p n.
  virtual void shift_space_dimensions(Variable v, dimension_type n);

  //! Permutes the space dimensions of the expression.
  /*!
    \param cycle
    A vector representing a cycle of the permutation according to which the
    space dimensions must be rearranged.

    The \p cycle vector represents a cycle of a permutation of space
    dimensions.
    For example, the permutation
    \f$ \{ x_1 \mapsto x_2, x_2 \mapsto x_3, x_3 \mapsto x_1 \}\f$ can be
    represented by the vector containing \f$ x_1, x_2, x_3 \f$.
  */
  virtual void permute_space_dimensions(const std::vector<Variable>& cycle);

  //! Returns <CODE>true</CODE> if and only if \p *this is \f$0\f$.
  virtual bool is_zero() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if all the homogeneous
    terms of \p *this are \f$0\f$.
  */
  virtual bool all_homogeneous_terms_are_zero() const;

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  virtual memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  virtual memory_size_type external_memory_in_bytes() const;

  //! Writes to \p s an ASCII representation of \p *this.
  virtual void ascii_dump(std::ostream& s) const;

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  virtual bool ascii_load(std::istream& s);

  //! Copy constructor with a specified space dimension.
  Linear_Expression_Impl(const Linear_Expression_Interface& e,
                         dimension_type space_dim);

  //! Returns \p true if *this is equal to \p x.
  //! Note that (*this == x) has a completely different meaning.
  virtual bool is_equal_to(const Linear_Expression_Interface& x) const;

  //! Normalizes the modulo of the coefficients and of the inhomogeneous term
  //! so that they are mutually prime.
  /*!
    Computes the Greatest Common Divisor (GCD) among the coefficients
    and the inhomogeneous term and normalizes them by the GCD itself.
  */
  virtual void normalize();

  //! Ensures that the first nonzero homogeneous coefficient is positive,
  //! by negating the row if necessary.
  virtual void sign_normalize();

  /*! \brief
    Negates the elements from index \p first (included)
    to index \p last (excluded).
  */
  virtual void negate(dimension_type first, dimension_type last);

  virtual Linear_Expression_Impl&
  operator+=(Coefficient_traits::const_reference n);
  virtual Linear_Expression_Impl&
  operator-=(Coefficient_traits::const_reference n);

  //! The basic comparison function.
  /*! \relates Linear_Expression_Impl

    \returns
    -1 or -2 if x is less than y, 0 if they are equal and 1 or 2 is y
    is greater. The absolute value of the result is 1 if the difference
    is only in the inhomogeneous terms, 2 otherwise.

    The order is a lexicographic. It starts comparing the variables'
    coefficient, starting from Variable(0), and at the end it compares
    the inhomogeneous terms.
  */
  virtual int compare(const Linear_Expression_Interface& y) const;

  virtual Linear_Expression_Impl&
  operator+=(const Linear_Expression_Interface& e2);
  virtual Linear_Expression_Impl& operator+=(const Variable v);
  virtual Linear_Expression_Impl&
  operator-=(const Linear_Expression_Interface& e2);
  virtual Linear_Expression_Impl& operator-=(const Variable v);
  virtual Linear_Expression_Impl&
  operator*=(Coefficient_traits::const_reference n);
  virtual Linear_Expression_Impl&
  operator/=(Coefficient_traits::const_reference n);

  virtual void negate();

  virtual Linear_Expression_Impl&
  add_mul_assign(Coefficient_traits::const_reference n, const Variable v);

  virtual Linear_Expression_Impl&
  sub_mul_assign(Coefficient_traits::const_reference n, const Variable v);

  virtual void add_mul_assign(Coefficient_traits::const_reference factor,
                              const Linear_Expression_Interface& e2);

  virtual void sub_mul_assign(Coefficient_traits::const_reference factor,
                              const Linear_Expression_Interface& e2);

  virtual void print(std::ostream& s) const;

  /*! \brief
    Returns <CODE>true</CODE> if the coefficient of each variable in
    \p vars[i] is \f$0\f$.
  */
  virtual bool all_zeroes(const Variables_Set& vars) const;

  //! Returns true if there is a variable in [first,last) whose coefficient
  //! is nonzero in both *this and x.
  virtual bool have_a_common_variable(const Linear_Expression_Interface& x,
                                      Variable first, Variable last) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns the i-th coefficient.
  virtual Coefficient_traits::const_reference get(dimension_type i) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Sets the i-th coefficient to n.
  virtual void set(dimension_type i, Coefficient_traits::const_reference n);

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  /*! \brief
    Returns <CODE>true</CODE> if (*this)[i] is \f$0\f$, for each i in
    [start, end).
  */
  virtual bool all_zeroes(dimension_type start, dimension_type end) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  /*! \brief
    Returns the number of zero coefficient in [start, end).
  */
  virtual dimension_type num_zeroes(dimension_type start, dimension_type end) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  /*! \brief
    Returns the gcd of the nonzero coefficients in [start,end). If all the
    coefficients in this range are 0 returns 0.
  */
  virtual Coefficient gcd(dimension_type start, dimension_type end) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  virtual void exact_div_assign(Coefficient_traits::const_reference c,
                                dimension_type start, dimension_type end);

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Equivalent to <CODE>(*this)[i] *= n</CODE>, for each i in [start, end).
  virtual void mul_assign(Coefficient_traits::const_reference n,
                          dimension_type start, dimension_type end);

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Linearly combines \p *this with \p y so that the coefficient of \p v
  //! is 0.
  /*!
    \param y
    The expression that will be combined with \p *this object;

    \param i
    The index of the coefficient that has to become \f$0\f$.

    Computes a linear combination of \p *this and \p y having
    the i-th coefficient equal to \f$0\f$. Then it assigns
    the resulting expression to \p *this.

    \p *this and \p y must have the same space dimension.
  */
  virtual void
  linear_combine(const Linear_Expression_Interface& y, dimension_type i);

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Equivalent to <CODE>(*this)[i] = (*this)[i] * c1 + y[i] * c2</CODE>,
  //! for each i in [start, end).
  virtual void linear_combine(const Linear_Expression_Interface& y,
                              Coefficient_traits::const_reference c1,
                              Coefficient_traits::const_reference c2,
                              dimension_type start, dimension_type end);

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Equivalent to <CODE>(*this)[i] = (*this)[i] * c1 + y[i] * c2</CODE>,
  //! for each i in [start, end). c1 and c2 may be zero.
  virtual void linear_combine_lax(const Linear_Expression_Interface& y,
                                  Coefficient_traits::const_reference c1,
                                  Coefficient_traits::const_reference c2,
                                  dimension_type start, dimension_type end);

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns the index of the last nonzero element, or 0 if there are no
  //! nonzero elements.
  virtual dimension_type last_nonzero() const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  /*! \brief
    Returns <CODE>true</CODE> if each coefficient in [start,end) is *not* in
    \f$0\f$, disregarding coefficients of variables in \p vars.
  */
  virtual bool
  all_zeroes_except(const Variables_Set& vars,
                    dimension_type start, dimension_type end) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Sets results to the sum of (*this)[i]*y[i], for each i in [start,end).
  virtual void
  scalar_product_assign(Coefficient& result,
                        const Linear_Expression_Interface& y,
                        dimension_type start, dimension_type end) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Computes the sign of the sum of (*this)[i]*y[i], for each i in [start,end).
  virtual int
  scalar_product_sign(const Linear_Expression_Interface& y,
                      dimension_type start, dimension_type end) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns the index of the first nonzero element, or \p last if there are no
  //! nonzero elements, considering only elements in [first,last).
  virtual dimension_type
  first_nonzero(dimension_type first, dimension_type last) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns the index of the last nonzero element in [first,last), or last
  //! if there are no nonzero elements.
  virtual dimension_type
  last_nonzero(dimension_type first, dimension_type last) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Removes from the set x all the indexes of nonzero elements of *this.
  virtual void has_a_free_dimension_helper(std::set<dimension_type>& x) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns \p true if (*this)[i] is equal to x[i], for each i in [start,end).
  virtual bool is_equal_to(const Linear_Expression_Interface& x,
                           dimension_type start, dimension_type end) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns \p true if (*this)[i]*c1 is equal to x[i]*c2, for each i in
  //! [start,end).
  virtual bool is_equal_to(const Linear_Expression_Interface& x,
                           Coefficient_traits::const_reference c1,
                           Coefficient_traits::const_reference c2,
                           dimension_type start, dimension_type end) const;

  // NOTE: This method is public, but it is not exposed in
  // Linear_Expression, so that it can be used internally in the PPL,
  // by friends of Linear_Expression.
  //! Sets \p r to a copy of the row that implements \p *this.
  virtual void get_row(Dense_Row& r) const;

  // NOTE: This method is public, but it is not exposed in
  // Linear_Expression, so that it can be used internally in the PPL,
  // by friends of Linear_Expression.
  //! Sets \p r to a copy of the row that implements \p *this.
  virtual void get_row(Sparse_Row& r) const;

  //! Implementation sizing constructor.
  /*!
    The bool parameter is just to avoid problems with the constructor
    Linear_Expression_Impl(Coefficient_traits::const_reference n).
  */
  Linear_Expression_Impl(dimension_type space_dim, bool);

  //! Linearly combines \p *this with \p y so that the coefficient of \p v
  //! is 0.
  /*!
    \param y
    The expression that will be combined with \p *this object;

    \param v
    The variable whose coefficient has to become \f$0\f$.

    Computes a linear combination of \p *this and \p y having
    the coefficient of variable \p v equal to \f$0\f$. Then it assigns
    the resulting expression to \p *this.

    \p *this and \p y must have the same space dimension.
  */
  template <typename Row2>
  void linear_combine(const Linear_Expression_Impl<Row2>& y, Variable v);

  //! Equivalent to <CODE>*this = *this * c1 + y * c2</CODE>, but assumes that
  //! \p *this and \p y have the same space dimension.
  template <typename Row2>
  void linear_combine(const Linear_Expression_Impl<Row2>& y,
                      Coefficient_traits::const_reference c1,
                      Coefficient_traits::const_reference c2);

  //! Equivalent to <CODE>*this = *this * c1 + y * c2</CODE>.
  //! c1 and c2 may be 0.
  template <typename Row2>
  void linear_combine_lax(const Linear_Expression_Impl<Row2>& y,
                          Coefficient_traits::const_reference c1,
                          Coefficient_traits::const_reference c2);

  //! Returns \p true if *this is equal to \p x.
  //! Note that (*this == x) has a completely different meaning.
  template <typename Row2>
  bool is_equal_to(const Linear_Expression_Impl<Row2>& x) const;

  template <typename Row2>
  Linear_Expression_Impl& operator+=(const Linear_Expression_Impl<Row2>& e2);
  template <typename Row2>
  Linear_Expression_Impl& operator-=(const Linear_Expression_Impl<Row2>& e2);

  template <typename Row2>
  Linear_Expression_Impl&
  sub_mul_assign(Coefficient_traits::const_reference n,
                 const Linear_Expression_Impl<Row2>& y,
                 dimension_type start, dimension_type end);

  template <typename Row2>
  void add_mul_assign(Coefficient_traits::const_reference factor,
                      const Linear_Expression_Impl<Row2>& e2);

  template <typename Row2>
  void sub_mul_assign(Coefficient_traits::const_reference factor,
                      const Linear_Expression_Impl<Row2>& e2);

  //! Linearly combines \p *this with \p y so that the coefficient of \p v
  //! is 0.
  /*!
    \param y
    The expression that will be combined with \p *this object;

    \param i
    The index of the coefficient that has to become \f$0\f$.

    Computes a linear combination of \p *this and \p y having
    the i-th coefficient equal to \f$0\f$. Then it assigns
    the resulting expression to \p *this.

    \p *this and \p y must have the same space dimension.
  */
  template <typename Row2>
  void linear_combine(const Linear_Expression_Impl<Row2>& y, dimension_type i);

  //! Equivalent to <CODE>(*this)[i] = (*this)[i] * c1 + y[i] * c2</CODE>,
  //! for each i in [start, end).
  template <typename Row2>
  void linear_combine(const Linear_Expression_Impl<Row2>& y,
                      Coefficient_traits::const_reference c1,
                      Coefficient_traits::const_reference c2,
                      dimension_type start, dimension_type end);

  //! Equivalent to <CODE>(*this)[i] = (*this)[i] * c1 + y[i] * c2</CODE>,
  //! for each i in [start, end). c1 and c2 may be zero.
  template <typename Row2>
  void linear_combine_lax(const Linear_Expression_Impl<Row2>& y,
                          Coefficient_traits::const_reference c1,
                          Coefficient_traits::const_reference c2,
                          dimension_type start, dimension_type end);

  //! The basic comparison function.
  /*! \relates Linear_Expression_Impl

    \returns
    -1 or -2 if x is less than y, 0 if they are equal and 1 or 2 is y
    is greater. The absolute value of the result is 1 if the difference
    is only in the inhomogeneous terms, 2 otherwise.

    The order is a lexicographic. It starts comparing the variables'
    coefficient, starting from Variable(0), and at the end it compares
    the inhomogeneous terms.
  */
  template <typename Row2>
  int compare(const Linear_Expression_Impl<Row2>& y) const;

  //! Sets results to the sum of (*this)[i]*y[i], for each i in [start,end).
  template <typename Row2>
  void
  scalar_product_assign(Coefficient& result,
                        const Linear_Expression_Impl<Row2>& y,
                        dimension_type start, dimension_type end) const;

  //! Computes the sign of the sum of (*this)[i]*y[i],
  //! for each i in [start,end).
  template <typename Row2>
  int scalar_product_sign(const Linear_Expression_Impl<Row2>& y,
                          dimension_type start, dimension_type end) const;

  //! Returns \p true if (*this)[i] is equal to x[i], for each i in [start,end).
  template <typename Row2>
  bool is_equal_to(const Linear_Expression_Impl<Row2>& x,
                   dimension_type start, dimension_type end) const;

  //! Returns \p true if (*this)[i]*c1 is equal to x[i]*c2, for each i in
  //! [start,end).
  template <typename Row2>
  bool is_equal_to(const Linear_Expression_Impl<Row2>& x,
                   Coefficient_traits::const_reference c1,
                   Coefficient_traits::const_reference c2,
                   dimension_type start, dimension_type end) const;

  //! Returns true if there is a variable in [first,last) whose coefficient
  //! is nonzero in both *this and x.
  template <typename Row2>
  bool have_a_common_variable(const Linear_Expression_Impl<Row2>& x,
                              Variable first, Variable last) const;

private:

  void construct(const Linear_Expression_Interface& e);
  void construct(const Linear_Expression_Interface& e,
                 dimension_type space_dim);

  template <typename Row2>
  void construct(const Linear_Expression_Impl<Row2>& e);
  template <typename Row2>
  void construct(const Linear_Expression_Impl<Row2>& e,
                 dimension_type space_dim);

  Row row;

  template <typename Row2>
  friend class Linear_Expression_Impl;

}; // class Parma_Polyhedra_Library::Linear_Expression_Impl


namespace Parma_Polyhedra_Library {

// NOTE: declaring explicit specializations.

template <>
bool
Linear_Expression_Impl<Dense_Row>::OK() const;
template <>
bool
Linear_Expression_Impl<Sparse_Row>::OK() const;

template <>
bool
Linear_Expression_Impl<Dense_Row>::all_homogeneous_terms_are_zero() const;
template <>
bool
Linear_Expression_Impl<Sparse_Row>::all_homogeneous_terms_are_zero() const;

template <>
bool
Linear_Expression_Impl<Dense_Row>::all_zeroes(dimension_type start,
                                              dimension_type end) const;
template <>
bool
Linear_Expression_Impl<Sparse_Row>::all_zeroes(dimension_type start,
                                               dimension_type end) const;

template <>
bool
Linear_Expression_Impl<Dense_Row>
::all_zeroes(const Variables_Set& vars) const;
template <>
bool
Linear_Expression_Impl<Sparse_Row>
::all_zeroes(const Variables_Set& vars) const;

template <>
bool
Linear_Expression_Impl<Dense_Row>
::all_zeroes_except(const Variables_Set& vars,
                    dimension_type start, dimension_type end) const;
template <>
bool
Linear_Expression_Impl<Sparse_Row>
::all_zeroes_except(const Variables_Set& vars,
                    dimension_type start, dimension_type end) const;

template <>
dimension_type
Linear_Expression_Impl<Dense_Row>
::first_nonzero(dimension_type first, dimension_type last) const;
template <>
dimension_type
Linear_Expression_Impl<Sparse_Row>
::first_nonzero(dimension_type first, dimension_type last) const;

template <>
Coefficient
Linear_Expression_Impl<Dense_Row>::gcd(dimension_type start,
                                       dimension_type end) const;
template <>
Coefficient
Linear_Expression_Impl<Sparse_Row>::gcd(dimension_type start,
                                        dimension_type end) const;

template <>
void
Linear_Expression_Impl<Dense_Row>
::has_a_free_dimension_helper(std::set<dimension_type>& x) const;
template <>
void
Linear_Expression_Impl<Sparse_Row>
::has_a_free_dimension_helper(std::set<dimension_type>& x) const;

template <>
template <>
bool
Linear_Expression_Impl<Dense_Row>
::have_a_common_variable(const Linear_Expression_Impl<Dense_Row>& y,
                         Variable first, Variable last) const;
template <>
template <>
bool
Linear_Expression_Impl<Dense_Row>
::have_a_common_variable(const Linear_Expression_Impl<Sparse_Row>& y,
                         Variable first, Variable last) const;
template <>
template <>
bool
Linear_Expression_Impl<Sparse_Row>
::have_a_common_variable(const Linear_Expression_Impl<Dense_Row>& y,
                         Variable first, Variable last) const;
template <>
template <>
bool
Linear_Expression_Impl<Sparse_Row>
::have_a_common_variable(const Linear_Expression_Impl<Sparse_Row>& y,
                         Variable first, Variable last) const;

template <>
bool
Linear_Expression_Impl<Dense_Row>::is_zero() const;
template <>
bool
Linear_Expression_Impl<Sparse_Row>::is_zero() const;

template <>
dimension_type
Linear_Expression_Impl<Dense_Row>::last_nonzero() const;
template <>
dimension_type
Linear_Expression_Impl<Sparse_Row>::last_nonzero() const;

template <>
dimension_type
Linear_Expression_Impl<Dense_Row>
::last_nonzero(dimension_type first, dimension_type last) const;
template <>
dimension_type
Linear_Expression_Impl<Sparse_Row>
::last_nonzero(dimension_type first, dimension_type last) const;

template <>
dimension_type
Linear_Expression_Impl<Dense_Row>::num_zeroes(dimension_type start,
                                              dimension_type end) const;
template <>
dimension_type
Linear_Expression_Impl<Sparse_Row>::num_zeroes(dimension_type start,
                                               dimension_type end) const;

template <>
void
Linear_Expression_Impl<Dense_Row>
::remove_space_dimensions(const Variables_Set& vars);
template <>
void
Linear_Expression_Impl<Sparse_Row>
::remove_space_dimensions(const Variables_Set& vars);

template <>
Representation
Linear_Expression_Impl<Dense_Row>::representation() const;
template <>
Representation
Linear_Expression_Impl<Sparse_Row>::representation() const;

template <>
void
Linear_Expression_Impl<Dense_Row>::const_iterator::skip_zeroes_backward();
template <>
void
Linear_Expression_Impl<Sparse_Row>::const_iterator::skip_zeroes_backward();

template <>
void
Linear_Expression_Impl<Dense_Row>::const_iterator::skip_zeroes_forward();
template <>
void
Linear_Expression_Impl<Sparse_Row>::const_iterator::skip_zeroes_forward();

} // namespace Parma_Polyhedra_Library


/* Automatically generated from PPL source file ../src/Linear_Expression_Impl_inlines.hh line 1. */
/* Linear_Expression_Impl class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/math_utilities_defs.hh line 1. */
/* Declarations of some math utility functions.
*/


/* Automatically generated from PPL source file ../src/math_utilities_defs.hh line 29. */
#include <gmpxx.h>

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Extract the numerator and denominator components of \p from.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, void>::type
numer_denom(const T& from,
            Coefficient& numer, Coefficient& denom);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Divides \p x by \p y into \p to, rounding the result towards plus infinity.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, void>::type
div_round_up(T& to,
             Coefficient_traits::const_reference x,
             Coefficient_traits::const_reference y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Assigns to \p x the minimum between \p x and \p y.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename N>
void
min_assign(N& x, const N& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Assigns to \p x the maximum between \p x and \p y.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename N>
void
max_assign(N& x, const N& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x is an even number.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_even(const T& x);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \f$x = -y\f$.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_additive_inverse(const T& x, const T& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  If \f$g\f$ is the GCD of \p x and \p y, the values of \p x and \p y
  divided by \f$g\f$ are assigned to \p n_x and \p n_y, respectively.

  \note
  \p x and \p n_x may be the same object and likewise for
  \p y and \p n_y.  Any other aliasing results in undefined behavior.
*/
#endif
void
normalize2(Coefficient_traits::const_reference x,
           Coefficient_traits::const_reference y,
           Coefficient& n_x, Coefficient& n_y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x is in canonical form.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool
is_canonical(const mpq_class& x);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns a mask for the lowest \p n bits,
#endif
template <typename T>
T
low_bits_mask(unsigned n);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/math_utilities_inlines.hh line 1. */
/* Implementation of some math utility functions: inline functions.
*/


/* Automatically generated from PPL source file ../src/math_utilities_inlines.hh line 29. */
#include <limits>

namespace Parma_Polyhedra_Library {

inline void
normalize2(Coefficient_traits::const_reference x,
           Coefficient_traits::const_reference y,
           Coefficient& n_x, Coefficient& n_y) {
  PPL_DIRTY_TEMP_COEFFICIENT(gcd);
  gcd_assign(gcd, x, y);
  exact_div_assign(n_x, x, gcd);
  exact_div_assign(n_y, y, gcd);
}

template <typename T>
inline T
low_bits_mask(const unsigned n) {
  PPL_ASSERT(n < unsigned(std::numeric_limits<T>::digits));
  return ~((~static_cast<T>(0)) << n);
}

template <typename T>
inline typename Enable_If<Is_Native_Or_Checked<T>::value, void>::type
numer_denom(const T& from,
            Coefficient& numer, Coefficient& denom) {
  PPL_ASSERT(!is_not_a_number(from)
         && !is_minus_infinity(from)
         && !is_plus_infinity(from));
  PPL_DIRTY_TEMP(mpq_class, q);
  assign_r(q, from, ROUND_NOT_NEEDED);
  numer = q.get_num();
  denom = q.get_den();
}

template <typename T>
inline typename Enable_If<Is_Native_Or_Checked<T>::value, void>::type
div_round_up(T& to,
             Coefficient_traits::const_reference x,
             Coefficient_traits::const_reference y) {
  PPL_DIRTY_TEMP(mpq_class, q_x);
  PPL_DIRTY_TEMP(mpq_class, q_y);
  // Note: this code assumes that a Coefficient is always convertible
  // to an mpq_class without loss of precision.
  assign_r(q_x, x, ROUND_NOT_NEEDED);
  assign_r(q_y, y, ROUND_NOT_NEEDED);
  div_assign_r(q_x, q_x, q_y, ROUND_NOT_NEEDED);
  assign_r(to, q_x, ROUND_UP);
}

template <typename N>
inline void
min_assign(N& x, const N& y) {
  if (x > y) {
    x = y;
  }
}

template <typename N>
inline void
max_assign(N& x, const N& y) {
  if (x < y) {
    x = y;
  }
}

template <typename T>
inline typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_even(const T& x) {
  T mod;
  return umod_2exp_assign_r(mod, x, 1, ROUND_DIRECT | ROUND_STRICT_RELATION) == V_EQ
    && mod == 0;
}

template <typename T>
inline typename Enable_If<Is_Native_Or_Checked<T>::value, bool>::type
is_additive_inverse(const T& x, const T& y) {
  T negated_x;
  return neg_assign_r(negated_x, x, ROUND_DIRECT | ROUND_STRICT_RELATION) == V_EQ
    && negated_x == y;
}

inline bool
is_canonical(const mpq_class& x) {
  if (x.get_den() <= 0) {
    return false;
  }
  PPL_DIRTY_TEMP(mpq_class, temp);
  temp = x;
  temp.canonicalize();
  return temp.get_num() == x.get_num();
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/math_utilities_defs.hh line 109. */

/* Automatically generated from PPL source file ../src/Linear_Expression_Impl_inlines.hh line 28. */
#include <stdexcept>

namespace Parma_Polyhedra_Library {

template <typename Row>
inline dimension_type
Linear_Expression_Impl<Row>::max_space_dimension() {
  return Row::max_size() - 1;
}

template <typename Row>
inline
Linear_Expression_Impl<Row>::Linear_Expression_Impl()
  : row(1) {
  PPL_ASSERT(OK());
}

template <typename Row>
inline
Linear_Expression_Impl<Row>
::Linear_Expression_Impl(dimension_type space_dim, bool)
  : row(space_dim + 1) {
  PPL_ASSERT(OK());
}

template <typename Row>
inline
Linear_Expression_Impl<Row>::~Linear_Expression_Impl() {
}

template <typename Row>
inline
Linear_Expression_Impl<Row>
::Linear_Expression_Impl(Coefficient_traits::const_reference n)
  : row(1) {
  if (n != 0) {
    row.insert(0, n);
  }
  PPL_ASSERT(OK());
}

template <typename Row>
inline dimension_type
Linear_Expression_Impl<Row>::space_dimension() const {
  return row.size() - 1;
}

template <typename Row>
inline void
Linear_Expression_Impl<Row>::set_space_dimension(dimension_type n) {
  row.resize(n + 1);
  PPL_ASSERT(OK());
}

template <typename Row>
inline Coefficient_traits::const_reference
Linear_Expression_Impl<Row>::coefficient(Variable v) const {
  if (v.space_dimension() > space_dimension()) {
    return Coefficient_zero();
  }
  return row.get(v.id() + 1);
}

template <typename Row>
inline void
Linear_Expression_Impl<Row>
::set_coefficient(Variable v, Coefficient_traits::const_reference n) {
  PPL_ASSERT(v.space_dimension() <= space_dimension());
  const dimension_type i = v.space_dimension();
  if (n == 0) {
    row.reset(i);
  }
  else {
    row.insert(i, n);
  }
  PPL_ASSERT(OK());
}

template <typename Row>
inline Coefficient_traits::const_reference
Linear_Expression_Impl<Row>::inhomogeneous_term() const {
  return row.get(0);
}

template <typename Row>
inline void
Linear_Expression_Impl<Row>
::set_inhomogeneous_term(Coefficient_traits::const_reference n) {
  if (n == 0) {
    row.reset(0);
  }
  else {
    row.insert(0, n);
  }
  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Linear_Expression_Impl<Row>::swap_space_dimensions(Variable v1, Variable v2) {
  row.swap_coefficients(v1.space_dimension(), v2.space_dimension());
  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Linear_Expression_Impl<Row>::shift_space_dimensions(Variable v,
                                                    dimension_type n) {
  row.add_zeroes_and_shift(n, v.space_dimension());
  PPL_ASSERT(OK());
}

template <typename Row>
inline memory_size_type
Linear_Expression_Impl<Row>::external_memory_in_bytes() const {
  return row.external_memory_in_bytes();
}

template <typename Row>
inline memory_size_type
Linear_Expression_Impl<Row>::total_memory_in_bytes() const {
  return external_memory_in_bytes() + sizeof(*this);
}

template <typename Row>
inline Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>::operator+=(Coefficient_traits::const_reference n) {
  typename Row::iterator itr = row.insert(0);
  (*itr) += n;
  if (*itr == 0) {
    row.reset(itr);
  }
  PPL_ASSERT(OK());
  return *this;
}

template <typename Row>
inline Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>::operator-=(Coefficient_traits::const_reference n) {
  typename Row::iterator itr = row.insert(0);
  (*itr) -= n;
  if (*itr == 0) {
    row.reset(itr);
  }
  PPL_ASSERT(OK());
  return *this;
}

template <typename Row>
inline void
Linear_Expression_Impl<Row>::normalize() {
  row.normalize();
  PPL_ASSERT(OK());
}

template <>
inline bool
Linear_Expression_Impl<Sparse_Row>::is_zero() const {
  return row.num_stored_elements() == 0;
}

template <>
inline bool
Linear_Expression_Impl<Sparse_Row>::all_homogeneous_terms_are_zero() const {
  return row.lower_bound(1) == row.end();
}

template <>
inline bool
Linear_Expression_Impl<Sparse_Row>::all_zeroes(dimension_type start,
                                               dimension_type end) const {
  return row.lower_bound(start) == row.lower_bound(end);
}

template <>
inline dimension_type
Linear_Expression_Impl<Sparse_Row>::num_zeroes(dimension_type start,
                                               dimension_type end) const {
  PPL_ASSERT(start <= end);
  return (end - start)
    - std::distance(row.lower_bound(start), row.lower_bound(end));
}

template <>
inline dimension_type
Linear_Expression_Impl<Sparse_Row>::last_nonzero() const {
  if (row.num_stored_elements() == 0) {
    return 0;
  }
  Sparse_Row::const_iterator i = row.end();
  --i;
  return i.index();
}

template <>
inline dimension_type
Linear_Expression_Impl<Sparse_Row>
::first_nonzero(dimension_type first, dimension_type last) const {
  PPL_ASSERT(first <= last);
  PPL_ASSERT(last <= row.size());
  Sparse_Row::const_iterator i = row.lower_bound(first);

  if (i != row.end() && i.index() < last) {
    return i.index();
  }
  else {
    return last;
  }
}

template <>
inline dimension_type
Linear_Expression_Impl<Sparse_Row>
::last_nonzero(dimension_type first, dimension_type last) const {
  PPL_ASSERT(first <= last);
  PPL_ASSERT(last <= row.size());
  Sparse_Row::const_iterator itr1 = row.lower_bound(first);
  Sparse_Row::const_iterator itr2 = row.lower_bound(last);

  if (itr1 == itr2) {
    return last;
  }

  --itr2;
  return itr2.index();
}

template <>
inline Representation
Linear_Expression_Impl<Dense_Row>::representation() const {
  return DENSE;
}

template <>
inline Representation
Linear_Expression_Impl<Sparse_Row>::representation() const {
  return SPARSE;
}

template <>
inline void
Linear_Expression_Impl<Sparse_Row>::const_iterator
::skip_zeroes_forward() {
  // Nothing to do.
}

template <>
inline void
Linear_Expression_Impl<Sparse_Row>::const_iterator
::skip_zeroes_backward() {
  // Nothing to do.
}

namespace IO_Operators {

template <typename Row>
inline std::ostream&
operator<<(std::ostream& s, const Linear_Expression_Impl<Row>& e) {
  e.print(s);
  return s;
}

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Expression_Impl_templates.hh line 1. */
/* Linear_Expression_Impl class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/Linear_Expression_Impl_templates.hh line 29. */

/* Automatically generated from PPL source file ../src/Constraint_defs.hh line 1. */
/* Constraint class declaration.
*/


/* Automatically generated from PPL source file ../src/Constraint_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Constraint;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Constraint_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/Congruence_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Congruence;

}

/* Automatically generated from PPL source file ../src/Polyhedron_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Polyhedron;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/termination_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Termination_Helpers;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Octagonal_Shape_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename T>
class Octagonal_Shape;

class Octagonal_Shape_Helper;

}

/* Automatically generated from PPL source file ../src/Grid_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Grid;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Constraint_defs.hh line 35. */

/* Automatically generated from PPL source file ../src/Linear_Expression_defs.hh line 1. */
/* Linear_Expression class declaration.
*/


/* Automatically generated from PPL source file ../src/Linear_Expression_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/Generator_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Generator;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Grid_Generator_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Grid_Generator;

}

/* Automatically generated from PPL source file ../src/Linear_System_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename Row>
class Linear_System;

template <typename Row>
class Linear_System_With_Bit_Matrix_iterator;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Constraint_System_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Constraint_System;
class Constraint_System_const_iterator;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Congruence_System_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Congruence_System;

}

/* Automatically generated from PPL source file ../src/PIP_Problem_types.hh line 1. */


namespace Parma_Polyhedra_Library {

//! Possible outcomes of the PIP_Problem solver.
/*! \ingroup PPL_CXX_interface */
enum PIP_Problem_Status {
  //! The problem is unfeasible.
  UNFEASIBLE_PIP_PROBLEM,
  //! The problem has an optimal solution.
  OPTIMIZED_PIP_PROBLEM
};

class PIP_Problem;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/BHRZ03_Certificate_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class BHRZ03_Certificate;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Scalar_Products_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Scalar_Products;
class Topology_Adjusted_Scalar_Product_Sign;
class Topology_Adjusted_Scalar_Product_Assign;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/MIP_Problem_types.hh line 1. */


namespace Parma_Polyhedra_Library {

//! Possible outcomes of the MIP_Problem solver.
/*! \ingroup PPL_CXX_interface */
enum MIP_Problem_Status {
  //! The problem is unfeasible.
  UNFEASIBLE_MIP_PROBLEM,
  //! The problem is unbounded.
  UNBOUNDED_MIP_PROBLEM,
  //! The problem has an optimal solution.
  OPTIMIZED_MIP_PROBLEM
};

class MIP_Problem;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/BD_Shape_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename T>
class BD_Shape;

class BD_Shape_Helpers;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Expression_defs.hh line 47. */

/* Automatically generated from PPL source file ../src/Expression_Adapter_defs.hh line 1. */
/* Expression_Adapter class declaration.
*/


/* Automatically generated from PPL source file ../src/Expression_Adapter_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Expression_Adapter_Base;

template <typename T>
class Expression_Adapter;

template <typename T>
class Expression_Adapter_Transparent;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Expression_Adapter_defs.hh line 32. */

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Adapters' base type (for template meta-programming).
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
class Parma_Polyhedra_Library::Expression_Adapter_Base {
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! An adapter for Linear_Expression objects.
/*!
  The adapters are meant to provide read-only, customized access to the
  Linear_Expression members in Constraint, Generator, Congruence and
  Grid_Generator objects. They typically implement the user-level view
  of these expressions.

  \note
  A few methods implement low-level access routines and will take
  bare indexes as arguments (rather than Variable objects):
  when such a bare index \c i is zero, the inhomogeneous term is meant;
  when the bare index \c i is greater than zero, the coefficient of the
  variable having id <CODE>i - 1</CODE> is meant.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Parma_Polyhedra_Library::Expression_Adapter
  : public Expression_Adapter_Base {
public:
  //! The type of this object.
  typedef Expression_Adapter<T> const_reference;
  //! The type obtained by one-level unwrapping.
  typedef typename T::const_reference inner_type;
  //! The raw, completely unwrapped type.
  typedef typename T::raw_type raw_type;

  //! Returns an adapter after one-level unwrapping.
  inner_type inner() const;

  //! The type of const iterators on coefficients.
  typedef typename raw_type::const_iterator const_iterator;

  //! Returns the current representation of \p *this.
  Representation representation() const;

  //! Iterator pointing to the first nonzero variable coefficient.
  const_iterator begin() const;

  //! Iterator pointing after the last nonzero variable coefficient.
  const_iterator end() const;

  //! Iterator pointing to the first nonzero variable coefficient
  //! of a variable bigger than or equal to \p v.
  const_iterator lower_bound(Variable v) const;

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  //! Returns the coefficient of \p v in \p *this.
  Coefficient_traits::const_reference coefficient(Variable v) const;

  //! Returns the inhomogeneous term of \p *this.
  Coefficient_traits::const_reference inhomogeneous_term() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is zero.
  bool is_zero() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if all the homogeneous
    terms of \p *this are zero.
  */
  bool all_homogeneous_terms_are_zero() const;

  /*! \brief Returns \p true if \p *this is equal to \p y.

    Note that <CODE>(*this == y)</CODE> has a completely different meaning.
  */
  template <typename Expression>
  bool is_equal_to(const Expression& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if the coefficient of each variable in
    \p vars is zero.
  */
  bool all_zeroes(const Variables_Set& vars) const;

  //! Returns the \p i -th coefficient.
  Coefficient_traits::const_reference get(dimension_type i) const;

  //! Returns the coefficient of variable \p v.
  Coefficient_traits::const_reference get(Variable v) const;

  /*! \brief
    Returns <CODE>true</CODE> if (*this)[i] is zero,
    for each i in [start, end).
  */
  bool all_zeroes(dimension_type start, dimension_type end) const;

  //! Returns the number of zero coefficient in [start, end).
  dimension_type num_zeroes(dimension_type start, dimension_type end) const;

  /*! \brief
    Returns the gcd of the nonzero coefficients in [start,end).
    Returns zero if all the coefficients in the range are zero.
  */
  Coefficient gcd(dimension_type start, dimension_type end) const;

  //! Returns the index of the last nonzero element, or zero if there are no
  //! nonzero elements.
  dimension_type last_nonzero() const;

  //! Returns the index of the last nonzero element in [first,last),
  //! or \p last if there are no nonzero elements.
  dimension_type last_nonzero(dimension_type first, dimension_type last) const;

  //! Returns the index of the first nonzero element, or \p last if there
  //! are no nonzero elements, considering only elements in [first,last).
  dimension_type first_nonzero(dimension_type first, dimension_type last) const;

  /*! \brief
    Returns <CODE>true</CODE> if all coefficients in [start,end),
    except those corresponding to variables in \p vars, are zero.
  */
  bool all_zeroes_except(const Variables_Set& vars,
                         dimension_type start, dimension_type end) const;

  //! Removes from set \p x all the indexes of nonzero elements in \p *this.
  void has_a_free_dimension_helper(std::set<dimension_type>& x) const;

  //! Returns \c true if <CODE>(*this)[i]</CODE> is equal to <CODE>y[i]</CODE>,
  //! for each i in [start,end).
  template <typename Expression>
  bool is_equal_to(const Expression& y,
                   dimension_type start, dimension_type end) const;

  //! Returns \c true if <CODE>(*this)[i]*c1</CODE> is equal to
  //! <CODE>y[i]*c2</CODE>, for each i in [start,end).
  template <typename Expression>
  bool is_equal_to(const Expression& y,
                   Coefficient_traits::const_reference c1,
                   Coefficient_traits::const_reference c2,
                   dimension_type start, dimension_type end) const;

  //! Sets \p r to a copy of the row as adapted by \p *this.
  void get_row(Dense_Row& r) const;

  //! Sets \p r to a copy of the row as adapted by \p *this.
  void get_row(Sparse_Row& r) const;

  //! Returns \c true if there is a variable in [first,last) whose coefficient
  //! is nonzero in both \p *this and \p y.
  template <typename Expression>
  bool have_a_common_variable(const Expression& y,
                              Variable first, Variable last) const;

protected:
  //! Constructor.
  explicit Expression_Adapter(const raw_type& expr);
  //! The raw, completely unwrapped object subject to adaptation.
  const raw_type& raw_;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A transparent adapter for Linear_Expression objects.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Parma_Polyhedra_Library::Expression_Adapter_Transparent
  : public Expression_Adapter<T> {
  typedef Expression_Adapter<T> base_type;
public:
  //! The type of this object.
  typedef Expression_Adapter_Transparent<T> const_reference;
  //! The type obtained by one-level unwrapping.
  typedef typename base_type::inner_type inner_type;
  //! The raw, completely unwrapped type.
  typedef typename base_type::raw_type raw_type;

  //! The type of const iterators on coefficients.
  typedef typename base_type::const_iterator const_iterator;

  //! Constructor.
  explicit Expression_Adapter_Transparent(const raw_type& expr);
};

/* Automatically generated from PPL source file ../src/Expression_Adapter_inlines.hh line 1. */
/* Expression_Adapter class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Expression_Adapter_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename T>
inline
Expression_Adapter<T>::Expression_Adapter(const raw_type& expr)
  : raw_(expr) {
}

template <typename T>
inline typename Expression_Adapter<T>::inner_type
Expression_Adapter<T>::inner() const {
  return inner_type(raw_);
}

template <typename T>
inline Representation
Expression_Adapter<T>::representation() const {
  return inner().representation();
}

template <typename T>
inline typename Expression_Adapter<T>::const_iterator
Expression_Adapter<T>::begin() const {
  return inner().begin();
}

template <typename T>
inline typename Expression_Adapter<T>::const_iterator
Expression_Adapter<T>::end() const {
  return inner().end();
}

template <typename T>
inline typename Expression_Adapter<T>::const_iterator
Expression_Adapter<T>::lower_bound(Variable v) const {
  return inner().lower_bound(v);
}

template <typename T>
inline dimension_type
Expression_Adapter<T>::space_dimension() const {
  return inner().space_dimension();
}

template <typename T>
inline Coefficient_traits::const_reference
Expression_Adapter<T>::coefficient(Variable v) const {
  return inner().coefficient(v);
}

template <typename T>
inline Coefficient_traits::const_reference
Expression_Adapter<T>::inhomogeneous_term() const {
  return inner().inhomogeneous_term();
}

template <typename T>
inline bool
Expression_Adapter<T>::is_zero() const {
  return inner().is_zero();
}

template <typename T>
inline bool
Expression_Adapter<T>::all_homogeneous_terms_are_zero() const {
  return inner().all_homogeneous_terms_are_zero();
}

template <typename T>
template <typename Expression>
inline bool
Expression_Adapter<T>::is_equal_to(const Expression& y) const {
  return inner().is_equal_to(y);
}

template <typename T>
inline bool
Expression_Adapter<T>
::all_zeroes(const Variables_Set& vars) const {
  return inner().all_zeroes(vars);
}

template <typename T>
inline Coefficient_traits::const_reference
Expression_Adapter<T>::get(dimension_type i) const {
  return inner().get(i);
}

template <typename T>
inline Coefficient_traits::const_reference
Expression_Adapter<T>::get(Variable v) const {
  return inner().get(v);
}

template <typename T>
inline bool
Expression_Adapter<T>::all_zeroes(dimension_type start,
                                  dimension_type end) const {
  return inner().all_zeroes(start, end);
}

template <typename T>
inline dimension_type
Expression_Adapter<T>::num_zeroes(dimension_type start,
                                  dimension_type end) const {
  return inner().num_zeroes(start, end);
}

template <typename T>
inline Coefficient
Expression_Adapter<T>::gcd(dimension_type start,
                           dimension_type end) const {
  return inner().gcd(start, end);
}

template <typename T>
inline dimension_type
Expression_Adapter<T>::last_nonzero() const {
  return inner().last_nonzero();
}

template <typename T>
inline dimension_type
Expression_Adapter<T>::last_nonzero(dimension_type first,
                                    dimension_type last) const {
  return inner().last_nonzero(first, last);
}

template <typename T>
inline dimension_type
Expression_Adapter<T>::first_nonzero(dimension_type first,
                                     dimension_type last) const {
  return inner().first_nonzero(first, last);
}

template <typename T>
inline bool
Expression_Adapter<T>
::all_zeroes_except(const Variables_Set& vars,
                    dimension_type start, dimension_type end) const {
  return inner().all_zeroes_except(vars, start, end);
}

template <typename T>
inline void
Expression_Adapter<T>
::has_a_free_dimension_helper(std::set<dimension_type>& x) const {
  inner().has_a_free_dimension_helper(x);
}

template <typename T>
template <typename Expression>
inline bool
Expression_Adapter<T>
::is_equal_to(const Expression& y,
              dimension_type start, dimension_type end) const {
  return inner().is_equal_to(y, start, end);
}

template <typename T>
template <typename Expression>
inline bool
Expression_Adapter<T>
::is_equal_to(const Expression& y,
              Coefficient_traits::const_reference c1,
              Coefficient_traits::const_reference c2,
              dimension_type start, dimension_type end) const {
  return inner().is_equal_to(y, c1, c2, start, end);
}

template <typename T>
inline void
Expression_Adapter<T>::get_row(Dense_Row& r) const {
  inner().get_row(r);
}

template <typename T>
inline void
Expression_Adapter<T>::get_row(Sparse_Row& r) const {
  inner().get_row(r);
}

template <typename T>
template <typename Expression>
inline bool
Expression_Adapter<T>
::have_a_common_variable(const Expression& y,
                         Variable first, Variable last) const {
  return inner().have_a_common_variable(y, first, last);
}

template <typename T>
inline
Expression_Adapter_Transparent<T>
::Expression_Adapter_Transparent(const raw_type& expr)
  : base_type(expr) {
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Expression_Adapter_defs.hh line 215. */

/* Automatically generated from PPL source file ../src/Expression_Hide_Inhomo_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename T>
class Expression_Hide_Inhomo;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Expression_Hide_Last_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename T>
class Expression_Hide_Last;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Expression_defs.hh line 51. */

/* Automatically generated from PPL source file ../src/Linear_Expression_defs.hh line 54. */
#include <cstddef>

namespace Parma_Polyhedra_Library {

// Put them in the namespace here to declare them friend later.

//! Returns the linear expression \p e1 + \p e2.
/*! \relates Linear_Expression */
Linear_Expression
operator+(const Linear_Expression& e1, const Linear_Expression& e2);

//! Returns the linear expression \p v + \p w.
/*! \relates Linear_Expression */
Linear_Expression
operator+(Variable v, Variable w);

//! Returns the linear expression \p v + \p e.
/*! \relates Linear_Expression */
Linear_Expression
operator+(Variable v, const Linear_Expression& e);

//! Returns the linear expression \p e + \p v.
/*! \relates Linear_Expression */
Linear_Expression
operator+(const Linear_Expression& e, Variable v);

//! Returns the linear expression \p n + \p e.
/*! \relates Linear_Expression */
Linear_Expression
operator+(Coefficient_traits::const_reference n, const Linear_Expression& e);

//! Returns the linear expression \p e + \p n.
/*! \relates Linear_Expression */
Linear_Expression
operator+(const Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns the linear expression \p e.
/*! \relates Linear_Expression */
Linear_Expression
operator+(const Linear_Expression& e);

//! Returns the linear expression - \p e.
/*! \relates Linear_Expression */
Linear_Expression
operator-(const Linear_Expression& e);

//! Returns the linear expression \p e1 - \p e2.
/*! \relates Linear_Expression */
Linear_Expression
operator-(const Linear_Expression& e1, const Linear_Expression& e2);

//! Returns the linear expression \p v - \p w.
/*! \relates Linear_Expression */
Linear_Expression
operator-(Variable v, Variable w);

//! Returns the linear expression \p v - \p e.
/*! \relates Linear_Expression */
Linear_Expression
operator-(Variable v, const Linear_Expression& e);

//! Returns the linear expression \p e - \p v.
/*! \relates Linear_Expression */
Linear_Expression
operator-(const Linear_Expression& e, Variable v);

//! Returns the linear expression \p n - \p e.
/*! \relates Linear_Expression */
Linear_Expression
operator-(Coefficient_traits::const_reference n, const Linear_Expression& e);

//! Returns the linear expression \p e - \p n.
/*! \relates Linear_Expression */
Linear_Expression
operator-(const Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns the linear expression \p n * \p e.
/*! \relates Linear_Expression */
Linear_Expression
operator*(Coefficient_traits::const_reference n, const Linear_Expression& e);

//! Returns the linear expression \p e * \p n.
/*! \relates Linear_Expression */
Linear_Expression
operator*(const Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns the linear expression \p e1 + \p e2 and assigns it to \p e1.
/*! \relates Linear_Expression */
Linear_Expression&
operator+=(Linear_Expression& e1, const Linear_Expression& e2);

//! Returns the linear expression \p e + \p v and assigns it to \p e.
/*! \relates Linear_Expression
  \exception std::length_error
  Thrown if the space dimension of \p v exceeds
  <CODE>Linear_Expression::max_space_dimension()</CODE>.
 */
Linear_Expression&
operator+=(Linear_Expression& e, Variable v);

//! Returns the linear expression \p e + \p n and assigns it to \p e.
/*! \relates Linear_Expression */
Linear_Expression&
operator+=(Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns the linear expression \p e1 - \p e2 and assigns it to \p e1.
/*! \relates Linear_Expression */
Linear_Expression&
operator-=(Linear_Expression& e1, const Linear_Expression& e2);

//! Returns the linear expression \p e - \p v and assigns it to \p e.
/*! \relates Linear_Expression
  \exception std::length_error
  Thrown if the space dimension of \p v exceeds
  <CODE>Linear_Expression::max_space_dimension()</CODE>.
 */
Linear_Expression&
operator-=(Linear_Expression& e, Variable v);

//! Returns the linear expression \p e - \p n and assigns it to \p e.
/*! \relates Linear_Expression */
Linear_Expression&
operator-=(Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns the linear expression \p n * \p e and assigns it to \p e.
/*! \relates Linear_Expression */
Linear_Expression&
operator*=(Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns the linear expression \p n / \p e and assigns it to \p e.
/*! \relates Linear_Expression */
Linear_Expression&
operator/=(Linear_Expression& e, Coefficient_traits::const_reference n);

//! Assigns to \p e its own negation.
/*! \relates Linear_Expression */
void
neg_assign(Linear_Expression& e);

//! Returns the linear expression \p e + \p n * \p v and assigns it to \p e.
/*! \relates Linear_Expression */
Linear_Expression&
add_mul_assign(Linear_Expression& e,
               Coefficient_traits::const_reference n, Variable v);

//! Sums \p e2 multiplied by \p factor into \p e1.
/*! \relates Linear_Expression */
void add_mul_assign(Linear_Expression& e1,
                    Coefficient_traits::const_reference factor,
                    const Linear_Expression& e2);

//! Subtracts \p e2 multiplied by \p factor from \p e1.
/*! \relates Linear_Expression */
void sub_mul_assign(Linear_Expression& e1,
                    Coefficient_traits::const_reference factor,
                    const Linear_Expression& e2);

//! Returns the linear expression \p e - \p n * \p v and assigns it to \p e.
/*! \relates Linear_Expression */
Linear_Expression&
sub_mul_assign(Linear_Expression& e,
               Coefficient_traits::const_reference n, Variable v);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! The basic comparison function.
/*! \relates Linear_Expression

  \returns -1 or -2 if x is less than y, 0 if they are equal and 1 or 2 is y
           is greater. The absolute value of the result is 1 if the difference
           is only in the inhomogeneous terms, 2 otherwise

  The order is a lexicographic. It starts comparing the variables' coefficient,
  starting from Variable(0), and at the end it compares the inhomogeneous
  terms.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
int compare(const Linear_Expression& x, const Linear_Expression& y);

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Linear_Expression */
std::ostream& operator<<(std::ostream& s, const Linear_Expression& e);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library

//! A linear expression.
/*! \ingroup PPL_CXX_interface
  An object of the class Linear_Expression represents the linear expression
  \f[
    \sum_{i=0}^{n-1} a_i x_i + b
  \f]
  where \f$n\f$ is the dimension of the vector space,
  each \f$a_i\f$ is the integer coefficient
  of the \f$i\f$-th variable \f$x_i\f$
  and \f$b\f$ is the integer for the inhomogeneous term.

  \par How to build a linear expression.

  Linear expressions are the basic blocks for defining
  both constraints (i.e., linear equalities or inequalities)
  and generators (i.e., lines, rays, points and closure points).
  A full set of functions is defined to provide a convenient interface
  for building complex linear expressions starting from simpler ones
  and from objects of the classes Variable and Coefficient:
  available operators include unary negation,
  binary addition and subtraction,
  as well as multiplication by a Coefficient.
  The space dimension of a linear expression is defined as the maximum
  space dimension of the arguments used to build it:
  in particular, the space dimension of a Variable <CODE>x</CODE>
  is defined as <CODE>x.id()+1</CODE>,
  whereas all the objects of the class Coefficient have space dimension zero.

  \par Example
  The following code builds the linear expression \f$4x - 2y - z + 14\f$,
  having space dimension \f$3\f$:
  \code
  Linear_Expression e = 4*x - 2*y - z + 14;
  \endcode
  Another way to build the same linear expression is:
  \code
  Linear_Expression e1 = 4*x;
  Linear_Expression e2 = 2*y;
  Linear_Expression e3 = z;
  Linear_Expression e = Linear_Expression(14);
  e += e1 - e2 - e3;
  \endcode
  Note that \p e1, \p e2 and \p e3 have space dimension 1, 2 and 3,
  respectively; also, in the fourth line of code, \p e is created
  with space dimension zero and then extended to space dimension 3
  in the fifth line.
*/
class Parma_Polyhedra_Library::Linear_Expression {
public:
  static const Representation default_representation = SPARSE;

  //! Default constructor: returns a copy of Linear_Expression::zero().
  explicit Linear_Expression(Representation r = default_representation);

  /*! \brief Ordinary copy constructor.
    \note
    The new expression will have the same representation as \p e
    (not necessarily the default_representation).
  */
  Linear_Expression(const Linear_Expression& e);

  //! Copy constructor that takes also a Representation.
  Linear_Expression(const Linear_Expression& e, Representation r);

  // Queried by expression adapters.
  typedef const Linear_Expression& const_reference;
  typedef Linear_Expression raw_type;

  /*! \brief Copy constructor from a linear expression adapter.
    \note
    The new expression will have the same representation as \p e
    (not necessarily the default_representation).
  */
  template <typename LE_Adapter>
  explicit
  Linear_Expression(const LE_Adapter& e,
                    typename
                    Enable_If<Is_Same_Or_Derived<Expression_Adapter_Base,
                                                 LE_Adapter>::value,
                              void*>::type = 0);

  /*! \brief Copy constructor from a linear expression adapter that takes a
    Representation.
  */
  template <typename LE_Adapter>
  Linear_Expression(const LE_Adapter& e,
                    Representation r,
                    typename
                    Enable_If<Is_Same_Or_Derived<Expression_Adapter_Base,
                                                 LE_Adapter>::value,
                              void*>::type = 0);

  /*! \brief
    Copy constructor from a linear expression adapter that takes a
    space dimension.
    \note
    The new expression will have the same representation as \p e
    (not necessarily default_representation).
  */
  template <typename LE_Adapter>
  explicit
  Linear_Expression(const LE_Adapter& e,
                    dimension_type space_dim,
                    typename
                    Enable_If<Is_Same_Or_Derived<Expression_Adapter_Base,
                                                 LE_Adapter>::value,
                              void*>::type = 0);

  /*! \brief
    Copy constructor from a linear expression adapter that takes a
    space dimension and a Representation.
  */
  template <typename LE_Adapter>
  Linear_Expression(const LE_Adapter& e,
                    dimension_type space_dim,
                    Representation r,
                    typename
                    Enable_If<Is_Same_Or_Derived<Expression_Adapter_Base,
                                                 LE_Adapter>::value,
                              void*>::type = 0);

  //! Assignment operator.
  Linear_Expression& operator=(const Linear_Expression& e);

  //! Destructor.
  ~Linear_Expression();

  /*! \brief
    Builds the linear expression corresponding
    to the inhomogeneous term \p n.
  */
  explicit Linear_Expression(Coefficient_traits::const_reference n,
                             Representation r = default_representation);

  //! Builds the linear expression corresponding to the variable \p v.
  /*!
    \exception std::length_error
    Thrown if the space dimension of \p v exceeds
    <CODE>Linear_Expression::max_space_dimension()</CODE>.
  */
  Linear_Expression(Variable v, Representation r = default_representation);

  //! Returns the current representation of *this.
  Representation representation() const;

  //! Converts *this to the specified representation.
  void set_representation(Representation r);

  //! A const %iterator on the expression (homogeneous) coefficient that are
  //! nonzero.
  /*!
    These iterators are invalidated by operations that modify the expression.
  */
  class const_iterator {
  private:
  public:
    typedef std::bidirectional_iterator_tag iterator_category;
    typedef const Coefficient value_type;
    typedef std::ptrdiff_t difference_type;
    typedef value_type* pointer;
    typedef Coefficient_traits::const_reference reference;

    //! Constructs an invalid const_iterator.
    /*!
      This constructor takes \f$O(1)\f$ time.
    */
    explicit const_iterator();

    //! The copy constructor.
    /*!
      \param i
      The %iterator that will be copied.

      This constructor takes \f$O(1)\f$ time.
    */
    const_iterator(const const_iterator& i);

    ~const_iterator();

    //! Swaps \p i with \p *this.
    /*!
      \param i
      The %iterator that will be swapped with \p *this.

      This method takes \f$O(1)\f$ time.
    */
    void m_swap(const_iterator& i);

    //! Assigns \p i to *this .
    /*!
      \param i
      The %iterator that will be assigned into *this.

      This method takes \f$O(1)\f$ time.
    */
    const_iterator& operator=(const const_iterator& i);

    //! Navigates to the next nonzero coefficient.
    /*!
      This method takes \f$O(n)\f$ time for dense expressions, and
      \f$O(1)\f$ time for sparse expressions.
    */
    const_iterator& operator++();

    //! Navigates to the previous nonzero coefficient.
    /*!
      This method takes \f$O(n)\f$ time for dense expressions, and
      \f$O(1)\f$ time for sparse expressions.
    */
    const_iterator& operator--();

    //! Returns the current element.
    reference operator*() const;

    //! Returns the variable of the coefficient pointed to by \c *this.
    /*!
      \returns the variable of the coefficient pointed to by \c *this.
    */
    Variable variable() const;

    //! Compares \p *this with \p i.
    /*!
      \param i
      The %iterator that will be compared with *this.
    */
    bool operator==(const const_iterator& i) const;

    //! Compares \p *this with \p i .
    /*!
      \param i
      The %iterator that will be compared with *this.
    */
    bool operator!=(const const_iterator& i) const;

  private:
    //! Constructor from a const_iterator_interface*.
    //! The new object takes ownership of the dynamic object.
    const_iterator(Linear_Expression_Interface::const_iterator_interface* i);

    Linear_Expression_Interface::const_iterator_interface* itr;

    friend class Linear_Expression;
  };

  //! Returns an iterator that points to the first nonzero coefficient in the
  //! expression.
  const_iterator begin() const;

  //! Returns an iterator that points to the last nonzero coefficient in the
  //! expression.
  const_iterator end() const;

  //! Returns an iterator that points to the first nonzero coefficient of a
  //! variable bigger than or equal to v.
  const_iterator lower_bound(Variable v) const;

  //! Returns the maximum space dimension a Linear_Expression can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  //! Sets the dimension of the vector space enclosing \p *this to \p n .
  void set_space_dimension(dimension_type n);

  //! Returns the coefficient of \p v in \p *this.
  Coefficient_traits::const_reference coefficient(Variable v) const;

  //! Sets the coefficient of \p v in \p *this to \p n.
  void set_coefficient(Variable v,
                       Coefficient_traits::const_reference n);

  //! Returns the inhomogeneous term of \p *this.
  Coefficient_traits::const_reference inhomogeneous_term() const;

  //! Sets the inhomogeneous term of \p *this to \p n.
  void set_inhomogeneous_term(Coefficient_traits::const_reference n);

  //! Linearly combines \p *this with \p y so that the coefficient of \p v
  //! is 0.
  /*!
    \param y
    The expression that will be combined with \p *this object;

    \param v
    The variable whose coefficient has to become \f$0\f$.

    Computes a linear combination of \p *this and \p y having
    the coefficient of variable \p v equal to \f$0\f$. Then it assigns
    the resulting expression to \p *this.

    \p *this and \p y must have the same space dimension.
  */
  void linear_combine(const Linear_Expression& y, Variable v);

  //! Equivalent to <CODE>*this = *this * c1 + y * c2</CODE>, but assumes that
  //! c1 and c2 are not 0.
  void linear_combine(const Linear_Expression& y,
                      Coefficient_traits::const_reference c1,
                      Coefficient_traits::const_reference c2);

  //! Equivalent to <CODE>*this = *this * c1 + y * c2</CODE>.
  //! c1 and c2 may be 0.
  void linear_combine_lax(const Linear_Expression& y,
                          Coefficient_traits::const_reference c1,
                          Coefficient_traits::const_reference c2);

  //! Swaps the coefficients of the variables \p v1 and \p v2 .
  void swap_space_dimensions(Variable v1, Variable v2);

  //! Removes all the specified dimensions from the expression.
  /*!
    The space dimension of the variable with the highest space
    dimension in \p vars must be at most the space dimension
    of \p this.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  //! Shift by \p n positions the coefficients of variables, starting from
  //! the coefficient of \p v. This increases the space dimension by \p n.
  void shift_space_dimensions(Variable v, dimension_type n);

  //! Permutes the space dimensions of the expression.
  /*!
    \param cycle
    A vector representing a cycle of the permutation according to which the
    space dimensions must be rearranged.

    The \p cycle vector represents a cycle of a permutation of space
    dimensions.
    For example, the permutation
    \f$ \{ x_1 \mapsto x_2, x_2 \mapsto x_3, x_3 \mapsto x_1 \}\f$ can be
    represented by the vector containing \f$ x_1, x_2, x_3 \f$.
  */
  void permute_space_dimensions(const std::vector<Variable>& cycle);

  //! Returns <CODE>true</CODE> if and only if \p *this is \f$0\f$.
  bool is_zero() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if all the homogeneous
    terms of \p *this are \f$0\f$.
  */
  bool all_homogeneous_terms_are_zero() const;

  //! Initializes the class.
  static void initialize();

  //! Finalizes the class.
  static void finalize();

  //! Returns the (zero-dimension space) constant 0.
  static const Linear_Expression& zero();

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Swaps \p *this with \p y.
  void m_swap(Linear_Expression& y);

  //! Copy constructor with a specified space dimension.
  Linear_Expression(const Linear_Expression& e, dimension_type space_dim);

  //! Copy constructor with a specified space dimension and representation.
  Linear_Expression(const Linear_Expression& e, dimension_type space_dim,
                    Representation r);

  //! Returns \p true if *this is equal to \p x.
  //! Note that (*this == x) has a completely different meaning.
  bool is_equal_to(const Linear_Expression& x) const;

  //! Normalizes the modulo of the coefficients and of the inhomogeneous term
  //! so that they are mutually prime.
  /*!
    Computes the Greatest Common Divisor (GCD) among the coefficients
    and the inhomogeneous term and normalizes them by the GCD itself.
  */
  void normalize();

  //! Ensures that the first nonzero homogeneous coefficient is positive,
  //! by negating the row if necessary.
  void sign_normalize();

  /*! \brief
    Returns <CODE>true</CODE> if the coefficient of each variable in
    \p vars[i] is \f$0\f$.
  */
  bool all_zeroes(const Variables_Set& vars) const;

private:
  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the (zero-dimension space) constant 0.
  */
  static const Linear_Expression* zero_p;

  Linear_Expression_Interface* impl;

  //! Implementation sizing constructor.
  /*!
    The bool parameter is just to avoid problems with
    the constructor Linear_Expression(Coefficient_traits::const_reference n).
  */
  Linear_Expression(dimension_type space_dim, bool,
                    Representation r = default_representation);

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns the i-th coefficient.
  Coefficient_traits::const_reference get(dimension_type i) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Sets the i-th coefficient to n.
  void set(dimension_type i, Coefficient_traits::const_reference n);

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Returns the coefficient of v.
  Coefficient_traits::const_reference get(Variable v) const;

  // NOTE: This method is public, but it's not exposed in Linear_Expression,
  // so that it can be used internally in the PPL, by friends of
  // Linear_Expression.
  //! Sets the coefficient of v to n.
  void set(Variable v, Coefficient_traits::const_reference n);

  /*! \brief
    Returns <CODE>true</CODE> if (*this)[i] is \f$0\f$, for each i in
    [start, end).
  */
  bool all_zeroes(dimension_type start, dimension_type end) const;

  /*! \brief
    Returns the number of zero coefficient in [start, end).
  */
  dimension_type num_zeroes(dimension_type start, dimension_type end) const;

  /*! \brief
    Returns the gcd of the nonzero coefficients in [start,end). If all the
    coefficients in this range are 0 returns 0.
  */
  Coefficient gcd(dimension_type start, dimension_type end) const;

  void exact_div_assign(Coefficient_traits::const_reference c,
                        dimension_type start, dimension_type end);

  //! Linearly combines \p *this with \p y so that the coefficient of \p v
  //! is 0.
  /*!
    \param y
    The expression that will be combined with \p *this object;

    \param i
    The index of the coefficient that has to become \f$0\f$.

    Computes a linear combination of \p *this and \p y having
    the i-th coefficient equal to \f$0\f$. Then it assigns
    the resulting expression to \p *this.

    \p *this and \p y must have the same space dimension.
  */
  void linear_combine(const Linear_Expression& y, dimension_type i);

  //! Equivalent to <CODE>(*this)[i] = (*this)[i] * c1 + y[i] * c2</CODE>,
  //! for each i in [start, end). It assumes that c1 and c2 are nonzero.
  void linear_combine(const Linear_Expression& y,
                      Coefficient_traits::const_reference c1,
                      Coefficient_traits::const_reference c2,
                      dimension_type start, dimension_type end);

  //! Equivalent to <CODE>(*this)[i] = (*this)[i] * c1 + y[i] * c2</CODE>,
  //! for each i in [start, end). c1 and c2 may be zero.
  void linear_combine_lax(const Linear_Expression& y,
                          Coefficient_traits::const_reference c1,
                          Coefficient_traits::const_reference c2,
                          dimension_type start, dimension_type end);

  //! Equivalent to <CODE>(*this)[i] *= n</CODE>, for each i in [start, end).
  void mul_assign(Coefficient_traits::const_reference n,
                  dimension_type start, dimension_type end);

  //! Returns the index of the last nonzero element, or 0 if there are no
  //! nonzero elements.
  dimension_type last_nonzero() const;

  //! Returns the index of the last nonzero element in [first,last), or last
  //! if there are no nonzero elements.
  dimension_type last_nonzero(dimension_type first, dimension_type last) const;

  //! Returns the index of the first nonzero element, or \p last if there are no
  //! nonzero elements, considering only elements in [first,last).
  dimension_type first_nonzero(dimension_type first, dimension_type last) const;

  /*! \brief
    Returns <CODE>true</CODE> if all coefficients in [start,end),
    except those corresponding to variables in \p vars, are zero.
  */
  bool all_zeroes_except(const Variables_Set& vars,
                         dimension_type start, dimension_type end) const;

  //! Sets results to the sum of (*this)[i]*y[i], for each i.
  void scalar_product_assign(Coefficient& result,
                             const Linear_Expression& y) const;

  //! Sets results to the sum of (*this)[i]*y[i], for each i in [start,end).
  void scalar_product_assign(Coefficient& result, const Linear_Expression& y,
                             dimension_type start, dimension_type end) const;

  //! Computes the sign of the sum of (*this)[i]*y[i], for each i.
  int scalar_product_sign(const Linear_Expression& y) const;

  //! Computes the sign of the sum of (*this)[i]*y[i],
  //! for each i in [start,end).
  int scalar_product_sign(const Linear_Expression& y,
                          dimension_type start, dimension_type end) const;

  //! Removes from the set x all the indexes of nonzero elements of *this.
  void has_a_free_dimension_helper(std::set<dimension_type>& x) const;

  //! Returns \p true if (*this)[i] is equal to x[i], for each i in [start,end).
  bool is_equal_to(const Linear_Expression& x,
                   dimension_type start, dimension_type end) const;

  //! Returns \p true if (*this)[i]*c1 is equal to x[i]*c2, for each i in
  //! [start,end).
  bool is_equal_to(const Linear_Expression& x,
                   Coefficient_traits::const_reference c1,
                   Coefficient_traits::const_reference c2,
                   dimension_type start, dimension_type end) const;

  //! Sets \p r to a copy of the row that implements \p *this.
  void get_row(Dense_Row& r) const;

  //! Sets \p r to a copy of the row that implements \p *this.
  void get_row(Sparse_Row& r) const;

  /*! \brief
    Returns \p true if there is a variable from index \p first (included)
    to index \p last (excluded) whose coefficient is nonzero in both
    \p *this and \p x.
  */
  bool have_a_common_variable(const Linear_Expression& x,
                              Variable first, Variable last) const;

  /*! \brief
    Negates the elements from index \p first (included)
    to index \p last (excluded).
  */
  void negate(dimension_type first, dimension_type last);

  template <typename Row>
  friend class Linear_Expression_Impl;

  // NOTE: The following classes are friends of Linear_Expression in order
  // to access its private methods.
  // Since they are *not* friend of Linear_Expression_Impl, they can only
  // access its public methods so they cannot break the class invariant of
  // Linear_Expression_Impl.
  friend class Grid;
  friend class Congruence;
  friend class Polyhedron;
  friend class PIP_Tree_Node;
  friend class Grid_Generator;
  friend class Generator;
  friend class Constraint;
  friend class Constraint_System;
  friend class PIP_Problem;
  friend class BHRZ03_Certificate;
  friend class Scalar_Products;
  friend class MIP_Problem;
  friend class Box_Helpers;
  friend class Congruence_System;
  friend class BD_Shape_Helpers;
  friend class Octagonal_Shape_Helper;
  friend class Termination_Helpers;
  template <typename T>
  friend class BD_Shape;
  template <typename T>
  friend class Octagonal_Shape;
  template <typename T>
  friend class Linear_System;
  template <typename T>
  friend class Box;
  template <typename T>
  friend class Expression_Adapter;
  template <typename T>
  friend class Expression_Hide_Inhomo;
  template <typename T>
  friend class Expression_Hide_Last;

  friend Linear_Expression
  operator+(const Linear_Expression& e1, const Linear_Expression& e2);
  friend Linear_Expression
  operator+(Coefficient_traits::const_reference n, const Linear_Expression& e);
  friend Linear_Expression
  operator+(const Linear_Expression& e, Coefficient_traits::const_reference n);
  friend Linear_Expression
  operator+(Variable v, const Linear_Expression& e);
  friend Linear_Expression
  operator+(Variable v, Variable w);

  friend Linear_Expression
  operator-(const Linear_Expression& e);

  friend Linear_Expression
  operator-(const Linear_Expression& e1, const Linear_Expression& e2);
  friend Linear_Expression
  operator-(Variable v, Variable w);
  friend Linear_Expression
  operator-(Coefficient_traits::const_reference n, const Linear_Expression& e);
  friend Linear_Expression
  operator-(const Linear_Expression& e, Coefficient_traits::const_reference n);
  friend Linear_Expression
  operator-(Variable v, const Linear_Expression& e);
  friend Linear_Expression
  operator-(const Linear_Expression& e, Variable v);

  friend Linear_Expression
  operator*(Coefficient_traits::const_reference n, const Linear_Expression& e);
  friend Linear_Expression
  operator*(const Linear_Expression& e, Coefficient_traits::const_reference n);

  friend Linear_Expression&
  operator+=(Linear_Expression& e1, const Linear_Expression& e2);
  friend Linear_Expression&
  operator+=(Linear_Expression& e, Variable v);
  friend Linear_Expression&
  operator+=(Linear_Expression& e, Coefficient_traits::const_reference n);

  friend Linear_Expression&
  operator-=(Linear_Expression& e1, const Linear_Expression& e2);
  friend Linear_Expression&
  operator-=(Linear_Expression& e, Variable v);
  friend Linear_Expression&
  operator-=(Linear_Expression& e, Coefficient_traits::const_reference n);

  friend Linear_Expression&
  operator*=(Linear_Expression& e, Coefficient_traits::const_reference n);
  friend Linear_Expression&
  operator/=(Linear_Expression& e, Coefficient_traits::const_reference n);

  friend void
  neg_assign(Linear_Expression& e);

  friend Linear_Expression&
  add_mul_assign(Linear_Expression& e,
                 Coefficient_traits::const_reference n, Variable v);
  friend Linear_Expression&
  sub_mul_assign(Linear_Expression& e,
                 Coefficient_traits::const_reference n, Variable v);

  friend void
  add_mul_assign(Linear_Expression& e1,
                 Coefficient_traits::const_reference factor,
                 const Linear_Expression& e2);
  friend void
  sub_mul_assign(Linear_Expression& e1,
                 Coefficient_traits::const_reference factor,
                 const Linear_Expression& e2);

  friend int
  compare(const Linear_Expression& x, const Linear_Expression& y);

  friend std::ostream&
  Parma_Polyhedra_Library::IO_Operators
  ::operator<<(std::ostream& s, const Linear_Expression& e);
};

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Linear_Expression */
void swap(Linear_Expression& x, Linear_Expression& y);

//! Swaps \p x with \p y.
/*! \relates Linear_Expression::const_iterator */
void swap(Linear_Expression::const_iterator& x,
          Linear_Expression::const_iterator& y);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Expression_inlines.hh line 1. */
/* Linear_Expression class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Linear_Expression_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

inline Linear_Expression&
Linear_Expression::operator=(const Linear_Expression& e) {
  Linear_Expression tmp = e;
  swap(*this, tmp);
  return *this;
}

inline
Linear_Expression::~Linear_Expression() {
  delete impl;
}

inline Representation
Linear_Expression::representation() const {
  return impl->representation();
}

inline dimension_type
Linear_Expression::space_dimension() const {
  return impl->space_dimension();
}

inline void
Linear_Expression::set_space_dimension(dimension_type n) {
  impl->set_space_dimension(n);
}

inline Coefficient_traits::const_reference
Linear_Expression::coefficient(Variable v) const {
  return impl->coefficient(v);
}

inline void
Linear_Expression
::set_coefficient(Variable v, Coefficient_traits::const_reference n) {
  impl->set_coefficient(v, n);
}

inline Coefficient_traits::const_reference
Linear_Expression::inhomogeneous_term() const {
  return impl->inhomogeneous_term();
}

inline void
Linear_Expression
::set_inhomogeneous_term(Coefficient_traits::const_reference n) {
  impl->set_inhomogeneous_term(n);
}

inline void
Linear_Expression::swap_space_dimensions(Variable v1, Variable v2) {
  impl->swap_space_dimensions(v1, v2);
}

inline void
Linear_Expression::shift_space_dimensions(Variable v, dimension_type n) {
  impl->shift_space_dimensions(v, n);
}

inline bool
Linear_Expression::is_zero() const {
  return impl->is_zero();
}

inline bool
Linear_Expression::all_homogeneous_terms_are_zero() const {
  return impl->all_homogeneous_terms_are_zero();
}

inline const Linear_Expression&
Linear_Expression::zero() {
  PPL_ASSERT(zero_p != 0);
  return *zero_p;
}

inline memory_size_type
Linear_Expression::external_memory_in_bytes() const {
  return impl->total_memory_in_bytes();
}

inline memory_size_type
Linear_Expression::total_memory_in_bytes() const {
  return external_memory_in_bytes() + sizeof(*this);
}

/*! \relates Linear_Expression */
inline Linear_Expression
operator+(const Linear_Expression& e) {
  return e;
}

/*! \relates Linear_Expression */
inline Linear_Expression
operator+(const Linear_Expression& e, Coefficient_traits::const_reference n) {
  Linear_Expression x = e;
  x += n;
  return x;
}

/*! \relates Linear_Expression */
inline Linear_Expression
operator+(const Linear_Expression& e, const Variable v) {
  Linear_Expression x = e;
  x += v;
  return x;
}

/*! \relates Linear_Expression */
inline Linear_Expression
operator-(const Linear_Expression& e, Coefficient_traits::const_reference n) {
  Linear_Expression x = e;
  x -= n;
  return x;
}

/*! \relates Linear_Expression */
inline Linear_Expression
operator-(const Variable v, const Variable w) {
  const dimension_type v_space_dim = v.space_dimension();
  const dimension_type w_space_dim = w.space_dimension();
  const dimension_type space_dim = std::max(v_space_dim, w_space_dim);
  if (space_dim > Linear_Expression::max_space_dimension()) {
    throw std::length_error("Linear_Expression "
                            "PPL::operator+(v, w):\n"
                            "v or w exceed the maximum allowed "
                            "space dimension.");
  }
  if (v_space_dim >= w_space_dim) {
    Linear_Expression e(v);
    e -= w;
    return e;
  }
  else {
    Linear_Expression e(w.space_dimension(), true);
    e -= w;
    e += v;
    return e;
  }
}

/*! \relates Linear_Expression */
inline Linear_Expression
operator*(const Linear_Expression& e, Coefficient_traits::const_reference n) {
  Linear_Expression x = e;
  x *= n;
  return x;
}

/*! \relates Linear_Expression */
inline Linear_Expression&
operator+=(Linear_Expression& e, Coefficient_traits::const_reference n) {
  *e.impl += n;
  return e;
}

/*! \relates Linear_Expression */
inline Linear_Expression&
operator-=(Linear_Expression& e, Coefficient_traits::const_reference n) {
  *e.impl -= n;
  return e;
}

inline void
Linear_Expression::m_swap(Linear_Expression& y) {
  using std::swap;
  swap(impl, y.impl);
}

inline void
Linear_Expression::normalize() {
  impl->normalize();
}

inline void
Linear_Expression::ascii_dump(std::ostream& s) const {
  impl->ascii_dump(s);
}

inline bool
Linear_Expression::ascii_load(std::istream& s) {
  return impl->ascii_load(s);
}

inline void
Linear_Expression::remove_space_dimensions(const Variables_Set& vars) {
  impl->remove_space_dimensions(vars);
}

inline void
Linear_Expression::permute_space_dimensions(const std::vector<Variable>& cycle) {
  impl->permute_space_dimensions(cycle);
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression
operator+(const Linear_Expression& e1, const Linear_Expression& e2) {
  if (e1.space_dimension() >= e2.space_dimension()) {
    Linear_Expression e = e1;
    e += e2;
    return e;
  }
  else {
    Linear_Expression e = e2;
    e += e1;
    return e;
  }
}

/*! \relates Linear_Expression */
inline Linear_Expression
operator+(const Variable v, const Linear_Expression& e) {
  return e + v;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression
operator+(Coefficient_traits::const_reference n,
               const Linear_Expression& e) {
  return e + n;
}

/*! \relates Linear_Expression */
inline Linear_Expression
operator+(const Variable v, const Variable w) {
  const dimension_type v_space_dim = v.space_dimension();
  const dimension_type w_space_dim = w.space_dimension();
  const dimension_type space_dim = std::max(v_space_dim, w_space_dim);
  if (space_dim > Linear_Expression::max_space_dimension()) {
    throw std::length_error("Linear_Expression "
                            "PPL::operator+(v, w):\n"
                            "v or w exceed the maximum allowed "
                            "space dimension.");
  }
  if (v_space_dim >= w_space_dim) {
    Linear_Expression e(v);
    e += w;
    return e;
  }
  else {
    Linear_Expression e(w);
    e += v;
    return e;
  }
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression
operator-(const Linear_Expression& e) {
  Linear_Expression r(e);
  neg_assign(r);
  return r;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression
operator-(const Linear_Expression& e1, const Linear_Expression& e2) {
  if (e1.space_dimension() >= e2.space_dimension()) {
    Linear_Expression e = e1;
    e -= e2;
    return e;
  }
  else {
    Linear_Expression e = e2;
    neg_assign(e);
    e += e1;
    return e;
  }
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression
operator-(const Variable v, const Linear_Expression& e) {
  Linear_Expression result(e, std::max(v.space_dimension(), e.space_dimension()));
  result.negate(0, e.space_dimension() + 1);
  result += v;
  return result;
}

/*! \relates Linear_Expression */
inline Linear_Expression
operator-(const Linear_Expression& e, const Variable v) {
  Linear_Expression result(e, std::max(v.space_dimension(), e.space_dimension()));
  result -= v;
  return result;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression
operator-(Coefficient_traits::const_reference n,
               const Linear_Expression& e) {
  Linear_Expression result(e);
  neg_assign(result);
  result += n;
  return result;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression
operator*(Coefficient_traits::const_reference n,
               const Linear_Expression& e) {
  return e * n;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression&
operator+=(Linear_Expression& e1, const Linear_Expression& e2) {
  *e1.impl += *e2.impl;
  return e1;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression&
operator+=(Linear_Expression& e, const Variable v) {
  *e.impl += v;
  return e;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression&
operator-=(Linear_Expression& e1, const Linear_Expression& e2) {
  *e1.impl -= *e2.impl;
  return e1;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression&
operator-=(Linear_Expression& e, const Variable v) {
  *e.impl -= v;
  return e;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression&
operator*=(Linear_Expression& e, Coefficient_traits::const_reference n) {
  *e.impl *= n;
  return e;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression&
operator/=(Linear_Expression& e, Coefficient_traits::const_reference n) {
  *e.impl /= n;
  return e;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline void
neg_assign(Linear_Expression& e) {
  e.impl->negate();
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression&
add_mul_assign(Linear_Expression& e,
               Coefficient_traits::const_reference n,
               const Variable v) {
  e.impl->add_mul_assign(n, v);
  return e;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline Linear_Expression&
sub_mul_assign(Linear_Expression& e,
                    Coefficient_traits::const_reference n,
                    const Variable v) {
  e.impl->sub_mul_assign(n, v);
  return e;
}

inline void
add_mul_assign(Linear_Expression& e1,
               Coefficient_traits::const_reference factor,
               const Linear_Expression& e2) {
  e1.impl->add_mul_assign(factor, *e2.impl);
}

inline void
sub_mul_assign(Linear_Expression& e1,
                    Coefficient_traits::const_reference factor,
                    const Linear_Expression& e2) {
  e1.impl->sub_mul_assign(factor, *e2.impl);
}

inline Coefficient_traits::const_reference
Linear_Expression::get(dimension_type i) const {
  return impl->get(i);
}

inline void
Linear_Expression::set(dimension_type i,
                       Coefficient_traits::const_reference n) {
  impl->set(i, n);
}

inline Coefficient_traits::const_reference
Linear_Expression::get(Variable v) const {
  return impl->get(v.space_dimension());
}

inline void
Linear_Expression::set(Variable v,
                       Coefficient_traits::const_reference n) {
  impl->set(v.space_dimension(), n);
}

inline bool
Linear_Expression::all_zeroes(dimension_type start, dimension_type end) const {
  return impl->all_zeroes(start, end);
}

inline dimension_type
Linear_Expression::num_zeroes(dimension_type start, dimension_type end) const {
  return impl->num_zeroes(start, end);
}

inline Coefficient
Linear_Expression::gcd(dimension_type start, dimension_type end) const {
  return impl->gcd(start, end);
}

inline void
Linear_Expression
::exact_div_assign(Coefficient_traits::const_reference c,
                   dimension_type start, dimension_type end) {
  impl->exact_div_assign(c, start, end);
}

inline void
Linear_Expression
::mul_assign(Coefficient_traits::const_reference c,
             dimension_type start, dimension_type end) {
  impl->mul_assign(c, start, end);
}

inline void
Linear_Expression::sign_normalize() {
  impl->sign_normalize();
}

inline void
Linear_Expression::negate(dimension_type first, dimension_type last) {
  impl->negate(first, last);
}

inline bool
Linear_Expression::all_zeroes(const Variables_Set& vars) const {
  return impl->all_zeroes(vars);
}

inline bool
Linear_Expression::all_zeroes_except(const Variables_Set& vars,
                                     dimension_type start,
                                     dimension_type end) const {
  return impl->all_zeroes_except(vars, start, end);
}

inline dimension_type
Linear_Expression::last_nonzero() const {
  return impl->last_nonzero();
}

inline void
Linear_Expression
::scalar_product_assign(Coefficient& result, const Linear_Expression& y) const {
  scalar_product_assign(result, y, 0, space_dimension() + 1);
}

inline void
Linear_Expression
::scalar_product_assign(Coefficient& result, const Linear_Expression& y,
                        dimension_type start, dimension_type end) const {
  impl->scalar_product_assign(result, *(y.impl), start, end);
}

inline int
Linear_Expression
::scalar_product_sign(const Linear_Expression& y) const {
  return scalar_product_sign(y, 0, space_dimension() + 1);
}

inline int
Linear_Expression
::scalar_product_sign(const Linear_Expression& y,
                      dimension_type start, dimension_type end) const {
  return impl->scalar_product_sign(*(y.impl), start, end);
}

inline dimension_type
Linear_Expression
::first_nonzero(dimension_type first, dimension_type last) const {
  return impl->first_nonzero(first, last);
}

inline dimension_type
Linear_Expression
::last_nonzero(dimension_type first, dimension_type last) const {
  return impl->last_nonzero(first, last);
}

inline void
Linear_Expression
::has_a_free_dimension_helper(std::set<dimension_type>& x) const {
  return impl->has_a_free_dimension_helper(x);
}

inline bool
Linear_Expression
::is_equal_to(const Linear_Expression& x,
              dimension_type start, dimension_type end) const {
  return impl->is_equal_to(*(x.impl), start, end);
}

inline bool
Linear_Expression
::is_equal_to(const Linear_Expression& x,
              Coefficient_traits::const_reference c1,
              Coefficient_traits::const_reference c2,
              dimension_type start, dimension_type end) const {
  return impl->is_equal_to(*(x.impl), c1, c2, start, end);
}

inline void
Linear_Expression
::get_row(Dense_Row& r) const {
  return impl->get_row(r);
}

inline void
Linear_Expression
::get_row(Sparse_Row& r) const {
  return impl->get_row(r);
}

inline void
Linear_Expression
::linear_combine(const Linear_Expression& y, dimension_type i) {
  impl->linear_combine(*y.impl, i);
}

inline void
Linear_Expression
::linear_combine(const Linear_Expression& y,
                 Coefficient_traits::const_reference c1,
                 Coefficient_traits::const_reference c2) {
  impl->linear_combine(*y.impl, c1, c2);
}

inline void
Linear_Expression
::linear_combine_lax(const Linear_Expression& y,
                     Coefficient_traits::const_reference c1,
                     Coefficient_traits::const_reference c2) {
  impl->linear_combine_lax(*y.impl, c1, c2);
}

inline int
compare(const Linear_Expression& x, const Linear_Expression& y) {
  return x.impl->compare(*y.impl);
}

inline bool
Linear_Expression::is_equal_to(const Linear_Expression& x) const {
  return impl->is_equal_to(*x.impl);
}

inline void
Linear_Expression::linear_combine(const Linear_Expression& y,
                                  Coefficient_traits::const_reference c1,
                                  Coefficient_traits::const_reference c2,
                                  dimension_type start,
                                  dimension_type end) {
  impl->linear_combine(*y.impl, c1, c2, start, end);
}

inline void
Linear_Expression::linear_combine_lax(const Linear_Expression& y,
                                      Coefficient_traits::const_reference c1,
                                      Coefficient_traits::const_reference c2,
                                      dimension_type start,
                                      dimension_type end) {
  impl->linear_combine_lax(*y.impl, c1, c2, start, end);
}

inline bool
Linear_Expression
::have_a_common_variable(const Linear_Expression& x,
                         Variable first, Variable last) const {
  return impl->have_a_common_variable(*(x.impl), first, last);
}

inline
Linear_Expression::const_iterator
::const_iterator()
  : itr(NULL) {
}

inline
Linear_Expression::const_iterator
::const_iterator(const const_iterator& i)
  : itr(i.itr->clone()) {
}

inline
Linear_Expression::const_iterator
::~const_iterator() {
  // Note that this does nothing if itr == NULL.
  delete itr;
}

inline void
Linear_Expression::const_iterator::m_swap(const_iterator& i) {
  using std::swap;
  swap(itr, i.itr);
}

inline Linear_Expression::const_iterator&
Linear_Expression::const_iterator
::operator=(const const_iterator& i) {
  const_iterator tmp = i;
  using std::swap;
  swap(*this, tmp);
  return *this;
}

inline Linear_Expression::const_iterator&
Linear_Expression::const_iterator
::operator++() {
  PPL_ASSERT(itr != NULL);
  ++(*itr);
  return *this;
}

inline Linear_Expression::const_iterator&
Linear_Expression::const_iterator
::operator--() {
  PPL_ASSERT(itr != NULL);
  --(*itr);
  return *this;
}

inline Linear_Expression::const_iterator::reference
Linear_Expression::const_iterator
::operator*() const {
  PPL_ASSERT(itr != NULL);
  return *(*itr);
}

inline Variable
Linear_Expression::const_iterator
::variable() const {
  PPL_ASSERT(itr != NULL);
  return itr->variable();
}

inline bool
Linear_Expression::const_iterator
::operator==(const const_iterator& i) const {
  PPL_ASSERT(itr != NULL);
  PPL_ASSERT(i.itr != NULL);
  return *itr == *(i.itr);
}

inline bool
Linear_Expression::const_iterator
::operator!=(const const_iterator& i) const {
  return !(*this == i);
}

inline
Linear_Expression::const_iterator
::const_iterator(Linear_Expression_Interface::const_iterator_interface* i)
  : itr(i) {
  PPL_ASSERT(i != NULL);
}

inline Linear_Expression::const_iterator
Linear_Expression
::begin() const {
  return const_iterator(impl->begin());
}

inline Linear_Expression::const_iterator
Linear_Expression
::end() const {
  return const_iterator(impl->end());
}

inline Linear_Expression::const_iterator
Linear_Expression
::lower_bound(Variable v) const {
  return const_iterator(impl->lower_bound(v));
}

template <typename LE_Adapter>
inline
Linear_Expression
::Linear_Expression(const LE_Adapter& e,
                    typename Enable_If<Is_Same_Or_Derived<Expression_Adapter_Base,
                                                          LE_Adapter>::value,
                                       void*>::type)
  : impl(NULL) {
  Linear_Expression tmp(e.representation());
  tmp.set_space_dimension(e.space_dimension());
  tmp.set_inhomogeneous_term(e.inhomogeneous_term());
  for (typename LE_Adapter::const_iterator i = e.begin(),
         i_end = e.end(); i != i_end; ++i) {
    add_mul_assign(tmp, *i, i.variable());
  }
  using std::swap;
  swap(impl, tmp.impl);
}

template <typename LE_Adapter>
inline
Linear_Expression
::Linear_Expression(const LE_Adapter& e,
                    Representation r,
                    typename Enable_If<Is_Same_Or_Derived<Expression_Adapter_Base,
                                                          LE_Adapter>::value,
                                       void*>::type)
  : impl(NULL) {
  Linear_Expression tmp(r);
  tmp.set_space_dimension(e.space_dimension());
  tmp.set_inhomogeneous_term(e.inhomogeneous_term());
  for (typename LE_Adapter::const_iterator i = e.begin(),
         i_end = e.end(); i != i_end; ++i) {
    add_mul_assign(tmp, *i, i.variable());
  }
  using std::swap;
  swap(impl, tmp.impl);
}

template <typename LE_Adapter>
inline
Linear_Expression
::Linear_Expression(const LE_Adapter& e,
                    dimension_type space_dim,
                    typename Enable_If<Is_Same_Or_Derived<Expression_Adapter_Base,
                                                          LE_Adapter>::value,
                                       void*>::type)
  : impl(NULL) {
  Linear_Expression tmp(e.representation());
  tmp.set_space_dimension(space_dim);
  tmp.set_inhomogeneous_term(e.inhomogeneous_term());
  typedef typename LE_Adapter::const_iterator itr_t;
  itr_t i_end;
  if (space_dim <= e.space_dimension()) {
    i_end = e.lower_bound(Variable(space_dim));
  }
  else {
    i_end = e.end();
  }
  for (itr_t i = e.begin(); i != i_end; ++i) {
    add_mul_assign(tmp, *i, i.variable());
  }
  using std::swap;
  swap(impl, tmp.impl);
}

template <typename LE_Adapter>
inline
Linear_Expression
::Linear_Expression(const LE_Adapter& e,
                    dimension_type space_dim,
                    Representation r,
                    typename Enable_If<Is_Same_Or_Derived<Expression_Adapter_Base,
                                                          LE_Adapter>::value,
                                       void*>::type)
  : impl(NULL) {
  Linear_Expression tmp(r);
  tmp.set_space_dimension(space_dim);
  tmp.set_inhomogeneous_term(e.inhomogeneous_term());
  typedef typename LE_Adapter::const_iterator itr_t;
  itr_t i_end;
  if (space_dim <= e.space_dimension()) {
    i_end = e.lower_bound(Variable(space_dim));
  }
  else {
    i_end = e.end();
  }
  for (itr_t i = e.begin(); i != i_end; ++i) {
    add_mul_assign(tmp, *i, i.variable());
  }
  using std::swap;
  swap(impl, tmp.impl);
}

namespace IO_Operators {

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline std::ostream&
operator<<(std::ostream& s, const Linear_Expression& e) {
  e.impl->print(s);
  return s;
}

} // namespace IO_Operators

/*! \relates Parma_Polyhedra_Library::Linear_Expression */
inline void
swap(Linear_Expression& x, Linear_Expression& y) {
  x.m_swap(y);
}

/*! \relates Linear_Expression::const_iterator */
inline void
swap(Linear_Expression::const_iterator& x,
     Linear_Expression::const_iterator& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Expression_defs.hh line 946. */

/* Automatically generated from PPL source file ../src/Topology_types.hh line 1. */


namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Kinds of polyhedra domains.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
enum Topology {
  NECESSARILY_CLOSED = 0,
  NOT_NECESSARILY_CLOSED = 1
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Expression_Hide_Last_defs.hh line 1. */
/* Expression_Hide_Last class declaration.
*/


/* Automatically generated from PPL source file ../src/Expression_Hide_Last_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/Expression_Hide_Last_defs.hh line 32. */

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! An adapter for Linear_Expression that maybe hides the last coefficient.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Parma_Polyhedra_Library::Expression_Hide_Last
  : public Expression_Adapter<T> {
  typedef Expression_Adapter<T> base_type;
public:
  //! The type of this object.
  typedef Expression_Hide_Last<T> const_reference;
  //! The type obtained by one-level unwrapping.
  typedef typename base_type::inner_type inner_type;
  //! The raw, completely unwrapped type.
  typedef typename base_type::raw_type raw_type;

  //! The type of const iterators on coefficients.
  typedef typename base_type::const_iterator const_iterator;

  //! Constructor.
  explicit Expression_Hide_Last(const raw_type& expr, bool hide_last);

  //! Iterator pointing after the last nonzero variable coefficient.
  const_iterator end() const;

  //! Iterator pointing to the first nonzero variable coefficient
  //! of a variable bigger than or equal to \p v.
  const_iterator lower_bound(Variable v) const;

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  //! Returns the coefficient of \p v in \p *this.
  Coefficient_traits::const_reference coefficient(Variable v) const;

  //! Returns <CODE>true</CODE> if and only if \p *this is zero.
  bool is_zero() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if all the homogeneous
    terms of \p *this are zero.
  */
  bool all_homogeneous_terms_are_zero() const;

  /*! \brief Returns \p true if \p *this is equal to \p y.

    Note that <CODE>(*this == y)</CODE> has a completely different meaning.
  */
  template <typename Expression>
  bool is_equal_to(const Expression& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if the coefficient of each variable in
    \p vars is zero.
  */
  bool all_zeroes(const Variables_Set& vars) const;

  //! Returns the \p i -th coefficient.
  Coefficient_traits::const_reference get(dimension_type i) const;

  //! Returns the coefficient of variable \p v.
  Coefficient_traits::const_reference get(Variable v) const;

  /*! \brief
    Returns <CODE>true</CODE> if (*this)[i] is zero,
    for each i in [start, end).
  */
  bool all_zeroes(dimension_type start, dimension_type end) const;

  //! Returns the number of zero coefficient in [start, end).
  dimension_type num_zeroes(dimension_type start, dimension_type end) const;

  /*! \brief
    Returns the gcd of the nonzero coefficients in [start,end).
    Returns zero if all the coefficients in the range are zero.
  */
  Coefficient gcd(dimension_type start, dimension_type end) const;

  //! Returns the index of the last nonzero element, or zero if there are no
  //! nonzero elements.
  dimension_type last_nonzero() const;

  //! Returns the index of the last nonzero element in [first,last),
  //! or \p last if there are no nonzero elements.
  dimension_type last_nonzero(dimension_type first, dimension_type last) const;

  //! Returns the index of the first nonzero element, or \p last if there are no
  //! nonzero elements, considering only elements in [first,last).
  dimension_type first_nonzero(dimension_type first, dimension_type last) const;

  /*! \brief
    Returns <CODE>true</CODE> if all coefficients in [start,end),
    except those corresponding to variables in \p vars, are zero.
  */
  bool all_zeroes_except(const Variables_Set& vars,
                         dimension_type start, dimension_type end) const;

  //! Removes from set \p x all the indexes of nonzero elements in \p *this.
  void has_a_free_dimension_helper(std::set<dimension_type>& x) const;

  //! Returns \c true if <CODE>(*this)[i]</CODE> is equal to <CODE>y[i]</CODE>,
  //! for each i in [start,end).
  template <typename Expression>
  bool is_equal_to(const Expression& y,
                   dimension_type start, dimension_type end) const;

  //! Returns \c true if <CODE>(*this)[i]*c1</CODE> is equal to
  //! <CODE>y[i]*c2</CODE>, for each i in [start,end).
  template <typename Expression>
  bool is_equal_to(const Expression& y,
                   Coefficient_traits::const_reference c1,
                   Coefficient_traits::const_reference c2,
                   dimension_type start, dimension_type end) const;

  //! Sets \p r to a copy of the row as adapted by \p *this.
  void get_row(Dense_Row& r) const;

  //! Sets \p r to a copy of the row as adapted by \p *this.
  void get_row(Sparse_Row& r) const;

  //! Returns \c true if there is a variable in [first,last) whose coefficient
  //! is nonzero in both \p *this and \p y.
  template <typename Expression>
  bool have_a_common_variable(const Expression& y,
                              Variable first, Variable last) const;

private:
  //! Whether or not the last coefficient is hidden.
  const bool hide_last_;
};

/* Automatically generated from PPL source file ../src/Expression_Hide_Last_inlines.hh line 1. */
/* Expression_Hide_Last class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Expression_Hide_Last_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename T>
inline
Expression_Hide_Last<T>::Expression_Hide_Last(const raw_type& expr,
                                              const bool hide_last)
  : base_type(expr), hide_last_(hide_last) {
}

template <typename T>
inline dimension_type
Expression_Hide_Last<T>::space_dimension() const {
  dimension_type dim = this->inner().space_dimension();
  if (hide_last_) {
    PPL_ASSERT(dim > 0);
    --dim;
  }
  return dim;
}

template <typename T>
inline typename Expression_Hide_Last<T>::const_iterator
Expression_Hide_Last<T>::end() const {
  if (hide_last_) {
    return this->inner().lower_bound(Variable(space_dimension()));
  }
  else {
    return this->inner().end();
  }
}

template <typename T>
inline typename Expression_Hide_Last<T>::const_iterator
Expression_Hide_Last<T>::lower_bound(Variable v) const {
  PPL_ASSERT(v.space_dimension() <= space_dimension() + 1);
  return this->inner().lower_bound(v);
}

template <typename T>
inline Coefficient_traits::const_reference
Expression_Hide_Last<T>::coefficient(Variable v) const {
  PPL_ASSERT(v.space_dimension() <= space_dimension());
  return this->inner().coefficient(v);
}

template <typename T>
inline bool
Expression_Hide_Last<T>::is_zero() const {
  return this->inner().all_zeroes(0, space_dimension() + 1);
}

template <typename T>
inline bool
Expression_Hide_Last<T>::all_homogeneous_terms_are_zero() const {
  return this->inner().all_zeroes(1, space_dimension() + 1);
}

template <typename T>
template <typename Expression>
inline bool
Expression_Hide_Last<T>
::is_equal_to(const Expression& y) const {
  const dimension_type x_dim = space_dimension();
  const dimension_type y_dim = y.space_dimension();
  if (x_dim != y_dim) {
    return false;
  }
  return is_equal_to(y, 0, x_dim + 1);
}

template <typename T>
inline bool
Expression_Hide_Last<T>::all_zeroes(const Variables_Set& vars) const {
  PPL_ASSERT(vars.space_dimension() <= space_dimension());
  return this->inner().all_zeroes(vars);
}

template <typename T>
inline Coefficient_traits::const_reference
Expression_Hide_Last<T>::get(dimension_type i) const {
  PPL_ASSERT(i <= space_dimension());
  return this->inner().get(i);
}

template <typename T>
inline Coefficient_traits::const_reference
Expression_Hide_Last<T>::get(Variable v) const {
  PPL_ASSERT(v.space_dimension() <= space_dimension());
  return this->inner().get(v);
}

template <typename T>
inline bool
Expression_Hide_Last<T>::all_zeroes(dimension_type start,
                                    dimension_type end) const {
  PPL_ASSERT(end <= space_dimension() + 1);
  return this->inner().all_zeroes(start, end);
}

template <typename T>
inline dimension_type
Expression_Hide_Last<T>::num_zeroes(dimension_type start,
                                    dimension_type end) const {
  PPL_ASSERT(end <= space_dimension() + 1);
  return this->inner().num_zeroes(start, end);
}

template <typename T>
inline Coefficient
Expression_Hide_Last<T>::gcd(dimension_type start,
                             dimension_type end) const {
  PPL_ASSERT(end <= space_dimension() + 1);
  return this->inner().gcd(start, end);
}

template <typename T>
inline dimension_type
Expression_Hide_Last<T>::last_nonzero() const {
  return this->inner().last_nonzero(0, space_dimension() + 1);
}

template <typename T>
inline dimension_type
Expression_Hide_Last<T>::last_nonzero(dimension_type first,
                                      dimension_type last) const {
  PPL_ASSERT(last <= space_dimension() + 1);
  return this->inner().last_nonzero(first, last);
}

template <typename T>
inline dimension_type
Expression_Hide_Last<T>::first_nonzero(dimension_type first,
                                       dimension_type last) const {
  PPL_ASSERT(last <= space_dimension() + 1);
  return this->inner().first_nonzero(first, last);
}

template <typename T>
inline bool
Expression_Hide_Last<T>
::all_zeroes_except(const Variables_Set& vars,
                    dimension_type start, dimension_type end) const {
  PPL_ASSERT(end <= space_dimension() + 1);
  return this->inner().all_zeroes_except(vars, start, end);
}

template <typename T>
inline void
Expression_Hide_Last<T>
::has_a_free_dimension_helper(std::set<dimension_type>& x) const {
  if (x.empty()) {
    return;
  }
  PPL_ASSERT(*(--x.end()) <= space_dimension());
  this->inner().has_a_free_dimension_helper(x);
}

template <typename T>
template <typename Expression>
inline bool
Expression_Hide_Last<T>
::is_equal_to(const Expression& y,
              dimension_type start, dimension_type end) const {
  PPL_ASSERT(end <= space_dimension() + 1);
  PPL_ASSERT(end <= y.space_dimension() + 1);
  return this->inner().is_equal_to(y, start, end);
}

template <typename T>
template <typename Expression>
inline bool
Expression_Hide_Last<T>
::is_equal_to(const Expression& y,
              Coefficient_traits::const_reference c1,
              Coefficient_traits::const_reference c2,
              dimension_type start, dimension_type end) const {
  PPL_ASSERT(end <= space_dimension() + 1);
  PPL_ASSERT(end <= y.space_dimension() + 1);
  return this->inner().is_equal_to(y, c1, c2, start, end);
}

template <typename T>
inline void
Expression_Hide_Last<T>::get_row(Dense_Row& r) const {
  this->inner().get_row(r);
  if (hide_last_) {
    PPL_ASSERT(r.size() != 0);
    r.resize(r.size() - 1);
  }
}

template <typename T>
inline void
Expression_Hide_Last<T>::get_row(Sparse_Row& r) const {
  this->inner().get_row(r);
  if (hide_last_) {
    PPL_ASSERT(r.size() != 0);
    r.resize(r.size() - 1);
  }
}

template <typename T>
template <typename Expression>
inline bool
Expression_Hide_Last<T>
::have_a_common_variable(const Expression& y,
                         Variable first, Variable last) const {
  PPL_ASSERT(last.space_dimension() <= space_dimension() + 1);
  PPL_ASSERT(last.space_dimension() <= y.space_dimension() + 1);
  return this->inner().have_a_common_variable(y, first, last);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Expression_Hide_Last_defs.hh line 164. */

/* Automatically generated from PPL source file ../src/Constraint_defs.hh line 40. */

#include <iosfwd>

namespace Parma_Polyhedra_Library {

//! Returns the constraint \p e1 \< \p e2.
/*! \relates Constraint */
Constraint
operator<(const Linear_Expression& e1, const Linear_Expression& e2);

//! Returns the constraint \p v1 \< \p v2.
/*! \relates Constraint */
Constraint
operator<(Variable v1, Variable v2);

//! Returns the constraint \p e \< \p n.
/*! \relates Constraint */
Constraint
operator<(const Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns the constraint \p n \< \p e.
/*! \relates Constraint */
Constraint
operator<(Coefficient_traits::const_reference n, const Linear_Expression& e);

//! Returns the constraint \p e1 \> \p e2.
/*! \relates Constraint */
Constraint
operator>(const Linear_Expression& e1, const Linear_Expression& e2);

//! Returns the constraint \p v1 \> \p v2.
/*! \relates Constraint */
Constraint
operator>(Variable v1, Variable v2);

//! Returns the constraint \p e \> \p n.
/*! \relates Constraint */
Constraint
operator>(const Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns the constraint \p n \> \p e.
/*! \relates Constraint */
Constraint
operator>(Coefficient_traits::const_reference n, const Linear_Expression& e);

//! Returns the constraint \p e1 = \p e2.
/*! \relates Constraint */
Constraint
operator==(const Linear_Expression& e1, const Linear_Expression& e2);

//! Returns the constraint \p v1 = \p v2.
/*! \relates Constraint */
Constraint
operator==(Variable v1, Variable v2);

//! Returns the constraint \p e = \p n.
/*! \relates Constraint */
Constraint
operator==(const Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns the constraint \p n = \p e.
/*! \relates Constraint */
Constraint
operator==(Coefficient_traits::const_reference n, const Linear_Expression& e);

//! Returns the constraint \p e1 \<= \p e2.
/*! \relates Constraint */
Constraint
operator<=(const Linear_Expression& e1, const Linear_Expression& e2);

//! Returns the constraint \p v1 \<= \p v2.
/*! \relates Constraint */
Constraint
operator<=(Variable v1, Variable v2);

//! Returns the constraint \p e \<= \p n.
/*! \relates Constraint */
Constraint
operator<=(const Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns the constraint \p n \<= \p e.
/*! \relates Constraint */
Constraint
operator<=(Coefficient_traits::const_reference n, const Linear_Expression& e);

//! Returns the constraint \p e1 \>= \p e2.
/*! \relates Constraint */
Constraint
operator>=(const Linear_Expression& e1, const Linear_Expression& e2);

//! Returns the constraint \p v1 \>= \p v2.
/*! \relates Constraint */
Constraint
operator>=(Variable v1, Variable v2);

//! Returns the constraint \p e \>= \p n.
/*! \relates Constraint */
Constraint
operator>=(const Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns the constraint \p n \>= \p e.
/*! \relates Constraint */
Constraint
operator>=(Coefficient_traits::const_reference n, const Linear_Expression& e);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! The basic comparison function.
/*! \relates Constraint
  \return
  The returned absolute value can be \f$0\f$, \f$1\f$ or \f$2\f$.

  \param x
  A row of coefficients;

  \param y
  Another row.

  Compares \p x and \p y, where \p x and \p y may be of different size,
  in which case the "missing" coefficients are assumed to be zero.
  The comparison is such that:
  -# equalities are smaller than inequalities;
  -# lines are smaller than points and rays;
  -# the ordering is lexicographic;
  -# the positions compared are, in decreasing order of significance,
     1, 2, ..., \p size(), 0;
  -# the result is negative, zero, or positive if x is smaller than,
     equal to, or greater than y, respectively;
  -# when \p x and \p y are different, the absolute value of the
     result is 1 if the difference is due to the coefficient in
     position 0; it is 2 otherwise.

  When \p x and \p y represent the hyper-planes associated
  to two equality or inequality constraints, the coefficient
  at 0 is the known term.
  In this case, the return value can be characterized as follows:
  - -2, if \p x is smaller than \p y and they are \e not parallel;
  - -1, if \p x is smaller than \p y and they \e are parallel;
  -  0, if \p x and y are equal;
  - +1, if \p y is smaller than \p x and they \e are parallel;
  - +2, if \p y is smaller than \p x and they are \e not parallel.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
int compare(const Constraint& x, const Constraint& y);

}

//! A linear equality or inequality.
/*! \ingroup PPL_CXX_interface
  An object of the class Constraint is either:
  - an equality: \f$\sum_{i=0}^{n-1} a_i x_i + b = 0\f$;
  - a non-strict inequality: \f$\sum_{i=0}^{n-1} a_i x_i + b \geq 0\f$; or
  - a strict inequality: \f$\sum_{i=0}^{n-1} a_i x_i + b > 0\f$;

  where \f$n\f$ is the dimension of the space,
  \f$a_i\f$ is the integer coefficient of variable \f$x_i\f$
  and \f$b\f$ is the integer inhomogeneous term.

  \par How to build a constraint
  Constraints are typically built by applying a relation symbol
  to a pair of linear expressions.
  Available relation symbols are equality (<CODE>==</CODE>),
  non-strict inequalities (<CODE>\>=</CODE> and <CODE>\<=</CODE>) and
  strict inequalities (<CODE>\<</CODE> and <CODE>\></CODE>).
  The space dimension of a constraint is defined as the maximum
  space dimension of the arguments of its constructor.

  \par
  In the following examples it is assumed that variables
  <CODE>x</CODE>, <CODE>y</CODE> and <CODE>z</CODE>
  are defined as follows:
  \code
  Variable x(0);
  Variable y(1);
  Variable z(2);
  \endcode

  \par Example 1
  The following code builds the equality constraint
  \f$3x + 5y - z = 0\f$, having space dimension \f$3\f$:
  \code
  Constraint eq_c(3*x + 5*y - z == 0);
  \endcode
  The following code builds the (non-strict) inequality constraint
  \f$4x \geq 2y - 13\f$, having space dimension \f$2\f$:
  \code
  Constraint ineq_c(4*x >= 2*y - 13);
  \endcode
  The corresponding strict inequality constraint
  \f$4x > 2y - 13\f$ is obtained as follows:
  \code
  Constraint strict_ineq_c(4*x > 2*y - 13);
  \endcode
  An unsatisfiable constraint on the zero-dimension space \f$\Rset^0\f$
  can be specified as follows:
  \code
  Constraint false_c = Constraint::zero_dim_false();
  \endcode
  Equivalent, but more involved ways are the following:
  \code
  Constraint false_c1(Linear_Expression::zero() == 1);
  Constraint false_c2(Linear_Expression::zero() >= 1);
  Constraint false_c3(Linear_Expression::zero() > 0);
  \endcode
  In contrast, the following code defines an unsatisfiable constraint
  having space dimension \f$3\f$:
  \code
  Constraint false_c(0*z == 1);
  \endcode

  \par How to inspect a constraint
  Several methods are provided to examine a constraint and extract
  all the encoded information: its space dimension, its type
  (equality, non-strict inequality, strict inequality) and
  the value of its integer coefficients.

  \par Example 2
  The following code shows how it is possible to access each single
  coefficient of a constraint. Given an inequality constraint
  (in this case \f$x - 5y + 3z \leq 4\f$), we construct a new constraint
  corresponding to its complement (thus, in this case we want to obtain
  the strict inequality constraint \f$x - 5y + 3z > 4\f$).
  \code
  Constraint c1(x - 5*y + 3*z <= 4);
  cout << "Constraint c1: " << c1 << endl;
  if (c1.is_equality())
    cout << "Constraint c1 is not an inequality." << endl;
  else {
    Linear_Expression e;
    for (dimension_type i = c1.space_dimension(); i-- > 0; )
      e += c1.coefficient(Variable(i)) * Variable(i);
    e += c1.inhomogeneous_term();
    Constraint c2 = c1.is_strict_inequality() ? (e <= 0) : (e < 0);
    cout << "Complement c2: " << c2 << endl;
  }
  \endcode
  The actual output is the following:
  \code
  Constraint c1: -A + 5*B - 3*C >= -4
  Complement c2: A - 5*B + 3*C > 4
  \endcode
  Note that, in general, the particular output obtained can be
  syntactically different from the (semantically equivalent)
  constraint considered.
*/
class Parma_Polyhedra_Library::Constraint {
public:

  //! The constraint type.
  enum Type {
    /*! The constraint is an equality. */
    EQUALITY,
    /*! The constraint is a non-strict inequality. */
    NONSTRICT_INEQUALITY,
    /*! The constraint is a strict inequality. */
    STRICT_INEQUALITY
  };

  //! The representation used for new Constraints.
  /*!
    \note The copy constructor and the copy constructor with specified size
          use the representation of the original object, so that it is
          indistinguishable from the original object.
  */
  static const Representation default_representation = SPARSE;

  //! Constructs the \f$0<=0\f$ constraint.
  explicit Constraint(Representation r = default_representation);

  //! Ordinary copy constructor.
  /*!
    \note The new Constraint will have the same representation as `c',
          not default_representation, so that they are indistinguishable.
  */
  Constraint(const Constraint& c);

  //! Copy constructor with given size.
  /*!
    \note The new Constraint will have the same representation as `c',
          not default_representation, so that they are indistinguishable.
  */
  Constraint(const Constraint& c, dimension_type space_dim);

  //! Copy constructor with given representation.
  Constraint(const Constraint& c, Representation r);

  //! Copy constructor with given size and representation.
  Constraint(const Constraint& c, dimension_type space_dim,
             Representation r);

  //! Copy-constructs from equality congruence \p cg.
  /*!
    \exception std::invalid_argument
    Thrown if \p cg is a proper congruence.
  */
  explicit Constraint(const Congruence& cg,
                      Representation r = default_representation);

  //! Destructor.
  ~Constraint();

  //! Returns the current representation of *this.
  Representation representation() const;

  //! Converts *this to the specified representation.
  void set_representation(Representation r);

  //! Assignment operator.
  Constraint& operator=(const Constraint& c);

  //! Returns the maximum space dimension a Constraint can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  //! Sets the dimension of the vector space enclosing \p *this to
  //! \p space_dim .
  void set_space_dimension(dimension_type space_dim);

  //! Swaps the coefficients of the variables \p v1 and \p v2 .
  void swap_space_dimensions(Variable v1, Variable v2);

  //! Removes all the specified dimensions from the constraint.
  /*!
    The space dimension of the variable with the highest space
    dimension in \p vars must be at most the space dimension
    of \p this.

    Always returns \p true. The return value is needed for compatibility with
    the Generator class.
  */
  bool remove_space_dimensions(const Variables_Set& vars);

  //! Permutes the space dimensions of the constraint.
  /*
    \param cycle
    A vector representing a cycle of the permutation according to which the
    space dimensions must be rearranged.

    The \p cycle vector represents a cycle of a permutation of space
    dimensions.
    For example, the permutation
    \f$ \{ x_1 \mapsto x_2, x_2 \mapsto x_3, x_3 \mapsto x_1 \}\f$ can be
    represented by the vector containing \f$ x_1, x_2, x_3 \f$.
  */
  void permute_space_dimensions(const std::vector<Variable>& cycle);

  //! Shift by \p n positions the coefficients of variables, starting from
  //! the coefficient of \p v. This increases the space dimension by \p n.
  void shift_space_dimensions(Variable v, dimension_type n);

  //! Returns the constraint type of \p *this.
  Type type() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this is an equality constraint.
  */
  bool is_equality() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this is an inequality constraint (either strict or non-strict).
  */
  bool is_inequality() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this is a non-strict inequality constraint.
  */
  bool is_nonstrict_inequality() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this is a strict inequality constraint.
  */
  bool is_strict_inequality() const;

  //! Returns the coefficient of \p v in \p *this.
  /*!
    \exception std::invalid_argument thrown if the index of \p v
    is greater than or equal to the space dimension of \p *this.
  */
  Coefficient_traits::const_reference coefficient(Variable v) const;

  //! Returns the inhomogeneous term of \p *this.
  Coefficient_traits::const_reference inhomogeneous_term() const;

  //! Initializes the class.
  static void initialize();

  //! Finalizes the class.
  static void finalize();

  //! The unsatisfiable (zero-dimension space) constraint \f$0 = 1\f$.
  static const Constraint& zero_dim_false();

  /*! \brief
    The true (zero-dimension space) constraint \f$0 \leq 1\f$,
    also known as <EM>positivity constraint</EM>.
  */
  static const Constraint& zero_dim_positivity();

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this is a tautology (i.e., an always true constraint).

    A tautology can have either one of the following forms:
    - an equality: \f$\sum_{i=0}^{n-1} 0 x_i + 0 = 0\f$; or
    - a non-strict inequality: \f$\sum_{i=0}^{n-1} 0 x_i + b \geq 0\f$,
      where \f$b \geq 0\f$; or
    - a strict inequality: \f$\sum_{i=0}^{n-1} 0 x_i + b > 0\f$,
      where \f$b > 0\f$.
  */
  bool is_tautological() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this is inconsistent (i.e., an always false constraint).

    An inconsistent constraint can have either one of the following forms:
    - an equality: \f$\sum_{i=0}^{n-1} 0 x_i + b = 0\f$,
      where \f$b \neq 0\f$; or
    - a non-strict inequality: \f$\sum_{i=0}^{n-1} 0 x_i + b \geq 0\f$,
      where \f$b < 0\f$; or
    - a strict inequality: \f$\sum_{i=0}^{n-1} 0 x_i + b > 0\f$,
      where \f$b \leq 0\f$.
  */
  bool is_inconsistent() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this and \p y
    are equivalent constraints.

    Constraints having different space dimensions are not equivalent.
    Note that constraints having different types may nonetheless be
    equivalent, if they both are tautologies or inconsistent.
  */
  bool is_equivalent_to(const Constraint& y) const;

  //! Returns <CODE>true</CODE> if \p *this is identical to \p y.
  /*!
    This is faster than is_equivalent_to(), but it may return `false' even
    for equivalent constraints.
  */
  bool is_equal_to(const Constraint& y) const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Swaps \p *this with \p y.
  void m_swap(Constraint& y);

  //! Returns the zero-dimension space constraint \f$\epsilon \geq 0\f$.
  static const Constraint& epsilon_geq_zero();

  /*! \brief
    The zero-dimension space constraint \f$\epsilon \leq 1\f$
    (used to implement NNC polyhedra).
  */
  static const Constraint& epsilon_leq_one();

  //! The type of the (adapted) internal expression.
  typedef Expression_Hide_Last<Linear_Expression> expr_type;
  //! Partial read access to the (adapted) internal expression.
  expr_type expression() const;

private:

  //! The possible kinds of Constraint objects.
  enum Kind {
    LINE_OR_EQUALITY = 0,
    RAY_OR_POINT_OR_INEQUALITY = 1
  };

  Linear_Expression expr;

  Kind kind_;

  Topology topology_;

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the unsatisfiable (zero-dimension space) constraint \f$0 = 1\f$.
  */
  static const Constraint* zero_dim_false_p;

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the true (zero-dimension space) constraint \f$0 \leq 1\f$, also
    known as <EM>positivity constraint</EM>.
  */
  static const Constraint* zero_dim_positivity_p;

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the zero-dimension space constraint \f$\epsilon \geq 0\f$.
  */
  static const Constraint* epsilon_geq_zero_p;

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the zero-dimension space constraint \f$\epsilon \leq 1\f$
    (used to implement NNC polyhedra).
  */
  static const Constraint* epsilon_leq_one_p;

  //! Constructs the \f$0<0\f$ constraint.
  Constraint(dimension_type space_dim, Kind kind, Topology topology,
             Representation r = default_representation);

  /*! \brief
    Builds a constraint of kind \p kind and topology \p topology,
    stealing the coefficients from \p e.

    \note The new Constraint will have the same representation as `e'.
  */
  Constraint(Linear_Expression& e, Kind kind, Topology topology);

  /*! \brief
    Builds a constraint of type \p type and topology \p topology,
    stealing the coefficients from \p e.

    \note The new Constraint will have the same representation as `e'.
  */
  Constraint(Linear_Expression& e, Type type, Topology topology);

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this row
    represents a line or an equality.
  */
  bool is_line_or_equality() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this row
    represents a ray, a point or an inequality.
  */
  bool is_ray_or_point_or_inequality() const;

  //! Sets to \p LINE_OR_EQUALITY the kind of \p *this row.
  void set_is_line_or_equality();

  //! Sets to \p RAY_OR_POINT_OR_INEQUALITY the kind of \p *this row.
  void set_is_ray_or_point_or_inequality();

  //! \name Flags inspection methods
  //@{
  //! Returns the topological kind of \p *this.
  Topology topology() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if the topology
    of \p *this row is not necessarily closed.
  */
  bool is_not_necessarily_closed() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if the topology
    of \p *this row is necessarily closed.
  */
  bool is_necessarily_closed() const;
  //@} // Flags inspection methods

  //! \name Flags coercion methods
  //@{

  // TODO: Consider setting the epsilon dimension in this method.
  //! Sets to \p x the topological kind of \p *this row.
  void set_topology(Topology x);

  //! Sets to \p NECESSARILY_CLOSED the topological kind of \p *this row.
  void set_necessarily_closed();

  //! Sets to \p NOT_NECESSARILY_CLOSED the topological kind of \p *this row.
  void set_not_necessarily_closed();
  //@} // Flags coercion methods

  //! Sets the dimension of the vector space enclosing \p *this to
  //! \p space_dim .
  //! Sets the space dimension of the rows in the system to \p space_dim .
  /*!
    This method is for internal use, it does *not* assert OK() at the end,
    so it can be used for invalid objects.
  */
  void set_space_dimension_no_ok(dimension_type space_dim);

  /*! \brief
    Throws a <CODE>std::invalid_argument</CODE> exception containing
    error message \p message.
  */
  void
  throw_invalid_argument(const char* method, const char* message) const;

  /*! \brief
    Throws a <CODE>std::invalid_argument</CODE> exception
    containing the appropriate error message.
  */
  void
  throw_dimension_incompatible(const char* method,
                               const char* name_var,
                               Variable v) const;

  //! Returns the epsilon coefficient. The constraint must be NNC.
  Coefficient_traits::const_reference epsilon_coefficient() const;

  //! Sets the epsilon coefficient to \p n. The constraint must be NNC.
  void set_epsilon_coefficient(Coefficient_traits::const_reference n);

  //! Marks the epsilon dimension as a standard dimension.
  /*!
    The row topology is changed to <CODE>NOT_NECESSARILY_CLOSED</CODE>, and
    the number of space dimensions is increased by 1.
  */
  void mark_as_necessarily_closed();

  //! Marks the last dimension as the epsilon dimension.
  /*!
    The row topology is changed to <CODE>NECESSARILY_CLOSED</CODE>, and
    the number of space dimensions is decreased by 1.
  */
  void mark_as_not_necessarily_closed();

  //! Sets the constraint type to <CODE>EQUALITY</CODE>.
  void set_is_equality();

  //! Sets the constraint to be an inequality.
  /*!
    Whether the constraint type will become <CODE>NONSTRICT_INEQUALITY</CODE>
    or <CODE>STRICT_INEQUALITY</CODE> depends on the topology and the value
    of the low-level coefficients of the constraint.
  */
  void set_is_inequality();

  //! Linearly combines \p *this with \p y so that i-th coefficient is 0.
  /*!
    \param y
    The Constraint that will be combined with \p *this object;

    \param i
    The index of the coefficient that has to become \f$0\f$.

    Computes a linear combination of \p *this and \p y having
    the i-th coefficient equal to \f$0\f$. Then it assigns
    the resulting Constraint to \p *this and normalizes it.
  */
  void linear_combine(const Constraint& y, dimension_type i);

  /*! \brief
    Normalizes the sign of the coefficients so that the first non-zero
    (homogeneous) coefficient of a line-or-equality is positive.
  */
  void sign_normalize();

  /*! \brief
    Strong normalization: ensures that different Constraint objects
    represent different hyperplanes or hyperspaces.

    Applies both Constraint::normalize() and Constraint::sign_normalize().
  */
  void strong_normalize();

  /*! \brief
    Returns <CODE>true</CODE> if and only if the coefficients are
    strongly normalized.
  */
  bool check_strong_normalized() const;

  /*! \brief
    Builds a new copy of the zero-dimension space constraint
    \f$\epsilon \geq 0\f$ (used to implement NNC polyhedra).
  */
  static Constraint construct_epsilon_geq_zero();

  friend int
  compare(const Constraint& x, const Constraint& y);

  friend class Linear_System<Constraint>;
  friend class Constraint_System;
  friend class Polyhedron;
  friend class Scalar_Products;
  friend class Topology_Adjusted_Scalar_Product_Sign;
  friend class Termination_Helpers;
  friend class Grid;
  template <typename T>
  friend class Octagonal_Shape;

  friend Constraint
  operator<(const Linear_Expression& e1, const Linear_Expression& e2);

  friend Constraint
  operator<(Variable v1, Variable v2);

  friend Constraint
  operator<(const Linear_Expression& e, Coefficient_traits::const_reference n);

  friend Constraint
  operator<(Coefficient_traits::const_reference n, const Linear_Expression& e);

  friend Constraint
  operator>(const Linear_Expression& e1, const Linear_Expression& e2);

  friend Constraint
  operator>(Variable v1, Variable v2);

  friend Constraint
  operator>(const Linear_Expression& e, Coefficient_traits::const_reference n);

  friend Constraint
  operator>(Coefficient_traits::const_reference n, const Linear_Expression& e);

  friend Constraint
  operator==(const Linear_Expression& e1, const Linear_Expression& e2);

  friend Constraint
  operator==(Variable v1, Variable v2);

  friend Constraint
  operator==(const Linear_Expression& e, Coefficient_traits::const_reference n);

  friend Constraint
  operator==(Coefficient_traits::const_reference n, const Linear_Expression& e);

  friend Constraint
  operator<=(const Linear_Expression& e1, const Linear_Expression& e2);

  friend Constraint
  operator<=(Variable v1, Variable v2);

  friend Constraint
  operator<=(const Linear_Expression& e, Coefficient_traits::const_reference n);

  friend Constraint
  operator<=(Coefficient_traits::const_reference n, const Linear_Expression& e);

  friend Constraint
  operator>=(const Linear_Expression& e1, const Linear_Expression& e2);

  friend Constraint
  operator>=(Variable v1, Variable v2);

  friend Constraint
  operator>=(const Linear_Expression& e, Coefficient_traits::const_reference n);

  friend Constraint
  operator>=(Coefficient_traits::const_reference n, const Linear_Expression& e);
};

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Constraint */
std::ostream& operator<<(std::ostream& s, const Constraint& c);

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Constraint */
std::ostream& operator<<(std::ostream& s, const Constraint::Type& t);

} // namespace IO_Operators

//! Returns <CODE>true</CODE> if and only if \p x is equivalent to \p y.
/*! \relates Constraint */
bool
operator==(const Constraint& x, const Constraint& y);

//! Returns <CODE>true</CODE> if and only if \p x is not equivalent to \p y.
/*! \relates Constraint */
bool
operator!=(const Constraint& x, const Constraint& y);

/*! \relates Constraint */
void swap(Constraint& x, Constraint& y);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Constraint_inlines.hh line 1. */
/* Constraint class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Constraint_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

inline bool
Constraint::is_necessarily_closed() const {
  return (topology_ == NECESSARILY_CLOSED);
}

inline bool
Constraint::is_not_necessarily_closed() const {
  return !is_necessarily_closed();
}

inline Constraint::expr_type
Constraint::expression() const {
  return expr_type(expr, is_not_necessarily_closed());
}

inline dimension_type
Constraint::space_dimension() const {
  return expression().space_dimension();
}

inline void
Constraint::shift_space_dimensions(Variable v, dimension_type n) {
  expr.shift_space_dimensions(v, n);
}

inline bool
Constraint::is_line_or_equality() const {
  return (kind_ == LINE_OR_EQUALITY);
}

inline bool
Constraint::is_ray_or_point_or_inequality() const {
  return (kind_ == RAY_OR_POINT_OR_INEQUALITY);
}

inline Topology
Constraint::topology() const {
  return topology_;
}

inline void
Constraint::set_is_line_or_equality() {
  kind_ = LINE_OR_EQUALITY;
}

inline void
Constraint::set_is_ray_or_point_or_inequality() {
  kind_ = RAY_OR_POINT_OR_INEQUALITY;
}

inline void
Constraint::set_topology(Topology x) {
  if (topology() == x) {
    return;
  }
  if (topology() == NECESSARILY_CLOSED) {
    // Add a column for the epsilon dimension.
    expr.set_space_dimension(expr.space_dimension() + 1);
  }
  else {
    PPL_ASSERT(expr.space_dimension() != 0);
    expr.set_space_dimension(expr.space_dimension() - 1);
  }
  topology_ = x;
}

inline void
Constraint::mark_as_necessarily_closed() {
  PPL_ASSERT(is_not_necessarily_closed());
  topology_ = NECESSARILY_CLOSED;
}

inline void
Constraint::mark_as_not_necessarily_closed() {
  PPL_ASSERT(is_necessarily_closed());
  topology_ = NOT_NECESSARILY_CLOSED;
}

inline void
Constraint::set_necessarily_closed() {
  set_topology(NECESSARILY_CLOSED);
}

inline void
Constraint::set_not_necessarily_closed() {
  set_topology(NOT_NECESSARILY_CLOSED);
}

inline
Constraint::Constraint(Representation r)
  : expr(r),
    kind_(RAY_OR_POINT_OR_INEQUALITY),
    topology_(NECESSARILY_CLOSED) {
  PPL_ASSERT(OK());
}

inline
Constraint::Constraint(dimension_type space_dim, Kind kind, Topology topology,
                       Representation r)
  : expr(r),
    kind_(kind),
    topology_(topology) {
  expr.set_space_dimension(space_dim + 1);
  PPL_ASSERT(space_dimension() == space_dim);
  PPL_ASSERT(OK());
}

inline
Constraint::Constraint(Linear_Expression& e, Kind kind, Topology topology)
  : kind_(kind),
    topology_(topology) {
  PPL_ASSERT(kind != RAY_OR_POINT_OR_INEQUALITY || topology == NOT_NECESSARILY_CLOSED);
  swap(expr, e);
  if (topology == NOT_NECESSARILY_CLOSED) {
    // Add the epsilon dimension.
    expr.set_space_dimension(expr.space_dimension() + 1);
  }
  strong_normalize();
  PPL_ASSERT(OK());
}

inline
Constraint::Constraint(Linear_Expression& e, Type type, Topology topology)
  : topology_(topology) {
  PPL_ASSERT(type != STRICT_INEQUALITY || topology == NOT_NECESSARILY_CLOSED);
  swap(expr, e);
  if (topology == NOT_NECESSARILY_CLOSED) {
    expr.set_space_dimension(expr.space_dimension() + 1);
  }
  if (type == EQUALITY) {
    kind_ = LINE_OR_EQUALITY;
  }
  else {
    kind_ = RAY_OR_POINT_OR_INEQUALITY;
  }
  strong_normalize();
  PPL_ASSERT(OK());
}

inline
Constraint::Constraint(const Constraint& c)
  : expr(c.expr),
    kind_(c.kind_),
    topology_(c.topology_) {
  // NOTE: This does not call PPL_ASSERT(OK()) because this is called by OK().
}

inline
Constraint::Constraint(const Constraint& c, Representation r)
  : expr(c.expr, r),
    kind_(c.kind_),
    topology_(c.topology_) {
  PPL_ASSERT(OK());
}

inline
Constraint::Constraint(const Constraint& c, const dimension_type space_dim)
  : expr(c.expr, c.is_necessarily_closed() ? space_dim : (space_dim + 1)),
    kind_(c.kind_), topology_(c.topology_) {
  PPL_ASSERT(space_dimension() == space_dim);
  PPL_ASSERT(OK());
}

inline
Constraint::Constraint(const Constraint& c, const dimension_type space_dim,
                       Representation r)
  : expr(c.expr, c.is_necessarily_closed() ? space_dim : (space_dim + 1), r),
    kind_(c.kind_), topology_(c.topology_) {
  PPL_ASSERT(space_dimension() == space_dim);
  PPL_ASSERT(OK());
}

inline
Constraint::~Constraint() {
}

inline Constraint&
Constraint::operator=(const Constraint& c) {
  Constraint tmp = c;
  swap(*this, tmp);

  return *this;
}

inline Representation
Constraint::representation() const {
  return expr.representation();
}

inline void
Constraint::set_representation(Representation r) {
  expr.set_representation(r);
}

inline dimension_type
Constraint::max_space_dimension() {
  return Linear_Expression::max_space_dimension();
}

inline void
Constraint::set_space_dimension_no_ok(dimension_type space_dim) {
  const dimension_type old_expr_space_dim = expr.space_dimension();
  if (topology() == NECESSARILY_CLOSED) {
    expr.set_space_dimension(space_dim);
  }
  else {
    const dimension_type old_space_dim = space_dimension();
    if (space_dim > old_space_dim) {
      expr.set_space_dimension(space_dim + 1);
      expr.swap_space_dimensions(Variable(space_dim), Variable(old_space_dim));
    }
    else {
      expr.swap_space_dimensions(Variable(space_dim), Variable(old_space_dim));
      expr.set_space_dimension(space_dim + 1);
    }
  }
  PPL_ASSERT(space_dimension() == space_dim);
  if (expr.space_dimension() < old_expr_space_dim) {
    strong_normalize();
  }
}

inline void
Constraint::set_space_dimension(dimension_type space_dim) {
  set_space_dimension_no_ok(space_dim);
  PPL_ASSERT(OK());
}

inline bool
Constraint::remove_space_dimensions(const Variables_Set& vars) {
  expr.remove_space_dimensions(vars);
  return true;
}

inline bool
Constraint::is_equality() const {
  return is_line_or_equality();
}

inline bool
Constraint::is_inequality() const {
  return is_ray_or_point_or_inequality();
}

inline Constraint::Type
Constraint::type() const {
  if (is_equality()) {
    return EQUALITY;
  }
  if (is_necessarily_closed()) {
    return NONSTRICT_INEQUALITY;
  }
  if (epsilon_coefficient() < 0) {
    return STRICT_INEQUALITY;
  }
  else {
    return NONSTRICT_INEQUALITY;
  }
}

inline bool
Constraint::is_nonstrict_inequality() const {
  return type() == NONSTRICT_INEQUALITY;
}

inline bool
Constraint::is_strict_inequality() const {
  return type() == STRICT_INEQUALITY;
}

inline void
Constraint::set_is_equality() {
  set_is_line_or_equality();
}

inline void
Constraint::set_is_inequality() {
  set_is_ray_or_point_or_inequality();
}

inline Coefficient_traits::const_reference
Constraint::coefficient(const Variable v) const {
  if (v.space_dimension() > space_dimension()) {
    throw_dimension_incompatible("coefficient(v)", "v", v);
  }
  return expr.coefficient(v);
}

inline Coefficient_traits::const_reference
Constraint::inhomogeneous_term() const {
  return expr.inhomogeneous_term();
}

inline memory_size_type
Constraint::external_memory_in_bytes() const {
  return expr.external_memory_in_bytes();
}

inline memory_size_type
Constraint::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

inline void
Constraint::strong_normalize() {
  expr.normalize();
  sign_normalize();
}

/*! \relates Constraint */
inline bool
operator==(const Constraint& x, const Constraint& y) {
  return x.is_equivalent_to(y);
}

/*! \relates Constraint */
inline bool
operator!=(const Constraint& x, const Constraint& y) {
  return !x.is_equivalent_to(y);
}

/*! \relates Constraint */
inline Constraint
operator==(const Linear_Expression& e1, const Linear_Expression& e2) {
  Linear_Expression diff(e1,
                         std::max(e1.space_dimension(), e2.space_dimension()),
                         Constraint::default_representation);
  diff -= e2;
  return Constraint(diff, Constraint::EQUALITY, NECESSARILY_CLOSED);
}

/*! \relates Constraint */
inline Constraint
operator==(Variable v1, Variable v2) {
  if (v1.space_dimension() > v2.space_dimension()) {
    swap(v1, v2);
  }
  PPL_ASSERT(v1.space_dimension() <= v2.space_dimension());

  Linear_Expression diff(v1, Constraint::default_representation);
  diff -= v2;
  return Constraint(diff, Constraint::EQUALITY, NECESSARILY_CLOSED);
}

/*! \relates Constraint */
inline Constraint
operator>=(const Linear_Expression& e1, const Linear_Expression& e2) {
  Linear_Expression diff(e1,
                         std::max(e1.space_dimension(), e2.space_dimension()),
                         Constraint::default_representation);
  diff -= e2;
  return Constraint(diff, Constraint::NONSTRICT_INEQUALITY, NECESSARILY_CLOSED);
}

/*! \relates Constraint */
inline Constraint
operator>=(const Variable v1, const Variable v2) {
  Linear_Expression diff(Constraint::default_representation);
  diff.set_space_dimension(std::max(v1.space_dimension(),
                                    v2.space_dimension()));
  diff += v1;
  diff -= v2;
  return Constraint(diff, Constraint::NONSTRICT_INEQUALITY, NECESSARILY_CLOSED);
}

/*! \relates Constraint */
inline Constraint
operator>(const Linear_Expression& e1, const Linear_Expression& e2) {
  Linear_Expression diff(e1, Constraint::default_representation);
  diff -= e2;
  Constraint c(diff, Constraint::STRICT_INEQUALITY, NOT_NECESSARILY_CLOSED);

  // NOTE: this also enforces normalization.
  c.set_epsilon_coefficient(-1);
  PPL_ASSERT(c.OK());

  return c;
}

/*! \relates Constraint */
inline Constraint
operator>(const Variable v1, const Variable v2) {
  Linear_Expression diff(Constraint::default_representation);
  diff.set_space_dimension(std::max(v1.space_dimension(),
                                    v2.space_dimension()));
  diff += v1;
  diff -= v2;
  Constraint c(diff, Constraint::STRICT_INEQUALITY, NOT_NECESSARILY_CLOSED);

  c.set_epsilon_coefficient(-1);
  PPL_ASSERT(c.OK());

  return c;
}

/*! \relates Constraint */
inline Constraint
operator==(Coefficient_traits::const_reference n, const Linear_Expression& e) {
  Linear_Expression diff(e, Constraint::default_representation);
  neg_assign(diff);
  diff += n;
  return Constraint(diff, Constraint::EQUALITY, NECESSARILY_CLOSED);
}

/*! \relates Constraint */
inline Constraint
operator>=(Coefficient_traits::const_reference n, const Linear_Expression& e) {
  Linear_Expression diff(e, Constraint::default_representation);
  neg_assign(diff);
  diff += n;
  return Constraint(diff, Constraint::NONSTRICT_INEQUALITY, NECESSARILY_CLOSED);
}

/*! \relates Constraint */
inline Constraint
operator>(Coefficient_traits::const_reference n, const Linear_Expression& e) {
  Linear_Expression diff(e, Constraint::default_representation);
  neg_assign(diff);
  diff += n;
  Constraint c(diff, Constraint::STRICT_INEQUALITY, NOT_NECESSARILY_CLOSED);

  // NOTE: this also enforces normalization.
  c.set_epsilon_coefficient(-1);
  PPL_ASSERT(c.OK());

  return c;
}

/*! \relates Constraint */
inline Constraint
operator==(const Linear_Expression& e, Coefficient_traits::const_reference n) {
  Linear_Expression diff(e, Constraint::default_representation);
  diff -= n;
  return Constraint(diff, Constraint::EQUALITY, NECESSARILY_CLOSED);
}

/*! \relates Constraint */
inline Constraint
operator>=(const Linear_Expression& e, Coefficient_traits::const_reference n) {
  Linear_Expression diff(e, Constraint::default_representation);
  diff -= n;
  return Constraint(diff, Constraint::NONSTRICT_INEQUALITY, NECESSARILY_CLOSED);
}

/*! \relates Constraint */
inline Constraint
operator>(const Linear_Expression& e, Coefficient_traits::const_reference n) {
  Linear_Expression diff(e, Constraint::default_representation);
  diff -= n;
  Constraint c(diff, Constraint::STRICT_INEQUALITY, NOT_NECESSARILY_CLOSED);

  // NOTE: this also enforces normalization.
  c.set_epsilon_coefficient(-1);
  PPL_ASSERT(c.OK());

  return c;
}

/*! \relates Constraint */
inline Constraint
operator<=(const Linear_Expression& e1, const Linear_Expression& e2) {
  return e2 >= e1;
}

/*! \relates Constraint */
inline Constraint
operator<=(const Variable v1, const Variable v2) {
  return v2 >= v1;
}

/*! \relates Constraint */
inline Constraint
operator<=(Coefficient_traits::const_reference n, const Linear_Expression& e) {
  return e >= n;
}

/*! \relates Constraint */
inline Constraint
operator<=(const Linear_Expression& e, Coefficient_traits::const_reference n) {
  return n >= e;
}

/*! \relates Constraint */
inline Constraint
operator<(const Linear_Expression& e1, const Linear_Expression& e2) {
  return e2 > e1;
}

/*! \relates Constraint */
inline Constraint
operator<(const Variable v1, const Variable v2) {
  return v2 > v1;
}

/*! \relates Constraint */
inline Constraint
operator<(Coefficient_traits::const_reference n, const Linear_Expression& e) {
  return e > n;
}

/*! \relates Constraint */
inline Constraint
operator<(const Linear_Expression& e, Coefficient_traits::const_reference n) {
  return n > e;
}

inline const Constraint&
Constraint::zero_dim_false() {
  PPL_ASSERT(zero_dim_false_p != 0);
  return *zero_dim_false_p;
}

inline const Constraint&
Constraint::zero_dim_positivity() {
  PPL_ASSERT(zero_dim_positivity_p != 0);
  return *zero_dim_positivity_p;
}

inline const Constraint&
Constraint::epsilon_geq_zero() {
  PPL_ASSERT(epsilon_geq_zero_p != 0);
  return *epsilon_geq_zero_p;
}

inline const Constraint&
Constraint::epsilon_leq_one() {
  PPL_ASSERT(epsilon_leq_one_p != 0);
  return *epsilon_leq_one_p;
}

inline void
Constraint::m_swap(Constraint& y) {
  using std::swap;
  swap(expr, y.expr);
  swap(kind_, y.kind_);
  swap(topology_, y.topology_);
}

inline Coefficient_traits::const_reference
Constraint::epsilon_coefficient() const {
  PPL_ASSERT(is_not_necessarily_closed());
  return expr.coefficient(Variable(expr.space_dimension() - 1));
}

inline void
Constraint::set_epsilon_coefficient(Coefficient_traits::const_reference n) {
  PPL_ASSERT(is_not_necessarily_closed());
  expr.set_coefficient(Variable(expr.space_dimension() - 1), n);
}

/*! \relates Constraint */
inline void
swap(Constraint& x, Constraint& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Constraint_defs.hh line 835. */

/* Automatically generated from PPL source file ../src/Generator_defs.hh line 1. */
/* Generator class declaration.
*/


/* Automatically generated from PPL source file ../src/Generator_System_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Generator_System;
class Generator_System_const_iterator;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Grid_Generator_System_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Grid_Generator_System;

}

/* Automatically generated from PPL source file ../src/Generator_defs.hh line 38. */

/* Automatically generated from PPL source file ../src/distances_defs.hh line 1. */
/* Class declarations for several distances.
*/


/* Automatically generated from PPL source file ../src/distances_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename Temp>
struct Rectilinear_Distance_Specialization;

template <typename Temp>
struct Euclidean_Distance_Specialization;

template <typename Temp>
struct L_Infinity_Distance_Specialization;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/distances_defs.hh line 29. */

template <typename Temp>
struct Parma_Polyhedra_Library::Rectilinear_Distance_Specialization {
  static void combine(Temp& running, const Temp& current, Rounding_Dir dir);

  static void finalize(Temp&, Rounding_Dir);
};

template <typename Temp>
struct Parma_Polyhedra_Library::Euclidean_Distance_Specialization {
  static void combine(Temp& running, Temp& current, Rounding_Dir dir);

  static void finalize(Temp& running, Rounding_Dir dir);
};


template <typename Temp>
struct Parma_Polyhedra_Library::L_Infinity_Distance_Specialization {
  static void combine(Temp& running, const Temp& current, Rounding_Dir);

  static void finalize(Temp&, Rounding_Dir);
};

/* Automatically generated from PPL source file ../src/distances_inlines.hh line 1. */
/* Inline functions implementing distances.
*/


/* Automatically generated from PPL source file ../src/distances_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

// A struct to work around the lack of partial specialization
// of function templates in C++.
template <typename To, typename From>
struct maybe_assign_struct {
  static inline Result
  function(const To*& top, To& tmp, const From& from, Rounding_Dir dir) {
    // When `To' and `From' are different types, we make the conversion
    // and use `tmp'.
    top = &tmp;
    return assign_r(tmp, from, dir);
  }
};

template <typename Type>
struct maybe_assign_struct<Type, Type> {
  static inline Result
  function(const Type*& top, Type&, const Type& from, Rounding_Dir) {
    // When the types are the same, conversion is unnecessary.
    top = &from;
    return V_EQ;
  }
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Assigns to \p top a pointer to a location that holds the
  conversion, according to \p dir, of \p from to type \p To.  When
  necessary, and only when necessary, the variable \p tmp is used to
  hold the result of conversion.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename To, typename From>
inline Result
maybe_assign(const To*& top, To& tmp, const From& from, Rounding_Dir dir) {
  return maybe_assign_struct<To, From>::function(top, tmp, from, dir);
}

template <typename Temp>
inline void
Rectilinear_Distance_Specialization<Temp>::combine(Temp& running,
                                                   const Temp& current,
                                                   Rounding_Dir dir) {
  add_assign_r(running, running, current, dir);
}

template <typename Temp>
inline void
Rectilinear_Distance_Specialization<Temp>::finalize(Temp&, Rounding_Dir) {
}

template <typename Temp>
inline void
Euclidean_Distance_Specialization<Temp>::combine(Temp& running,
                                                 Temp& current,
                                                 Rounding_Dir dir) {
  mul_assign_r(current, current, current, dir);
  add_assign_r(running, running, current, dir);
}

template <typename Temp>
inline void
Euclidean_Distance_Specialization<Temp>::finalize(Temp& running,
                                                  Rounding_Dir dir) {
  sqrt_assign_r(running, running, dir);
}

template <typename Temp>
inline void
L_Infinity_Distance_Specialization<Temp>::combine(Temp& running,
                                                  const Temp& current,
                                                  Rounding_Dir) {
  if (current > running) {
    running = current;
  }
}

template <typename Temp>
inline void
L_Infinity_Distance_Specialization<Temp>::finalize(Temp&, Rounding_Dir) {
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/distances_defs.hh line 53. */

/* Automatically generated from PPL source file ../src/Expression_Hide_Inhomo_defs.hh line 1. */
/* Expression_Hide_Inhomo class declaration.
*/


/* Automatically generated from PPL source file ../src/Expression_Hide_Inhomo_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/Expression_Hide_Inhomo_defs.hh line 32. */

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  An adapter for Linear_Expression that hides the inhomogeneous term.

  The methods of this class always pretend that the value of the
  inhomogeneous term is zero.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Parma_Polyhedra_Library::Expression_Hide_Inhomo
  : public Expression_Adapter<T> {
  typedef Expression_Adapter<T> base_type;
public:
  //! The type of this object.
  typedef Expression_Hide_Inhomo<T> const_reference;
  //! The type obtained by one-level unwrapping.
  typedef typename base_type::inner_type inner_type;
  //! The raw, completely unwrapped type.
  typedef typename base_type::raw_type raw_type;

  //! Constructor.
  explicit Expression_Hide_Inhomo(const raw_type& expr);

public:
  //! The type of const iterators on coefficients.
  typedef typename base_type::const_iterator const_iterator;

  //! Returns the constant zero.
  Coefficient_traits::const_reference inhomogeneous_term() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is zero.
  bool is_zero() const;

  /*! \brief Returns \p true if \p *this is equal to \p y.

    Note that <CODE>(*this == y)</CODE> has a completely different meaning.
  */
  template <typename Expression>
  bool is_equal_to(const Expression& y) const;

  //! Returns the i-th coefficient.
  Coefficient_traits::const_reference get(dimension_type i) const;

  //! Returns the coefficient of v.
  Coefficient_traits::const_reference get(Variable v) const;

  /*! \brief
    Returns <CODE>true</CODE> if the coefficient of each variable in
    \p vars is zero.
  */
  bool all_zeroes(const Variables_Set& vars) const;

  /*! \brief
    Returns <CODE>true</CODE> if (*this)[i] is zero,
    for each i in [start, end).
  */
  bool all_zeroes(dimension_type start, dimension_type end) const;

  /*! \brief
    Returns the number of zero coefficient in [start, end).
  */
  dimension_type num_zeroes(dimension_type start, dimension_type end) const;

  /*! \brief
    Returns the gcd of the nonzero coefficients in [start,end). If all the
    coefficients in this range are zero, returns zero.
  */
  Coefficient gcd(dimension_type start, dimension_type end) const;

  //! Returns the index of the last nonzero element, or zero if there are no
  //! nonzero elements.
  dimension_type last_nonzero() const;

  //! Returns the index of the last nonzero element in [first,last), or last
  //! if there are no nonzero elements.
  dimension_type last_nonzero(dimension_type first, dimension_type last) const;

  //! Returns the index of the first nonzero element, or \p last if there
  //! are no nonzero elements, considering only elements in [first,last).
  dimension_type first_nonzero(dimension_type first, dimension_type last) const;

  /*! \brief
    Returns <CODE>true</CODE> if all coefficients in [start,end),
    except those corresponding to variables in \p vars, are zero.
  */
  bool all_zeroes_except(const Variables_Set& vars,
                         dimension_type start, dimension_type end) const;

  //! Removes from set \p x all the indexes of nonzero elements in \p *this.
  void has_a_free_dimension_helper(std::set<dimension_type>& x) const;

  //! Returns \c true if <CODE>(*this)[i]</CODE> is equal to <CODE>y[i]</CODE>,
  //! for each i in [start,end).
  template <typename Expression>
  bool is_equal_to(const Expression& y,
                   dimension_type start, dimension_type end) const;

  //! Returns \c true if <CODE>(*this)[i]*c1</CODE> is equal to
  //! <CODE>y[i]*c2</CODE>, for each i in [start,end).
  template <typename Expression>
  bool is_equal_to(const Expression& y,
                   Coefficient_traits::const_reference c1,
                   Coefficient_traits::const_reference c2,
                   dimension_type start, dimension_type end) const;

  //! Sets \p r to a copy of the row as adapted by \p *this.
  void get_row(Dense_Row& r) const;

  //! Sets \p r to a copy of the row as adapted by \p *this.
  void get_row(Sparse_Row& r) const;
};

/* Automatically generated from PPL source file ../src/Expression_Hide_Inhomo_inlines.hh line 1. */
/* Expression_Hide_Inhomo class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Expression_Hide_Inhomo_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename T>
Expression_Hide_Inhomo<T>::Expression_Hide_Inhomo(const raw_type& expr)
  : base_type(expr) {
}

template <typename T>
inline Coefficient_traits::const_reference
Expression_Hide_Inhomo<T>::inhomogeneous_term() const {
  // Pretend it is zero.
  return Coefficient_zero();
}

template <typename T>
inline bool
Expression_Hide_Inhomo<T>::is_zero() const {
  // Don't check the inhomogeneous_term (i.e., pretend it is zero).
  return this->inner().all_homogeneous_terms_are_zero();
}

template <typename T>
template <typename Expression>
inline bool
Expression_Hide_Inhomo<T>
::is_equal_to(const Expression& y) const {
  const dimension_type x_dim = this->space_dimension();
  const dimension_type y_dim = y.space_dimension();
  if (x_dim != y_dim) {
    return false;
  }
  if (y.inhomogeneous_term() != 0) {
    return false;
  }
  // Note that the inhomogeneous term is not compared.
  return this->inner().is_equal_to(y, 1, x_dim + 1);
}

template <typename T>
inline Coefficient_traits::const_reference
Expression_Hide_Inhomo<T>::get(dimension_type i) const {
  if (i == 0) {
    return Coefficient_zero();
  }
  else {
    return this->inner().get(i);
  }
}

template <typename T>
inline Coefficient_traits::const_reference
Expression_Hide_Inhomo<T>::get(Variable v) const {
  return this->inner().get(v);
}

template <typename T>
inline bool
Expression_Hide_Inhomo<T>
::all_zeroes(const Variables_Set& vars) const {
  return this->inner().all_zeroes(vars);
}

template <typename T>
inline bool
Expression_Hide_Inhomo<T>::all_zeroes(dimension_type start,
                                      dimension_type end) const {
  if (start == end) {
    return true;
  }
  if (start == 0) {
    ++start;
  }
  return this->inner().all_zeroes(start, end);
}

template <typename T>
inline dimension_type
Expression_Hide_Inhomo<T>::num_zeroes(dimension_type start,
                                      dimension_type end) const {
  if (start == end) {
    return 0;
  }
  dimension_type nz = 0;
  if (start == 0) {
    ++start;
    ++nz;
  }
  nz += this->inner().num_zeroes(start, end);
  return nz;
}

template <typename T>
inline Coefficient
Expression_Hide_Inhomo<T>::gcd(dimension_type start,
                               dimension_type end) const {
  if (start == end) {
    return Coefficient_zero();
  }
  if (start == 0) {
    ++start;
  }
  return this->inner().gcd(start, end);
}

template <typename T>
inline dimension_type
Expression_Hide_Inhomo<T>::last_nonzero() const {
  return this->inner().last_nonzero();
}

template <typename T>
inline dimension_type
Expression_Hide_Inhomo<T>::last_nonzero(dimension_type first,
                                        dimension_type last) const {
  if (first == last) {
    return last;
  }
  if (first == 0) {
    ++first;
  }
  return this->inner().last_nonzero(first, last);
}

template <typename T>
inline dimension_type
Expression_Hide_Inhomo<T>::first_nonzero(dimension_type first,
                                         dimension_type last) const {
  if (first == last) {
    return last;
  }
  if (first == 0) {
    ++first;
  }
  return this->inner().first_nonzero(first, last);
}

template <typename T>
inline bool
Expression_Hide_Inhomo<T>
::all_zeroes_except(const Variables_Set& vars,
                    dimension_type start, dimension_type end) const {
  if (start == end) {
    return true;
  }
  if (start == 0) {
    ++start;
  }
  return this->inner().all_zeroes_except(vars, start, end);
}

template <typename T>
inline void
Expression_Hide_Inhomo<T>
::has_a_free_dimension_helper(std::set<dimension_type>& y) const {
  bool had_0 = (y.count(0) == 1);
  this->inner().has_a_free_dimension_helper(y);
  if (had_0) {
    y.insert(0);
  }
}

template <typename T>
template <typename Expression>
inline bool
Expression_Hide_Inhomo<T>
::is_equal_to(const Expression& y,
              dimension_type start, dimension_type end) const {
  if (start == end) {
    return true;
  }
  if (start == 0) {
    ++start;
  }
  return this->inner().is_equal_to(y, start, end);
}

template <typename T>
template <typename Expression>
inline bool
Expression_Hide_Inhomo<T>
::is_equal_to(const Expression& y,
              Coefficient_traits::const_reference c1,
              Coefficient_traits::const_reference c2,
              dimension_type start, dimension_type end) const {
  if (start == end) {
    return true;
  }
  if (start == 0) {
    ++start;
  }
  return this->inner().is_equal_to(y, c1, c2, start, end);
}

template <typename T>
inline void
Expression_Hide_Inhomo<T>::get_row(Dense_Row& r) const {
  this->inner().get_row(r);
  r.reset(0);
}

template <typename T>
inline void
Expression_Hide_Inhomo<T>::get_row(Sparse_Row& r) const {
  this->inner().get_row(r);
  r.reset(0);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Expression_Hide_Inhomo_defs.hh line 146. */

/* Automatically generated from PPL source file ../src/Generator_defs.hh line 46. */

#include <iosfwd>

namespace Parma_Polyhedra_Library {

// Put them in the namespace here to declare them friend later.

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! The basic comparison function.
/*! \relates Generator
  \return
  The returned absolute value can be \f$0\f$, \f$1\f$ or \f$2\f$.

  \param x
  A row of coefficients;

  \param y
  Another row.

  Compares \p x and \p y, where \p x and \p y may be of different size,
  in which case the "missing" coefficients are assumed to be zero.
  The comparison is such that:
  -# equalities are smaller than inequalities;
  -# lines are smaller than points and rays;
  -# the ordering is lexicographic;
  -# the positions compared are, in decreasing order of significance,
     1, 2, ..., \p size(), 0;
  -# the result is negative, zero, or positive if x is smaller than,
     equal to, or greater than y, respectively;
  -# when \p x and \p y are different, the absolute value of the
     result is 1 if the difference is due to the coefficient in
     position 0; it is 2 otherwise.

  When \p x and \p y represent the hyper-planes associated
  to two equality or inequality constraints, the coefficient
  at 0 is the known term.
  In this case, the return value can be characterized as follows:
  - -2, if \p x is smaller than \p y and they are \e not parallel;
  - -1, if \p x is smaller than \p y and they \e are parallel;
  -  0, if \p x and y are equal;
  - +1, if \p y is smaller than \p x and they \e are parallel;
  - +2, if \p y is smaller than \p x and they are \e not parallel.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
int compare(const Generator& x, const Generator& y);

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Generator */
std::ostream& operator<<(std::ostream& s, const Generator& g);

} // namespace IO_Operators

//! Swaps \p x with \p y.
/*! \relates Generator */
void swap(Generator& x, Generator& y);

} // namespace Parma_Polyhedra_Library


//! A line, ray, point or closure point.
/*! \ingroup PPL_CXX_interface
  An object of the class Generator is one of the following:

  - a line \f$\vect{l} = (a_0, \ldots, a_{n-1})^\transpose\f$;

  - a ray \f$\vect{r} = (a_0, \ldots, a_{n-1})^\transpose\f$;

  - a point
    \f$\vect{p} = (\frac{a_0}{d}, \ldots, \frac{a_{n-1}}{d})^\transpose\f$;

  - a closure point
    \f$\vect{c} = (\frac{a_0}{d}, \ldots, \frac{a_{n-1}}{d})^\transpose\f$;

  where \f$n\f$ is the dimension of the space
  and, for points and closure points, \f$d > 0\f$ is the divisor.

  \par A note on terminology.
  As observed in Section \ref representation, there are cases when,
  in order to represent a polyhedron \f$\cP\f$ using the generator system
  \f$\cG = (L, R, P, C)\f$, we need to include in the finite set
  \f$P\f$ even points of \f$\cP\f$ that are <EM>not</EM> vertices
  of \f$\cP\f$.
  This situation is even more frequent when working with NNC polyhedra
  and it is the reason why we prefer to use the word `point'
  where other libraries use the word `vertex'.

  \par How to build a generator.
  Each type of generator is built by applying the corresponding
  function (<CODE>line</CODE>, <CODE>ray</CODE>, <CODE>point</CODE>
  or <CODE>closure_point</CODE>) to a linear expression,
  representing a direction in the space;
  the space dimension of the generator is defined as the space dimension
  of the corresponding linear expression.
  Linear expressions used to define a generator should be homogeneous
  (any constant term will be simply ignored).
  When defining points and closure points, an optional Coefficient argument
  can be used as a common <EM>divisor</EM> for all the coefficients
  occurring in the provided linear expression;
  the default value for this argument is 1.

  \par
  In all the following examples it is assumed that variables
  <CODE>x</CODE>, <CODE>y</CODE> and <CODE>z</CODE>
  are defined as follows:
  \code
  Variable x(0);
  Variable y(1);
  Variable z(2);
  \endcode

  \par Example 1
  The following code builds a line with direction \f$x-y-z\f$
  and having space dimension \f$3\f$:
  \code
  Generator l = line(x - y - z);
  \endcode
  As mentioned above, the constant term of the linear expression
  is not relevant. Thus, the following code has the same effect:
  \code
  Generator l = line(x - y - z + 15);
  \endcode
  By definition, the origin of the space is not a line, so that
  the following code throws an exception:
  \code
  Generator l = line(0*x);
  \endcode

  \par Example 2
  The following code builds a ray with the same direction as the
  line in Example 1:
  \code
  Generator r = ray(x - y - z);
  \endcode
  As is the case for lines, when specifying a ray the constant term
  of the linear expression is not relevant; also, an exception is thrown
  when trying to build a ray from the origin of the space.

  \par Example 3
  The following code builds the point
  \f$\vect{p} = (1, 0, 2)^\transpose \in \Rset^3\f$:
  \code
  Generator p = point(1*x + 0*y + 2*z);
  \endcode
  The same effect can be obtained by using the following code:
  \code
  Generator p = point(x + 2*z);
  \endcode
  Similarly, the origin \f$\vect{0} \in \Rset^3\f$ can be defined
  using either one of the following lines of code:
  \code
  Generator origin3 = point(0*x + 0*y + 0*z);
  Generator origin3_alt = point(0*z);
  \endcode
  Note however that the following code would have defined
  a different point, namely \f$\vect{0} \in \Rset^2\f$:
  \code
  Generator origin2 = point(0*y);
  \endcode
  The following two lines of code both define the only point
  having space dimension zero, namely \f$\vect{0} \in \Rset^0\f$.
  In the second case we exploit the fact that the first argument
  of the function <CODE>point</CODE> is optional.
  \code
  Generator origin0 = Generator::zero_dim_point();
  Generator origin0_alt = point();
  \endcode

  \par Example 4
  The point \f$\vect{p}\f$ specified in Example 3 above
  can also be obtained with the following code,
  where we provide a non-default value for the second argument
  of the function <CODE>point</CODE> (the divisor):
  \code
  Generator p = point(2*x + 0*y + 4*z, 2);
  \endcode
  Obviously, the divisor can be usefully exploited to specify
  points having some non-integer (but rational) coordinates.
  For instance, the point
  \f$\vect{q} = (-1.5, 3.2, 2.1)^\transpose \in \Rset^3\f$
  can be specified by the following code:
  \code
  Generator q = point(-15*x + 32*y + 21*z, 10);
  \endcode
  If a zero divisor is provided, an exception is thrown.

  \par Example 5
  Closure points are specified in the same way we defined points,
  but invoking their specific constructor function.
  For instance, the closure point
  \f$\vect{c} = (1, 0, 2)^\transpose \in \Rset^3\f$ is defined by
  \code
  Generator c = closure_point(1*x + 0*y + 2*z);
  \endcode
  For the particular case of the (only) closure point
  having space dimension zero, we can use any of the following:
  \code
  Generator closure_origin0 = Generator::zero_dim_closure_point();
  Generator closure_origin0_alt = closure_point();
  \endcode

  \par How to inspect a generator
  Several methods are provided to examine a generator and extract
  all the encoded information: its space dimension, its type and
  the value of its integer coefficients.

  \par Example 6
  The following code shows how it is possible to access each single
  coefficient of a generator.
  If <CODE>g1</CODE> is a point having coordinates
  \f$(a_0, \ldots, a_{n-1})^\transpose\f$,
  we construct the closure point <CODE>g2</CODE> having coordinates
  \f$(a_0, 2 a_1, \ldots, (i+1)a_i, \ldots, n a_{n-1})^\transpose\f$.
  \code
  if (g1.is_point()) {
    cout << "Point g1: " << g1 << endl;
    Linear_Expression e;
    for (dimension_type i = g1.space_dimension(); i-- > 0; )
      e += (i + 1) * g1.coefficient(Variable(i)) * Variable(i);
    Generator g2 = closure_point(e, g1.divisor());
    cout << "Closure point g2: " << g2 << endl;
  }
  else
    cout << "Generator g1 is not a point." << endl;
  \endcode
  Therefore, for the point
  \code
  Generator g1 = point(2*x - y + 3*z, 2);
  \endcode
  we would obtain the following output:
  \code
  Point g1: p((2*A - B + 3*C)/2)
  Closure point g2: cp((2*A - 2*B + 9*C)/2)
  \endcode
  When working with (closure) points, be careful not to confuse
  the notion of <EM>coefficient</EM> with the notion of <EM>coordinate</EM>:
  these are equivalent only when the divisor of the (closure) point is 1.
*/
class Parma_Polyhedra_Library::Generator {
public:

  //! The representation used for new Generators.
  /*!
    \note The copy constructor and the copy constructor with specified size
          use the representation of the original object, so that it is
          indistinguishable from the original object.
  */
  static const Representation default_representation = SPARSE;

  //! Returns the line of direction \p e.
  /*!
    \exception std::invalid_argument
    Thrown if the homogeneous part of \p e represents the origin of
    the vector space.
  */
  static Generator line(const Linear_Expression& e,
                        Representation r = default_representation);

  //! Returns the ray of direction \p e.
  /*!
    \exception std::invalid_argument
    Thrown if the homogeneous part of \p e represents the origin of
    the vector space.
  */
  static Generator ray(const Linear_Expression& e,
                       Representation r = default_representation);

  //! Returns the point at \p e / \p d.
  /*!
    Both \p e and \p d are optional arguments, with default values
    Linear_Expression::zero() and Coefficient_one(), respectively.

    \exception std::invalid_argument
    Thrown if \p d is zero.
  */
  static Generator point(const Linear_Expression& e
                         = Linear_Expression::zero(),
                         Coefficient_traits::const_reference d
                         = Coefficient_one(),
                         Representation r = default_representation);

  //! Returns the origin.
  static Generator point(Representation r);

  //! Returns the point at \p e.
  static Generator point(const Linear_Expression& e,
                         Representation r);

  //! Constructs the point at the origin.
  explicit Generator(Representation r = default_representation);

  //! Returns the closure point at \p e / \p d.
  /*!
    Both \p e and \p d are optional arguments, with default values
    Linear_Expression::zero() and Coefficient_one(), respectively.

    \exception std::invalid_argument
    Thrown if \p d is zero.
  */
  static Generator
  closure_point(const Linear_Expression& e = Linear_Expression::zero(),
                Coefficient_traits::const_reference d = Coefficient_one(),
                Representation r = default_representation);

  //! Returns the closure point at the origin.
  static Generator
  closure_point(Representation r);

  //! Returns the closure point at \p e.
  static Generator
  closure_point(const Linear_Expression& e, Representation r);

  //! Ordinary copy constructor.
  //! The representation of the new Generator will be the same as g.
  Generator(const Generator& g);

  //! Copy constructor with given representation.
  Generator(const Generator& g, Representation r);

  //! Copy constructor with given space dimension.
  //! The representation of the new Generator will be the same as g.
  Generator(const Generator& g, dimension_type space_dim);

  //! Copy constructor with given representation and space dimension.
  Generator(const Generator& g, dimension_type space_dim, Representation r);

  //! Destructor.
  ~Generator();

  //! Assignment operator.
  Generator& operator=(const Generator& g);

  //! Returns the current representation of *this.
  Representation representation() const;

  //! Converts *this to the specified representation.
  void set_representation(Representation r);

  //! Returns the maximum space dimension a Generator can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  //! Sets the dimension of the vector space enclosing \p *this to
  //! \p space_dim .
  void set_space_dimension(dimension_type space_dim);

  //! Swaps the coefficients of the variables \p v1 and \p v2 .
  void swap_space_dimensions(Variable v1, Variable v2);

  //! Removes all the specified dimensions from the generator.
  /*!
    The space dimension of the variable with the highest space
    dimension in \p vars must be at most the space dimension
    of \p this.

    If all dimensions with nonzero coefficients are removed from a ray or a
    line, it is changed into a point and this method returns \p false .
    Otherwise, it returns \p true .
  */
  bool remove_space_dimensions(const Variables_Set& vars);

  //! Permutes the space dimensions of the generator.
  /*!
    \param cycle
    A vector representing a cycle of the permutation according to which the
    space dimensions must be rearranged.

    The \p cycle vector represents a cycle of a permutation of space
    dimensions.
    For example, the permutation
    \f$ \{ x_1 \mapsto x_2, x_2 \mapsto x_3, x_3 \mapsto x_1 \}\f$ can be
    represented by the vector containing \f$ x_1, x_2, x_3 \f$.
  */
  void permute_space_dimensions(const std::vector<Variable>& cycle);

  //! Shift by \p n positions the coefficients of variables, starting from
  //! the coefficient of \p v. This increases the space dimension by \p n.
  void shift_space_dimensions(Variable v, dimension_type n);

  //! The generator type.
  enum Type {
    /*! The generator is a line. */
    LINE,
    /*! The generator is a ray. */
    RAY,
    /*! The generator is a point. */
    POINT,
    /*! The generator is a closure point. */
    CLOSURE_POINT
  };

  //! Returns the generator type of \p *this.
  Type type() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a line.
  bool is_line() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a ray.
  bool is_ray() const;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! Returns <CODE>true</CODE> if and only if \p *this is a line or a ray.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  bool is_line_or_ray() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a point.
  bool is_point() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a closure point.
  bool is_closure_point() const;

  //! Returns the coefficient of \p v in \p *this.
  /*!
    \exception std::invalid_argument
    Thrown if the index of \p v is greater than or equal to the
    space dimension of \p *this.
  */
  Coefficient_traits::const_reference coefficient(Variable v) const;

  //! If \p *this is either a point or a closure point, returns its divisor.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this is neither a point nor a closure point.
  */
  Coefficient_traits::const_reference divisor() const;

  //! Initializes the class.
  static void initialize();

  //! Finalizes the class.
  static void finalize();

  //! Returns the origin of the zero-dimensional space \f$\Rset^0\f$.
  static const Generator& zero_dim_point();

  /*! \brief
    Returns, as a closure point,
    the origin of the zero-dimensional space \f$\Rset^0\f$.
  */
  static const Generator& zero_dim_closure_point();

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this and \p y
    are equivalent generators.

    Generators having different space dimensions are not equivalent.
  */
  bool is_equivalent_to(const Generator& y) const;

  //! Returns <CODE>true</CODE> if \p *this is identical to \p y.
  /*!
    This is faster than is_equivalent_to(), but it may return `false' even
    for equivalent generators.
  */
  bool is_equal_to(const Generator& y) const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Swaps \p *this with \p y.
  void m_swap(Generator& y);

  //! The type of the (adapted) internal expression.
  typedef Expression_Hide_Last<Expression_Hide_Inhomo<Linear_Expression> >
  expr_type;
  //! Partial read access to the (adapted) internal expression.
  expr_type expression() const;

private:
  //! The possible kinds of Generator objects.
  enum Kind {
    LINE_OR_EQUALITY = 0,
    RAY_OR_POINT_OR_INEQUALITY = 1
  };

  //! The linear expression encoding \p *this.
  Linear_Expression expr;

  //! The kind of \p *this.
  Kind kind_;

  //! The topology of \p *this.
  Topology topology_;

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the origin of the zero-dimensional space \f$\Rset^0\f$.
  */
  static const Generator* zero_dim_point_p;

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the origin of the zero-dimensional space \f$\Rset^0\f$, as a closure point.
  */
  static const Generator* zero_dim_closure_point_p;

  /*! \brief
    Builds a generator of type \p type and topology \p topology,
    stealing the coefficients from \p e.

    If the topology is NNC, the last dimension of \p e is used as the epsilon
    coefficient.
  */
  Generator(Linear_Expression& e, Type type, Topology topology);

  Generator(Linear_Expression& e, Kind kind, Topology topology);

  Generator(dimension_type space_dim, Kind kind, Topology topology,
            Representation r = default_representation);

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this row
    represents a line or an equality.
  */
  bool is_line_or_equality() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this row
    represents a ray, a point or an inequality.
  */
  bool is_ray_or_point_or_inequality() const;

  //! Sets to \p LINE_OR_EQUALITY the kind of \p *this row.
  void set_is_line_or_equality();

  //! Sets to \p RAY_OR_POINT_OR_INEQUALITY the kind of \p *this row.
  void set_is_ray_or_point_or_inequality();

  //! \name Flags inspection methods
  //@{
  //! Returns the topological kind of \p *this.
  Topology topology() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if the topology
    of \p *this row is not necessarily closed.
  */
  bool is_not_necessarily_closed() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if the topology
    of \p *this row is necessarily closed.
  */
  bool is_necessarily_closed() const;
  //@} // Flags inspection methods

  //! \name Flags coercion methods
  //@{

  //! Sets to \p x the topological kind of \p *this row.
  void set_topology(Topology x);

  //! Sets to \p NECESSARILY_CLOSED the topological kind of \p *this row.
  void set_necessarily_closed();

  //! Sets to \p NOT_NECESSARILY_CLOSED the topological kind of \p *this row.
  void set_not_necessarily_closed();
  //@} // Flags coercion methods

  //! Marks the epsilon dimension as a standard dimension.
  /*!
    The row topology is changed to <CODE>NECESSARILY_CLOSED</CODE>, and
    the number of space dimensions is increased by 1.
  */
  void mark_as_necessarily_closed();

  //! Marks the last dimension as the epsilon dimension.
  /*!
    The row topology is changed to <CODE>NOT_NECESSARILY_CLOSED</CODE>, and
    the number of space dimensions is decreased by 1.
  */
  void mark_as_not_necessarily_closed();

  //! Linearly combines \p *this with \p y so that i-th coefficient is 0.
  /*!
    \param y
    The Generator that will be combined with \p *this object;

    \param i
    The index of the coefficient that has to become \f$0\f$.

    Computes a linear combination of \p *this and \p y having
    the i-th coefficient equal to \f$0\f$. Then it assigns
    the resulting Generator to \p *this and normalizes it.
  */
  void linear_combine(const Generator& y, dimension_type i);

  //! Sets the dimension of the vector space enclosing \p *this to
  //! \p space_dim .
  //! Sets the space dimension of the rows in the system to \p space_dim .
  /*!
    This method is for internal use, it does *not* assert OK() at the end,
    so it can be used for invalid objects.
  */
  void set_space_dimension_no_ok(dimension_type space_dim);

  /*! \brief
    Throw a <CODE>std::invalid_argument</CODE> exception
    containing the appropriate error message.
  */
  void
  throw_dimension_incompatible(const char* method,
                               const char* v_name,
                               Variable v) const;

  /*! \brief
    Throw a <CODE>std::invalid_argument</CODE> exception
    containing the appropriate error message.
  */
  void
  throw_invalid_argument(const char* method, const char* reason) const;

  //! Returns <CODE>true</CODE> if and only if \p *this is not a line.
  bool is_ray_or_point() const;

  //! Sets the Generator kind to <CODE>LINE_OR_EQUALITY</CODE>.
  void set_is_line();

  //! Sets the Generator kind to <CODE>RAY_OR_POINT_OR_INEQUALITY</CODE>.
  void set_is_ray_or_point();

  /*! \brief
    Returns <CODE>true</CODE> if and only if the closure point
    \p *this has the same \e coordinates of the point \p p.

    It is \e assumed that \p *this is a closure point, \p p is a point
    and both topologies and space dimensions agree.
  */
  bool is_matching_closure_point(const Generator& p) const;

  //! Returns the epsilon coefficient. The generator must be NNC.
  Coefficient_traits::const_reference epsilon_coefficient() const;

  //! Sets the epsilon coefficient to \p n. The generator must be NNC.
  void set_epsilon_coefficient(Coefficient_traits::const_reference n);

  /*! \brief
    Normalizes the sign of the coefficients so that the first non-zero
    (homogeneous) coefficient of a line-or-equality is positive.
  */
  void sign_normalize();

  /*! \brief
    Strong normalization: ensures that different Generator objects
    represent different hyperplanes or hyperspaces.

    Applies both Generator::normalize() and Generator::sign_normalize().
  */
  void strong_normalize();

  /*! \brief
    Returns <CODE>true</CODE> if and only if the coefficients are
    strongly normalized.
  */
  bool check_strong_normalized() const;

  /*! \brief
    A print function, with fancy, more human-friendly output.

    This is used by operator<<().
  */
  void fancy_print(std::ostream& s) const;

  friend class Expression_Adapter<Generator>;
  friend class Linear_System<Generator>;
  friend class Parma_Polyhedra_Library::Scalar_Products;
  friend class Parma_Polyhedra_Library::Topology_Adjusted_Scalar_Product_Sign;
  friend class Parma_Polyhedra_Library::Topology_Adjusted_Scalar_Product_Assign;
  friend class Parma_Polyhedra_Library::Generator_System;
  friend class Parma_Polyhedra_Library::Generator_System_const_iterator;
  // FIXME: the following friend declaration should be avoided.
  friend class Parma_Polyhedra_Library::Polyhedron;
  // This is for access to Linear_Expression in `insert'.
  friend class Parma_Polyhedra_Library::Grid_Generator_System;
  friend class Parma_Polyhedra_Library::MIP_Problem;
  friend class Parma_Polyhedra_Library::Grid;

  friend std::ostream&
  Parma_Polyhedra_Library::IO_Operators::operator<<(std::ostream& s,
                                                    const Generator& g);

  friend int
  compare(const Generator& x, const Generator& y);
};


namespace Parma_Polyhedra_Library {

//! Shorthand for Generator::line(const Linear_Expression& e, Representation r).
/*! \relates Generator */
Generator line(const Linear_Expression& e,
               Representation r = Generator::default_representation);

//! Shorthand for Generator::ray(const Linear_Expression& e, Representation r).
/*! \relates Generator */
Generator ray(const Linear_Expression& e,
              Representation r = Generator::default_representation);

/*! \brief
  Shorthand for
  Generator::point(const Linear_Expression& e, Coefficient_traits::const_reference d, Representation r).

  \relates Generator
*/
Generator
point(const Linear_Expression& e = Linear_Expression::zero(),
      Coefficient_traits::const_reference d = Coefficient_one(),
      Representation r = Generator::default_representation);

//! Shorthand for Generator::point(Representation r).
/*! \relates Generator */
Generator
point(Representation r);

/*! \brief
  Shorthand for
  Generator::point(const Linear_Expression& e, Representation r).

  \relates Generator
*/
Generator
point(const Linear_Expression& e, Representation r);

/*! \brief
  Shorthand for
  Generator::closure_point(const Linear_Expression& e, Coefficient_traits::const_reference d, Representation r).

  \relates Generator
*/
Generator
closure_point(const Linear_Expression& e = Linear_Expression::zero(),
              Coefficient_traits::const_reference d = Coefficient_one(),
              Representation r = Generator::default_representation);

//! Shorthand for Generator::closure_point(Representation r).
/*! \relates Generator */
Generator
closure_point(Representation r);

/*! \brief
  Shorthand for
  Generator::closure_point(const Linear_Expression& e, Representation r).

  \relates Generator
*/
Generator
closure_point(const Linear_Expression& e, Representation r);

//! Returns <CODE>true</CODE> if and only if \p x is equivalent to \p y.
/*! \relates Generator */
bool operator==(const Generator& x, const Generator& y);

//! Returns <CODE>true</CODE> if and only if \p x is not equivalent to \p y.
/*! \relates Generator */
bool operator!=(const Generator& x, const Generator& y);

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates Generator
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.

  \note
  Distances are \e only defined between generators that are points and/or
  closure points; for rays or lines, \c false is returned.
*/
template <typename To>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const Generator& x,
                                 const Generator& y,
                                 Rounding_Dir dir);

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates Generator
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.

  \note
  Distances are \e only defined between generators that are points and/or
  closure points; for rays or lines, \c false is returned.
*/
template <typename Temp, typename To>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const Generator& x,
                                 const Generator& y,
                                 Rounding_Dir dir);

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates Generator
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.

  \note
  Distances are \e only defined between generators that are points and/or
  closure points; for rays or lines, \c false is returned.
*/
template <typename Temp, typename To>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const Generator& x,
                                 const Generator& y,
                                 Rounding_Dir dir,
                                 Temp& tmp0,
                                 Temp& tmp1,
                                 Temp& tmp2);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates Generator
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.

  \note
  Distances are \e only defined between generators that are points and/or
  closure points; for rays or lines, \c false is returned.
*/
template <typename To>
bool euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const Generator& x,
                               const Generator& y,
                               Rounding_Dir dir);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates Generator
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.

  \note
  Distances are \e only defined between generators that are points and/or
  closure points; for rays or lines, \c false is returned.
*/
template <typename Temp, typename To>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const Generator& x,
                                 const Generator& y,
                                 Rounding_Dir dir);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates Generator
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.

  \note
  Distances are \e only defined between generators that are points and/or
  closure points; for rays or lines, \c false is returned.
*/
template <typename Temp, typename To>
bool euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const Generator& x,
                               const Generator& y,
                               Rounding_Dir dir,
                               Temp& tmp0,
                               Temp& tmp1,
                               Temp& tmp2);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates Generator
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.

  \note
  Distances are \e only defined between generators that are points and/or
  closure points; for rays or lines, \c false is returned.
*/
template <typename To>
bool l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const Generator& x,
                                const Generator& y,
                                Rounding_Dir dir);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates Generator
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.

  \note
  Distances are \e only defined between generators that are points and/or
  closure points; for rays or lines, \c false is returned.
*/
template <typename Temp, typename To>
bool l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const Generator& x,
                                const Generator& y,
                                Rounding_Dir dir);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates Generator
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.

  \note
  Distances are \e only defined between generators that are points and/or
  closure points; for rays or lines, \c false is returned.
*/
template <typename Temp, typename To>
bool l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const Generator& x,
                                const Generator& y,
                                Rounding_Dir dir,
                                Temp& tmp0,
                                Temp& tmp1,
                                Temp& tmp2);

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Generator */
std::ostream& operator<<(std::ostream& s, const Generator::Type& t);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Generator_inlines.hh line 1. */
/* Generator class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline bool
Generator::is_necessarily_closed() const {
  return (topology() == NECESSARILY_CLOSED);
}

inline bool
Generator::is_not_necessarily_closed() const {
  return (topology() == NOT_NECESSARILY_CLOSED);
}

inline Generator::expr_type
Generator::expression() const {
  return expr_type(expr, is_not_necessarily_closed());
}

inline dimension_type
Generator::space_dimension() const {
  return expression().space_dimension();
}

inline bool
Generator::is_line_or_equality() const {
  return (kind_ == LINE_OR_EQUALITY);
}

inline bool
Generator::is_ray_or_point_or_inequality() const {
  return (kind_ == RAY_OR_POINT_OR_INEQUALITY);
}

inline Topology
Generator::topology() const {
  return topology_;
}

inline void
Generator::set_is_line_or_equality() {
  kind_ = LINE_OR_EQUALITY;
}

inline void
Generator::set_is_ray_or_point_or_inequality() {
  kind_ = RAY_OR_POINT_OR_INEQUALITY;
}

inline void
Generator::set_topology(Topology x) {
  if (topology() == x) {
    return;
  }
  if (topology() == NECESSARILY_CLOSED) {
    // Add a column for the epsilon dimension.
    expr.set_space_dimension(expr.space_dimension() + 1);
  }
  else {
    PPL_ASSERT(expr.space_dimension() > 0);
    expr.set_space_dimension(expr.space_dimension() - 1);
  }
  topology_ = x;
}

inline void
Generator::mark_as_necessarily_closed() {
  PPL_ASSERT(is_not_necessarily_closed());
  topology_ = NECESSARILY_CLOSED;
}

inline void
Generator::mark_as_not_necessarily_closed() {
  PPL_ASSERT(is_necessarily_closed());
  topology_ = NOT_NECESSARILY_CLOSED;
}

inline void
Generator::set_necessarily_closed() {
  set_topology(NECESSARILY_CLOSED);
}

inline void
Generator::set_not_necessarily_closed() {
  set_topology(NOT_NECESSARILY_CLOSED);
}

inline
Generator::Generator(Representation r)
  : expr(r),
    kind_(RAY_OR_POINT_OR_INEQUALITY),
    topology_(NECESSARILY_CLOSED) {
  expr.set_inhomogeneous_term(Coefficient_one());
  PPL_ASSERT(space_dimension() == 0);
  PPL_ASSERT(OK());
}

inline
Generator::Generator(dimension_type space_dim, Kind kind, Topology topology,
                     Representation r)
  : expr(r),
    kind_(kind),
    topology_(topology) {
  if (is_necessarily_closed()) {
    expr.set_space_dimension(space_dim);
  }
  else {
    expr.set_space_dimension(space_dim + 1);
  }
  PPL_ASSERT(space_dimension() == space_dim);
  PPL_ASSERT(OK());
}

inline
Generator::Generator(Linear_Expression& e, Type type, Topology topology)
  : topology_(topology) {
  PPL_ASSERT(type != CLOSURE_POINT || topology == NOT_NECESSARILY_CLOSED);
  swap(expr, e);
  if (topology == NOT_NECESSARILY_CLOSED) {
    expr.set_space_dimension(expr.space_dimension() + 1);
  }
  if (type == LINE) {
    kind_ = LINE_OR_EQUALITY;
  }
  else {
    kind_ = RAY_OR_POINT_OR_INEQUALITY;
  }
  strong_normalize();
}

inline
Generator::Generator(Linear_Expression& e, Kind kind, Topology topology)
  : kind_(kind),
    topology_(topology) {
  swap(expr, e);
  if (topology == NOT_NECESSARILY_CLOSED) {
    expr.set_space_dimension(expr.space_dimension() + 1);
  }
  strong_normalize();
}

inline
Generator::Generator(const Generator& g)
  : expr(g.expr),
    kind_(g.kind_),
    topology_(g.topology_) {
}

inline
Generator::Generator(const Generator& g, Representation r)
  : expr(g.expr, r),
    kind_(g.kind_),
    topology_(g.topology_) {
  // This does not assert OK() because it's called by OK().
  PPL_ASSERT(OK());
}

inline
Generator::Generator(const Generator& g, dimension_type space_dim)
  : expr(g.expr, g.is_necessarily_closed() ? space_dim : (space_dim + 1)),
    kind_(g.kind_),
    topology_(g.topology_) {
  PPL_ASSERT(OK());
  PPL_ASSERT(space_dimension() == space_dim);
}

inline
Generator::Generator(const Generator& g, dimension_type space_dim,
                     Representation r)
  : expr(g.expr, g.is_necessarily_closed() ? space_dim : (space_dim + 1), r),
    kind_(g.kind_),
    topology_(g.topology_) {
  PPL_ASSERT(OK());
  PPL_ASSERT(space_dimension() == space_dim);
}

inline
Generator::~Generator() {
}

inline Generator&
Generator::operator=(const Generator& g) {
  Generator tmp = g;
  swap(*this, tmp);

  return *this;
}

inline Representation
Generator::representation() const {
  return expr.representation();
}

inline void
Generator::set_representation(Representation r) {
  expr.set_representation(r);
}

inline dimension_type
Generator::max_space_dimension() {
  return Linear_Expression::max_space_dimension();
}

inline void
Generator::set_space_dimension_no_ok(dimension_type space_dim) {
  const dimension_type old_expr_space_dim = expr.space_dimension();
  if (topology() == NECESSARILY_CLOSED) {
    expr.set_space_dimension(space_dim);
  }
  else {
    const dimension_type old_space_dim = space_dimension();
    if (space_dim > old_space_dim) {
      expr.set_space_dimension(space_dim + 1);
      expr.swap_space_dimensions(Variable(space_dim), Variable(old_space_dim));
    }
    else {
      expr.swap_space_dimensions(Variable(space_dim), Variable(old_space_dim));
      expr.set_space_dimension(space_dim + 1);
    }
  }
  PPL_ASSERT(space_dimension() == space_dim);
  if (expr.space_dimension() < old_expr_space_dim) {
    strong_normalize();
  }
}

inline void
Generator::set_space_dimension(dimension_type space_dim) {
  set_space_dimension_no_ok(space_dim);
  PPL_ASSERT(OK());
}

inline void
Generator::shift_space_dimensions(Variable v, dimension_type n) {
  expr.shift_space_dimensions(v, n);
}

inline bool
Generator::is_line() const {
  return is_line_or_equality();
}

inline bool
Generator::is_ray_or_point() const {
  return is_ray_or_point_or_inequality();
}

inline bool
Generator::is_line_or_ray() const {
  return expr.inhomogeneous_term() == 0;
}

inline bool
Generator::is_ray() const {
  return is_ray_or_point() && is_line_or_ray();
}

inline Generator::Type
Generator::type() const {
  if (is_line()) {
    return LINE;
  }
  if (is_line_or_ray()) {
    return RAY;
  }
  if (is_necessarily_closed()) {
    return POINT;
  }
  else {
    // Checking the value of the epsilon coefficient.
    if (epsilon_coefficient() == 0) {
      return CLOSURE_POINT;
    }
    else {
      return POINT;
    }
  }
}

inline bool
Generator::is_point() const {
  return type() == POINT;
}

inline bool
Generator::is_closure_point() const {
  return type() == CLOSURE_POINT;
}

inline void
Generator::set_is_line() {
  set_is_line_or_equality();
}

inline void
Generator::set_is_ray_or_point() {
  set_is_ray_or_point_or_inequality();
}

inline Coefficient_traits::const_reference
Generator::coefficient(const Variable v) const {
  if (v.space_dimension() > space_dimension()) {
    throw_dimension_incompatible("coefficient(v)", "v", v);
  }
  return expr.coefficient(v);
}

inline Coefficient_traits::const_reference
Generator::divisor() const {
  Coefficient_traits::const_reference d = expr.inhomogeneous_term();
  if (!is_ray_or_point() || d == 0) {
    throw_invalid_argument("divisor()",
                           "*this is neither a point nor a closure point");
  }
  return d;
}

inline Coefficient_traits::const_reference
Generator::epsilon_coefficient() const {
  PPL_ASSERT(is_not_necessarily_closed());
  return expr.coefficient(Variable(expr.space_dimension() - 1));
}


inline void
Generator::set_epsilon_coefficient(Coefficient_traits::const_reference n) {
  PPL_ASSERT(is_not_necessarily_closed());
  expr.set_coefficient(Variable(expr.space_dimension() - 1), n);
}


inline memory_size_type
Generator::external_memory_in_bytes() const {
  return expr.external_memory_in_bytes();
}

inline memory_size_type
Generator::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

inline void
Generator::strong_normalize() {
  expr.normalize();
  sign_normalize();
}

inline const Generator&
Generator::zero_dim_point() {
  PPL_ASSERT(zero_dim_point_p != 0);
  return *zero_dim_point_p;
}

inline const Generator&
Generator::zero_dim_closure_point() {
  PPL_ASSERT(zero_dim_closure_point_p != 0);
  return *zero_dim_closure_point_p;
}

/*! \relates Generator */
inline Generator
line(const Linear_Expression& e, Representation r) {
  return Generator::line(e, r);
}

/*! \relates Generator */
inline Generator
ray(const Linear_Expression& e, Representation r) {
  return Generator::ray(e, r);
}

/*! \relates Generator */
inline Generator
point(const Linear_Expression& e, Coefficient_traits::const_reference d,
      Representation r) {
  return Generator::point(e, d, r);
}

/*! \relates Generator */
inline Generator
point(Representation r) {
  return Generator::point(r);
}

/*! \relates Generator */
inline Generator
point(const Linear_Expression& e, Representation r) {
  return Generator::point(e, r);
}

/*! \relates Generator */
inline Generator
closure_point(const Linear_Expression& e,
              Coefficient_traits::const_reference d,
              Representation r) {
  return Generator::closure_point(e, d, r);
}

/*! \relates Generator */
inline Generator
closure_point(Representation r) {
  return Generator::closure_point(r);
}

/*! \relates Generator */
inline Generator
closure_point(const Linear_Expression& e,
              Representation r) {
  return Generator::closure_point(e, r);
}

/*! \relates Generator */
inline bool
operator==(const Generator& x, const Generator& y) {
  return x.is_equivalent_to(y);
}

/*! \relates Generator */
inline bool
operator!=(const Generator& x, const Generator& y) {
  return !x.is_equivalent_to(y);
}

inline void
Generator::ascii_dump(std::ostream& s) const {

  expr.ascii_dump(s);

  s << " ";

  switch (type()) {
  case Generator::LINE:
    s << "L ";
    break;
  case Generator::RAY:
    s << "R ";
    break;
  case Generator::POINT:
    s << "P ";
    break;
  case Generator::CLOSURE_POINT:
    s << "C ";
    break;
  }
  if (is_necessarily_closed()) {
    s << "(C)";
  }
  else {
    s << "(NNC)";
  }
  s << "\n";
}

inline bool
Generator::ascii_load(std::istream& s) {
  std::string str;

  expr.ascii_load(s);

  if (!(s >> str)) {
    return false;
  }
  if (str == "L") {
    set_is_line();
  }
  else if (str == "R" || str == "P" || str == "C") {
    set_is_ray_or_point();
  }
  else {
    return false;
  }

  std::string str2;

  if (!(s >> str2)) {
    return false;
  }
  if (str2 == "(C)") {
    if (is_not_necessarily_closed()) {
      // TODO: Avoid using the mark_as_*() methods if possible.
      mark_as_necessarily_closed();
    }
  }
  else {
    if (str2 == "(NNC)") {
      if (is_necessarily_closed()) {
        // TODO: Avoid using the mark_as_*() methods if possible.
        mark_as_not_necessarily_closed();
      }
    }
    else {
      return false;
    }
  }

  // Checking for equality of actual and declared types.
  switch (type()) {
  case Generator::LINE:
    if (str != "L") {
      return false;
    }
    break;
  case Generator::RAY:
    if (str != "R") {
      return false;
    }
    break;
  case Generator::POINT:
    if (str != "P") {
      return false;
    }
    break;
  case Generator::CLOSURE_POINT:
    if (str != "C") {
      return false;
    }
    break;
  }

  return true;
}

inline void
Generator::m_swap(Generator& y) {
  using std::swap;
  swap(expr, y.expr);
  swap(kind_, y.kind_);
  swap(topology_, y.topology_);
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Generator */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Specialization, typename Temp, typename To>
inline bool
l_m_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                    const Generator& x,
                    const Generator& y,
                    const Rounding_Dir dir,
                    Temp& tmp0,
                    Temp& tmp1,
                    Temp& tmp2) {
  // Generator kind compatibility check: we only compute distances
  // between (closure) points.
  if (x.is_line_or_ray() || y.is_line_or_ray()) {
    return false;
  }
  const dimension_type x_space_dim = x.space_dimension();
  // Dimension-compatibility check.
  if (x_space_dim != y.space_dimension()) {
    return false;
  }

  // All zero-dim generators have distance zero.
  if (x_space_dim == 0) {
    assign_r(r, 0, ROUND_NOT_NEEDED);
    return true;
  }

  PPL_DIRTY_TEMP(mpq_class, x_coord);
  PPL_DIRTY_TEMP(mpq_class, y_coord);
  PPL_DIRTY_TEMP(mpq_class, x_div);
  PPL_DIRTY_TEMP(mpq_class, y_div);
  assign_r(x_div, x.divisor(), ROUND_NOT_NEEDED);
  assign_r(y_div, y.divisor(), ROUND_NOT_NEEDED);

  assign_r(tmp0, 0, ROUND_NOT_NEEDED);
  // TODO: This loop can be optimized more, if needed.
  for (dimension_type i = x_space_dim; i-- > 0; ) {
    assign_r(x_coord, x.coefficient(Variable(i)), ROUND_NOT_NEEDED);
    div_assign_r(x_coord, x_coord, x_div, ROUND_NOT_NEEDED);
    assign_r(y_coord, y.coefficient(Variable(i)), ROUND_NOT_NEEDED);
    div_assign_r(y_coord, y_coord, y_div, ROUND_NOT_NEEDED);
    const Temp* tmp1p;
    const Temp* tmp2p;

    if (x_coord > y_coord) {
      maybe_assign(tmp1p, tmp1, x_coord, dir);
      maybe_assign(tmp2p, tmp2, y_coord, inverse(dir));
    }
    else {
      maybe_assign(tmp1p, tmp1, y_coord, dir);
      maybe_assign(tmp2p, tmp2, x_coord, inverse(dir));
    }
    sub_assign_r(tmp1, *tmp1p, *tmp2p, dir);
    PPL_ASSERT(sgn(tmp1) >= 0);
    Specialization::combine(tmp0, tmp1, dir);
  }
  Specialization::finalize(tmp0, dir);
  assign_r(r, tmp0, dir);
  return true;
}

/*! \relates Generator */
template <typename Temp, typename To>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Generator& x,
                            const Generator& y,
                            const Rounding_Dir dir,
                            Temp& tmp0,
                            Temp& tmp1,
                            Temp& tmp2) {
  return l_m_distance_assign<Rectilinear_Distance_Specialization<Temp> >
    (r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Generator */
template <typename Temp, typename To>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Generator& x,
                            const Generator& y,
                            const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return rectilinear_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Generator */
template <typename To>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Generator& x,
                            const Generator& y,
                            const Rounding_Dir dir) {
  return rectilinear_distance_assign<To, To>(r, x, y, dir);
}

/*! \relates Generator */
template <typename Temp, typename To>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Generator& x,
                          const Generator& y,
                          const Rounding_Dir dir,
                          Temp& tmp0,
                          Temp& tmp1,
                          Temp& tmp2) {
  return l_m_distance_assign<Euclidean_Distance_Specialization<Temp> >
    (r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Generator */
template <typename Temp, typename To>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Generator& x,
                          const Generator& y,
                          const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return euclidean_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Generator */
template <typename To>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Generator& x,
                          const Generator& y,
                          const Rounding_Dir dir) {
  return euclidean_distance_assign<To, To>(r, x, y, dir);
}

/*! \relates Generator */
template <typename Temp, typename To>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Generator& x,
                           const Generator& y,
                           const Rounding_Dir dir,
                           Temp& tmp0,
                           Temp& tmp1,
                           Temp& tmp2) {
  return l_m_distance_assign<L_Infinity_Distance_Specialization<Temp> >
    (r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Generator */
template <typename Temp, typename To>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Generator& x,
                           const Generator& y,
                           const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return l_infinity_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Generator */
template <typename To>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Generator& x,
                           const Generator& y,
                           const Rounding_Dir dir) {
  return l_infinity_distance_assign<To, To>(r, x, y, dir);
}

/*! \relates Generator */
inline void
swap(Generator& x, Generator& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Generator_defs.hh line 1032. */

/* Automatically generated from PPL source file ../src/Grid_Generator_defs.hh line 1. */
/* Grid_Generator class declaration.
*/


/* Automatically generated from PPL source file ../src/Grid_Generator_defs.hh line 29. */

/* Automatically generated from PPL source file ../src/Grid_Generator_defs.hh line 33. */

/* Automatically generated from PPL source file ../src/Grid_Generator_defs.hh line 39. */

/* Automatically generated from PPL source file ../src/Grid_Generator_defs.hh line 41. */
#include <iosfwd>

namespace Parma_Polyhedra_Library {

// Put these in the namespace here to declare them friend later.

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! The basic comparison function.
/*! \relates Grid_Generator
  \return
  The returned absolute value can be \f$0\f$, \f$1\f$ or \f$2\f$.

  \param x
  A row of coefficients;

  \param y
  Another row.

  Compares \p x and \p y, where \p x and \p y may be of different size,
  in which case the "missing" coefficients are assumed to be zero.
  The comparison is such that:
  -# equalities are smaller than inequalities;
  -# lines are smaller than points and rays;
  -# the ordering is lexicographic;
  -# the positions compared are, in decreasing order of significance,
     1, 2, ..., \p size(), 0;
  -# the result is negative, zero, or positive if x is smaller than,
     equal to, or greater than y, respectively;
  -# when \p x and \p y are different, the absolute value of the
     result is 1 if the difference is due to the coefficient in
     position 0; it is 2 otherwise.

  When \p x and \p y represent the hyper-planes associated
  to two equality or inequality constraints, the coefficient
  at 0 is the known term.
  In this case, the return value can be characterized as follows:
  - -2, if \p x is smaller than \p y and they are \e not parallel;
  - -1, if \p x is smaller than \p y and they \e are parallel;
  -  0, if \p x and y are equal;
  - +1, if \p y is smaller than \p x and they \e are parallel;
  - +2, if \p y is smaller than \p x and they are \e not parallel.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
int compare(const Grid_Generator& x, const Grid_Generator& y);

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Grid_Generator */
std::ostream& operator<<(std::ostream& s, const Grid_Generator& g);

} // namespace IO_Operators

//! Swaps \p x with \p y.
/*! \relates Grid_Generator */
void swap(Grid_Generator& x, Grid_Generator& y);

} // namespace Parma_Polyhedra_Library

//! A grid line, parameter or grid point.
/*! \ingroup PPL_CXX_interface
  An object of the class Grid_Generator is one of the following:

  - a grid_line \f$\vect{l} = (a_0, \ldots, a_{n-1})^\transpose\f$;

  - a parameter
    \f$\vect{q} = (\frac{a_0}{d}, \ldots, \frac{a_{n-1}}{d})^\transpose\f$;

  - a grid_point
    \f$\vect{p} = (\frac{a_0}{d}, \ldots, \frac{a_{n-1}}{d})^\transpose\f$;

  where \f$n\f$ is the dimension of the space
  and, for grid_points and parameters, \f$d > 0\f$ is the divisor.

  \par How to build a grid generator.
  Each type of generator is built by applying the corresponding
  function (<CODE>grid_line</CODE>, <CODE>parameter</CODE>
  or <CODE>grid_point</CODE>) to a linear expression;
  the space dimension of the generator is defined as the space dimension
  of the corresponding linear expression.
  Linear expressions used to define a generator should be homogeneous
  (any constant term will be simply ignored).
  When defining grid points and parameters, an optional Coefficient argument
  can be used as a common <EM>divisor</EM> for all the coefficients
  occurring in the provided linear expression;
  the default value for this argument is 1.

  \par
  In all the following examples it is assumed that variables
  <CODE>x</CODE>, <CODE>y</CODE> and <CODE>z</CODE>
  are defined as follows:
  \code
  Variable x(0);
  Variable y(1);
  Variable z(2);
  \endcode

  \par Example 1
  The following code builds a grid line with direction \f$x-y-z\f$
  and having space dimension \f$3\f$:
  \code
  Grid_Generator l = grid_line(x - y - z);
  \endcode
  By definition, the origin of the space is not a line, so that
  the following code throws an exception:
  \code
  Grid_Generator l = grid_line(0*x);
  \endcode

  \par Example 2
  The following code builds the parameter as the vector
  \f$\vect{p} = (1, -1, -1)^\transpose \in \Rset^3\f$
  which has the same direction as the line in Example 1:
  \code
  Grid_Generator q = parameter(x - y - z);
  \endcode
  Note that, unlike lines, for parameters, the length as well
  as the direction of the vector represented by the code is significant.
  Thus \p q is \e not the same as the parameter \p q1 defined by
  \code
  Grid_Generator q1 = parameter(2x - 2y - 2z);
  \endcode
  By definition, the origin of the space is not a parameter, so that
  the following code throws an exception:
  \code
  Grid_Generator q = parameter(0*x);
  \endcode

  \par Example 3
  The following code builds the grid point
  \f$\vect{p} = (1, 0, 2)^\transpose \in \Rset^3\f$:
  \code
  Grid_Generator p = grid_point(1*x + 0*y + 2*z);
  \endcode
  The same effect can be obtained by using the following code:
  \code
  Grid_Generator p = grid_point(x + 2*z);
  \endcode
  Similarly, the origin \f$\vect{0} \in \Rset^3\f$ can be defined
  using either one of the following lines of code:
  \code
  Grid_Generator origin3 = grid_point(0*x + 0*y + 0*z);
  Grid_Generator origin3_alt = grid_point(0*z);
  \endcode
  Note however that the following code would have defined
  a different point, namely \f$\vect{0} \in \Rset^2\f$:
  \code
  Grid_Generator origin2 = grid_point(0*y);
  \endcode
  The following two lines of code both define the only grid point
  having space dimension zero, namely \f$\vect{0} \in \Rset^0\f$.
  In the second case we exploit the fact that the first argument
  of the function <CODE>point</CODE> is optional.
  \code
  Grid_Generator origin0 = Generator::zero_dim_point();
  Grid_Generator origin0_alt = grid_point();
  \endcode

  \par Example 4
  The grid point \f$\vect{p}\f$ specified in Example 3 above
  can also be obtained with the following code,
  where we provide a non-default value for the second argument
  of the function <CODE>grid_point</CODE> (the divisor):
  \code
  Grid_Generator p = grid_point(2*x + 0*y + 4*z, 2);
  \endcode
  Obviously, the divisor can be used to specify
  points having some non-integer (but rational) coordinates.
  For instance, the grid point
  \f$\vect{p1} = (-1.5, 3.2, 2.1)^\transpose \in \Rset^3\f$
  can be specified by the following code:
  \code
  Grid_Generator p1 = grid_point(-15*x + 32*y + 21*z, 10);
  \endcode
  If a zero divisor is provided, an exception is thrown.

  \par Example 5
  Parameters, like grid points can have a divisor.
  For instance, the parameter
  \f$\vect{q} = (1, 0, 2)^\transpose \in \Rset^3\f$ can be defined:
  \code
  Grid_Generator q = parameter(2*x + 0*y + 4*z, 2);
  \endcode
  Also, the divisor can be used to specify
  parameters having some non-integer (but rational) coordinates.
  For instance, the parameter
  \f$\vect{q} = (-1.5, 3.2, 2.1)^\transpose \in \Rset^3\f$
  can be defined:
  \code
  Grid_Generator q = parameter(-15*x + 32*y + 21*z, 10);
  \endcode
  If a zero divisor is provided, an exception is thrown.

  \par How to inspect a grid generator
  Several methods are provided to examine a grid generator and extract
  all the encoded information: its space dimension, its type and
  the value of its integer coefficients and the value of the denominator.

  \par Example 6
  The following code shows how it is possible to access each single
  coefficient of a grid generator.
  If <CODE>g1</CODE> is a grid point having coordinates
  \f$(a_0, \ldots, a_{n-1})^\transpose\f$,
  we construct the parameter <CODE>g2</CODE> having coordinates
  \f$(a_0, 2 a_1, \ldots, (i+1)a_i, \ldots, n a_{n-1})^\transpose\f$.
  \code
  if (g1.is_point()) {
    cout << "Grid point g1: " << g1 << endl;
    Linear_Expression e;
    for (dimension_type i = g1.space_dimension(); i-- > 0; )
      e += (i + 1) * g1.coefficient(Variable(i)) * Variable(i);
    Grid_Generator g2 = parameter(e, g1.divisor());
    cout << "Parameter g2: " << g2 << endl;
  }
  else
    cout << "Grid generator g1 is not a grid point." << endl;
  \endcode
  Therefore, for the grid point
  \code
  Grid_Generator g1 = grid_point(2*x - y + 3*z, 2);
  \endcode
  we would obtain the following output:
  \code
  Grid point g1: p((2*A - B + 3*C)/2)
  Parameter g2: parameter((2*A - 2*B + 9*C)/2)
  \endcode
  When working with grid points and parameters, be careful not to confuse
  the notion of <EM>coefficient</EM> with the notion of <EM>coordinate</EM>:
  these are equivalent only when the divisor is 1.
*/
class Parma_Polyhedra_Library::Grid_Generator {
public:

  //! The possible kinds of Grid_Generator objects.
  enum Kind {
    LINE_OR_EQUALITY = 0,
    RAY_OR_POINT_OR_INEQUALITY = 1
  };

  //! The representation used for new Grid_Generators.
  /*!
    \note The copy constructor and the copy constructor with specified size
          use the representation of the original object, so that it is
          indistinguishable from the original object.
  */
  static const Representation default_representation = SPARSE;

  //! Returns the line of direction \p e.
  /*!
    \exception std::invalid_argument
    Thrown if the homogeneous part of \p e represents the origin of
    the vector space.
  */
  static Grid_Generator grid_line(const Linear_Expression& e,
                                  Representation r = default_representation);

  //! Returns the parameter of direction \p e and size \p e/d, with the same
  //! representation as e.
  /*!
    Both \p e and \p d are optional arguments, with default values
    Linear_Expression::zero() and Coefficient_one(), respectively.

    \exception std::invalid_argument
    Thrown if \p d is zero.
  */
  static Grid_Generator parameter(const Linear_Expression& e
                                  = Linear_Expression::zero(),
                                  Coefficient_traits::const_reference d
                                  = Coefficient_one(),
                                  Representation r = default_representation);

  // TODO: Improve the documentation of this method.
  //! Returns the parameter of direction and size \p Linear_Expression::zero() .
  static Grid_Generator parameter(Representation r);

  //! Returns the parameter of direction and size \p e .
  static Grid_Generator parameter(const Linear_Expression& e,
                                  Representation r);

  //! Returns the point at \p e / \p d.
  /*!
    Both \p e and \p d are optional arguments, with default values
    Linear_Expression::zero() and Coefficient_one(), respectively.

    \exception std::invalid_argument
    Thrown if \p d is zero.
  */
  static Grid_Generator grid_point(const Linear_Expression& e
                                   = Linear_Expression::zero(),
                                   Coefficient_traits::const_reference d
                                   = Coefficient_one(),
                                   Representation r = default_representation);

  //! Returns the point at \p e .
  static Grid_Generator grid_point(Representation r);

  //! Returns the point at \p e .
  static Grid_Generator grid_point(const Linear_Expression& e,
                                   Representation r);

  //! Returns the origin of the zero-dimensional space \f$\Rset^0\f$.
  explicit Grid_Generator(Representation r = default_representation);

  //! Ordinary copy constructor.
  //! The new Grid_Generator will have the same representation as g.
  Grid_Generator(const Grid_Generator& g);

  //! Copy constructor with specified representation.
  Grid_Generator(const Grid_Generator& g, Representation r);

  //! Copy constructor with specified space dimension.
  //! The new Grid_Generator will have the same representation as g.
  Grid_Generator(const Grid_Generator& g, dimension_type space_dim);

  //! Copy constructor with specified space dimension and representation.
  Grid_Generator(const Grid_Generator& g, dimension_type space_dim,
                 Representation r);

  //! Destructor.
  ~Grid_Generator();

  //! Assignment operator.
  Grid_Generator& operator=(const Grid_Generator& g);

  //! Returns the current representation of *this.
  Representation representation() const;

  //! Converts *this to the specified representation.
  void set_representation(Representation r);

  //! Returns the maximum space dimension a Grid_Generator can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  //! Sets the dimension of the vector space enclosing \p *this to
  //! \p space_dim .
  void set_space_dimension(dimension_type space_dim);

  //! Swaps the coefficients of the variables \p v1 and \p v2 .
  void swap_space_dimensions(Variable v1, Variable v2);

  //! Removes all the specified dimensions from the grid generator.
  /*!
    The space dimension of the variable with the highest space
    dimension in \p vars must be at most the space dimension
    of \p this.

    Always returns \p true. The return value is needed for compatibility with
    the Generator class.
  */
  bool remove_space_dimensions(const Variables_Set& vars);

  //! Permutes the space dimensions of the grid generator.
  /*
    \param cycle
    A vector representing a cycle of the permutation according to which the
    space dimensions must be rearranged.

    The \p cycle vector represents a cycle of a permutation of space
    dimensions.
    For example, the permutation
    \f$ \{ x_1 \mapsto x_2, x_2 \mapsto x_3, x_3 \mapsto x_1 \}\f$ can be
    represented by the vector containing \f$ x_1, x_2, x_3 \f$.
  */
  void permute_space_dimensions(const std::vector<Variable>& cycle);

  //! Shift by \p n positions the coefficients of variables, starting from
  //! the coefficient of \p v. This increases the space dimension by \p n.
  void shift_space_dimensions(Variable v, dimension_type n);

  //! The generator type.
  enum Type {
    /*! The generator is a grid line. */
    LINE,
    /*! The generator is a parameter. */
    PARAMETER,
    /*! The generator is a grid point. */
    POINT
  };

  //! Returns the generator type of \p *this.
  Type type() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a line.
  bool is_line() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a parameter.
  bool is_parameter() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is a line or
    a parameter.
  */
  bool is_line_or_parameter() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a point.
  bool is_point() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this row represents a
    parameter or a point.
  */
  bool is_parameter_or_point() const;

  //! Returns the coefficient of \p v in \p *this.
  /*!
    \exception std::invalid_argument
    Thrown if the index of \p v is greater than or equal to the
    space dimension of \p *this.
  */
  Coefficient_traits::const_reference coefficient(Variable v) const;

  //! Returns the divisor of \p *this.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this is a line.
  */
  Coefficient_traits::const_reference divisor() const;

  //! Initializes the class.
  static void initialize();

  //! Finalizes the class.
  static void finalize();

  //! Returns the origin of the zero-dimensional space \f$\Rset^0\f$.
  static const Grid_Generator& zero_dim_point();

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this and \p y are
    equivalent generators.

    Generators having different space dimensions are not equivalent.
  */
  bool is_equivalent_to(const Grid_Generator& y) const;

  //! Returns <CODE>true</CODE> if \p *this is identical to \p y.
  /*!
    This is faster than is_equivalent_to(), but it may return `false' even
    for equivalent generators.
  */
  bool is_equal_to(const Grid_Generator& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if all the homogeneous terms
    of \p *this are \f$0\f$.
  */
  bool all_homogeneous_terms_are_zero() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Swaps \p *this with \p y.
  void m_swap(Grid_Generator& y);

  /*! \brief
    Scales \p *this to be represented with a divisor of \p d (if
    \*this is a parameter or point). Does nothing at all on lines.

    It is assumed that \p d is a multiple of the current divisor
    and different from zero. The behavior is undefined if the assumption
    does not hold.
  */
  void scale_to_divisor(Coefficient_traits::const_reference d);

  //! Sets the divisor of \p *this to \p d.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this is a line.
  */
  void set_divisor(Coefficient_traits::const_reference d);

  //! The type of the (adapted) internal expression.
  typedef Expression_Hide_Last<Expression_Hide_Inhomo<Linear_Expression> >
  expr_type;
  //! Partial read access to the (adapted) internal expression.
  expr_type expression() const;

private:
  Linear_Expression expr;

  Kind kind_;

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the origin of the zero-dimensional space \f$\Rset^0\f$.
  */
  static const Grid_Generator* zero_dim_point_p;

  //! Constructs a Grid_Generator with the specified space dimension, kind
  //! and topology.
  Grid_Generator(dimension_type space_dim, Kind kind, Topology topology,
                 Representation r = default_representation);

  // TODO: Avoid reducing the space dimension.
  /*! \brief
    Constructs a grid generator of type \p t from linear expression \p e,
    stealing the underlying data structures from \p e.

    The last column in \p e becomes the parameter divisor column of
    the new Grid_Generator.

    \note The new Grid_Generator will have the same representation as `e'.
  */
  Grid_Generator(Linear_Expression& e, Type t);

  //! Sets the dimension of the vector space enclosing \p *this to
  //! \p space_dim .
  //! Sets the space dimension of the rows in the system to \p space_dim .
  /*!
    This method is for internal use, it does *not* assert OK() at the end,
    so it can be used for invalid objects.
  */
  void set_space_dimension_no_ok(dimension_type space_dim);

  /*! \brief
    Returns <CODE>true</CODE> if \p *this is equal to \p gg in
    dimension \p dim.
  */
  bool is_equal_at_dimension(dimension_type dim,
                             const Grid_Generator& gg) const;

  /*! \brief
    A print function, with fancy, more human-friendly output.

    This is used by operator<<().
  */
  void fancy_print(std::ostream& s) const;

  //! Converts the Grid_Generator into a parameter.
  void set_is_parameter();

  //! Sets the Grid_Generator kind to <CODE>LINE_OR_EQUALITY</CODE>.
  void set_is_line();

  //! Sets the Grid_Generator kind to <CODE>RAY_OR_POINT_OR_INEQUALITY</CODE>.
  void set_is_parameter_or_point();

  //! \name Flags inspection methods
  //@{
  //! Returns the topological kind of \p *this.
  Topology topology() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if the topology
    of \p *this row is not necessarily closed.
  */
  bool is_not_necessarily_closed() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if the topology
    of \p *this row is necessarily closed.
  */
  bool is_necessarily_closed() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this row
    represents a line or an equality.
  */
  bool is_line_or_equality() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this row
    represents a ray, a point or an inequality.
  */
  bool is_ray_or_point_or_inequality() const;
  //@} // Flags inspection methods

  //! \name Flags coercion methods
  //@{

  //! Sets to \p x the topological kind of \p *this row.
  void set_topology(Topology x);

  //! Sets to \p NECESSARILY_CLOSED the topological kind of \p *this row.
  void set_necessarily_closed();

  //! Sets to \p NOT_NECESSARILY_CLOSED the topological kind of \p *this row.
  void set_not_necessarily_closed();

  //! Sets to \p LINE_OR_EQUALITY the kind of \p *this row.
  void set_is_line_or_equality();

  //! Sets to \p RAY_OR_POINT_OR_INEQUALITY the kind of \p *this row.
  void set_is_ray_or_point_or_inequality();
  //@} // Flags coercion methods

  /*! \brief
    Normalizes the sign of the coefficients so that the first non-zero
    (homogeneous) coefficient of a line-or-equality is positive.
  */
  void sign_normalize();

  /*! \brief
    Strong normalization: ensures that different Grid_Generator objects
    represent different hyperplanes or hyperspaces.

    Applies both Grid_Generator::normalize() and Grid_Generator::sign_normalize().
  */
  void strong_normalize();

  /*! \brief
    Returns <CODE>true</CODE> if and only if the coefficients are
    strongly normalized.
  */
  bool check_strong_normalized() const;

  //! Linearly combines \p *this with \p y so that i-th coefficient is 0.
  /*!
    \param y
    The Grid_Generator that will be combined with \p *this object;

    \param i
    The index of the coefficient that has to become \f$0\f$.

    Computes a linear combination of \p *this and \p y having
    the i-th coefficient equal to \f$0\f$. Then it assigns
    the resulting Grid_Generator to \p *this and normalizes it.
  */
  void linear_combine(const Grid_Generator& y, dimension_type i);

  /*! \brief
    Throw a <CODE>std::invalid_argument</CODE> exception containing
    the appropriate error message.
  */
  void
  throw_dimension_incompatible(const char* method,
                               const char* name_var,
                               const Variable v) const;

  /*! \brief
    Throw a <CODE>std::invalid_argument</CODE> exception containing
    the appropriate error message.
  */
  void
  throw_invalid_argument(const char* method, const char* reason) const;

  friend std::ostream&
  IO_Operators::operator<<(std::ostream& s, const Grid_Generator& g);

  friend int
  compare(const Grid_Generator& x, const Grid_Generator& y);

  friend class Expression_Adapter<Grid_Generator>;
  friend class Grid_Generator_System;
  friend class Grid;
  friend class Linear_System<Grid_Generator>;
  friend class Scalar_Products;
  friend class Topology_Adjusted_Scalar_Product_Sign;
};


namespace Parma_Polyhedra_Library {

/*! \brief
  Shorthand for
  Grid_Generator::grid_line(const Linear_Expression& e, Representation r).

  \relates Grid_Generator
*/
Grid_Generator
grid_line(const Linear_Expression& e,
          Representation r = Grid_Generator::default_representation);

/*! \brief
  Shorthand for
  Grid_Generator::parameter(const Linear_Expression& e, Coefficient_traits::const_reference d, Representation r).

  \relates Grid_Generator
*/
Grid_Generator
parameter(const Linear_Expression& e = Linear_Expression::zero(),
          Coefficient_traits::const_reference d = Coefficient_one(),
          Representation r = Grid_Generator::default_representation);

//! Shorthand for Grid_Generator::parameter(Representation r).
/*! \relates Grid_Generator */
Grid_Generator
parameter(Representation r);

/*! \brief
  Shorthand for
  Grid_Generator::parameter(const Linear_Expression& e, Representation r).

  \relates Grid_Generator
*/
Grid_Generator
parameter(const Linear_Expression& e, Representation r);

/*! \brief
  Shorthand for
  Grid_Generator::grid_point(const Linear_Expression& e, Coefficient_traits::const_reference d, Representation r).

  \relates Grid_Generator
*/
Grid_Generator
grid_point(const Linear_Expression& e = Linear_Expression::zero(),
           Coefficient_traits::const_reference d = Coefficient_one(),
           Representation r = Grid_Generator::default_representation);

//! Shorthand for Grid_Generator::grid_point(Representation r).
/*! \relates Grid_Generator */
Grid_Generator
grid_point(Representation r);

/*! \brief
  Shorthand for
  Grid_Generator::grid_point(const Linear_Expression& e, Representation r).

  \relates Grid_Generator
*/
Grid_Generator
grid_point(const Linear_Expression& e, Representation r);

//! Returns <CODE>true</CODE> if and only if \p x is equivalent to \p y.
/*! \relates Grid_Generator */
bool operator==(const Grid_Generator& x, const Grid_Generator& y);

//! Returns <CODE>true</CODE> if and only if \p x is not equivalent to \p y.
/*! \relates Grid_Generator */
bool operator!=(const Grid_Generator& x, const Grid_Generator& y);


namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Grid_Generator */
std::ostream& operator<<(std::ostream& s, const Grid_Generator::Type& t);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Grid_Generator_inlines.hh line 1. */
/* Grid Generator class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline bool
Grid_Generator::is_necessarily_closed() const {
  return true;
}

inline bool
Grid_Generator::is_not_necessarily_closed() const {
  return false;
}

inline bool
Grid_Generator::is_line_or_equality() const {
  return (kind_ == LINE_OR_EQUALITY);
}

inline bool
Grid_Generator::is_ray_or_point_or_inequality() const {
  return (kind_ == RAY_OR_POINT_OR_INEQUALITY);
}

inline Topology
Grid_Generator::topology() const {
  return NECESSARILY_CLOSED;
}

inline void
Grid_Generator::set_is_line_or_equality() {
  kind_ = LINE_OR_EQUALITY;
}

inline void
Grid_Generator::set_is_ray_or_point_or_inequality() {
  kind_ = RAY_OR_POINT_OR_INEQUALITY;
}

inline void
Grid_Generator::set_topology(Topology x) {
  PPL_USED(x);
  PPL_ASSERT(x == NECESSARILY_CLOSED);
}

inline void
Grid_Generator::set_necessarily_closed() {
  set_topology(NECESSARILY_CLOSED);
}

inline void
Grid_Generator::set_not_necessarily_closed() {
  set_topology(NOT_NECESSARILY_CLOSED);
}

inline
Grid_Generator::Grid_Generator(Linear_Expression& e, Type type) {
  swap(expr, e);
  if (type == LINE) {
    kind_ = LINE_OR_EQUALITY;
  }
  else {
    kind_ = RAY_OR_POINT_OR_INEQUALITY;
  }
  PPL_ASSERT(OK());
}

inline
Grid_Generator::Grid_Generator(Representation r)
  : expr(Coefficient_one(), r),
    kind_(RAY_OR_POINT_OR_INEQUALITY) {
  expr.set_space_dimension(1);
  PPL_ASSERT(OK());
}

inline
Grid_Generator::Grid_Generator(const Grid_Generator& g)
  : expr(g.expr),
    kind_(g.kind_) {
}

inline
Grid_Generator::Grid_Generator(const Grid_Generator& g, Representation r)
  : expr(g.expr, r),
    kind_(g.kind_) {
}

inline
Grid_Generator::Grid_Generator(dimension_type space_dim, Kind kind,
                               Topology topology, Representation r)
  : expr(r),
    kind_(kind) {
  PPL_USED(topology);
  PPL_ASSERT(topology == NECESSARILY_CLOSED);
  expr.set_space_dimension(space_dim + 1);
  PPL_ASSERT(space_dimension() == space_dim);
}

inline
Grid_Generator::Grid_Generator(const Grid_Generator& g,
                               dimension_type space_dim)
  : expr(g.expr, space_dim + 1),
    kind_(g.kind_) {
  PPL_ASSERT(OK());
  PPL_ASSERT(space_dimension() == space_dim);
}

inline
Grid_Generator::Grid_Generator(const Grid_Generator& g,
                               dimension_type space_dim, Representation r)
  : expr(g.expr, space_dim + 1, r),
    kind_(g.kind_) {
  PPL_ASSERT(OK());
  PPL_ASSERT(space_dimension() == space_dim);
}

inline
Grid_Generator::~Grid_Generator() {
}

inline Grid_Generator::expr_type
Grid_Generator::expression() const {
  return expr_type(expr, true);
}

inline Representation
Grid_Generator::representation() const {
  return expr.representation();
}

inline void
Grid_Generator::set_representation(Representation r) {
  expr.set_representation(r);
}

inline dimension_type
Grid_Generator::max_space_dimension() {
  return Linear_Expression::max_space_dimension() - 1;
}

inline dimension_type
Grid_Generator::space_dimension() const {
  return expression().space_dimension();
}

inline void
Grid_Generator::set_space_dimension(dimension_type space_dim) {
  const dimension_type old_space_dim = space_dimension();
  if (space_dim > old_space_dim) {
    expr.set_space_dimension(space_dim + 1);
    expr.swap_space_dimensions(Variable(space_dim), Variable(old_space_dim));
  }
  else {
    expr.swap_space_dimensions(Variable(space_dim), Variable(old_space_dim));
    expr.set_space_dimension(space_dim + 1);
  }
  PPL_ASSERT(space_dimension() == space_dim);
}

inline void
Grid_Generator::set_space_dimension_no_ok(dimension_type space_dim) {
  set_space_dimension(space_dim);
}

inline void
Grid_Generator::shift_space_dimensions(Variable v, dimension_type n) {
  expr.shift_space_dimensions(v, n);
}

inline Grid_Generator::Type
Grid_Generator::type() const {
  if (is_line()) {
    return LINE;
  }
  return is_point() ? POINT : PARAMETER;
}

inline bool
Grid_Generator::is_line() const {
  return is_line_or_equality();
}

inline bool
Grid_Generator::is_parameter() const {
  return is_parameter_or_point() && is_line_or_parameter();
}

inline bool
Grid_Generator::is_line_or_parameter() const {
  return expr.inhomogeneous_term() == 0;
}

inline bool
Grid_Generator::is_point() const {
  return !is_line_or_parameter();
}

inline bool
Grid_Generator::is_parameter_or_point() const {
  return is_ray_or_point_or_inequality();
}

inline void
Grid_Generator::set_divisor(Coefficient_traits::const_reference d) {
  PPL_ASSERT(!is_line());
  if (is_line_or_parameter()) {
    expr.set_coefficient(Variable(space_dimension()), d);
  }
  else {
    expr.set_inhomogeneous_term(d);
  }
}

inline Coefficient_traits::const_reference
Grid_Generator::divisor() const {
  if (is_line()) {
    throw_invalid_argument("divisor()", "*this is a line");
  }
  if (is_line_or_parameter()) {
    return expr.coefficient(Variable(space_dimension()));
  }
  else {
    return expr.inhomogeneous_term();
  }
}

inline bool
Grid_Generator::is_equal_at_dimension(dimension_type dim,
                                      const Grid_Generator& y) const {
  const Grid_Generator& x = *this;
  return x.expr.get(dim) * y.divisor() == y.expr.get(dim) * x.divisor();
}

inline void
Grid_Generator::set_is_line() {
  set_is_line_or_equality();
}

inline void
Grid_Generator::set_is_parameter_or_point() {
  set_is_ray_or_point_or_inequality();
}

inline Grid_Generator&
Grid_Generator::operator=(const Grid_Generator& g) {
  Grid_Generator tmp = g;
  swap(*this, tmp);

  return *this;
}

inline Coefficient_traits::const_reference
Grid_Generator::coefficient(const Variable v) const {
  if (v.space_dimension() > space_dimension()) {
    throw_dimension_incompatible("coefficient(v)", "v", v);
  }
  return expr.coefficient(v);
}

inline memory_size_type
Grid_Generator::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

inline memory_size_type
Grid_Generator::external_memory_in_bytes() const {
  return expr.external_memory_in_bytes();
}

inline const Grid_Generator&
Grid_Generator::zero_dim_point() {
  PPL_ASSERT(zero_dim_point_p != 0);
  return *zero_dim_point_p;
}

inline void
Grid_Generator::strong_normalize() {
  PPL_ASSERT(!is_parameter());
  expr.normalize();
  sign_normalize();
}

inline void
Grid_Generator::m_swap(Grid_Generator& y) {
  using std::swap;
  swap(expr, y.expr);
  swap(kind_, y.kind_);
}

/*! \relates Grid_Generator */
inline bool
operator==(const Grid_Generator& x, const Grid_Generator& y) {
  return x.is_equivalent_to(y);
}

/*! \relates Grid_Generator */
inline bool
operator!=(const Grid_Generator& x, const Grid_Generator& y) {
  return !(x == y);
}

/*! \relates Grid_Generator */
inline Grid_Generator
grid_line(const Linear_Expression& e, Representation r) {
  return Grid_Generator::grid_line(e, r);
}

/*! \relates Grid_Generator */
inline Grid_Generator
parameter(const Linear_Expression& e,
          Coefficient_traits::const_reference d, Representation r) {
  return Grid_Generator::parameter(e, d, r);
}

/*! \relates Grid_Generator */
inline Grid_Generator
parameter(Representation r) {
  return Grid_Generator::parameter(r);
}

/*! \relates Grid_Generator */
inline Grid_Generator
parameter(const Linear_Expression& e, Representation r) {
  return Grid_Generator::parameter(e, r);
}

/*! \relates Grid_Generator */
inline Grid_Generator
grid_point(const Linear_Expression& e,
           Coefficient_traits::const_reference d, Representation r) {
  return Grid_Generator::grid_point(e, d, r);
}

/*! \relates Grid_Generator */
inline Grid_Generator
grid_point(Representation r) {
  return Grid_Generator::grid_point(r);
}

/*! \relates Grid_Generator */
inline Grid_Generator
grid_point(const Linear_Expression& e, Representation r) {
  return Grid_Generator::grid_point(e, r);
}

/*! \relates Grid_Generator */
inline void
swap(Grid_Generator& x, Grid_Generator& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Grid_Generator_defs.hh line 795. */

/* Automatically generated from PPL source file ../src/Congruence_defs.hh line 1. */
/* Congruence class declaration.
*/


/* Automatically generated from PPL source file ../src/Congruence_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/Congruence_defs.hh line 31. */

/* Automatically generated from PPL source file ../src/Congruence_defs.hh line 37. */

#include <iosfwd>
#include <vector>

// These are declared here because they are friend of Congruence.
namespace Parma_Polyhedra_Library {

//! Returns <CODE>true</CODE> if and only if \p x and \p y are equivalent.
/*! \relates Congruence */
bool
operator==(const Congruence& x, const Congruence& y);

//! Returns <CODE>false</CODE> if and only if \p x and \p y are equivalent.
/*! \relates Congruence */
bool
operator!=(const Congruence& x, const Congruence& y);

} // namespace Parma_Polyhedra_Library


//! A linear congruence.
/*! \ingroup PPL_CXX_interface
  An object of the class Congruence is a congruence:
  - \f$\cg = \sum_{i=0}^{n-1} a_i x_i + b = 0 \pmod m\f$

  where \f$n\f$ is the dimension of the space,
  \f$a_i\f$ is the integer coefficient of variable \f$x_i\f$,
  \f$b\f$ is the integer inhomogeneous term and \f$m\f$ is the integer modulus;
  if \f$m = 0\f$, then \f$\cg\f$ represents the equality congruence
  \f$\sum_{i=0}^{n-1} a_i x_i + b = 0\f$
  and, if \f$m \neq 0\f$, then the congruence \f$\cg\f$ is
  said to be a proper congruence.

  \par How to build a congruence
  Congruences \f$\pmod{1}\f$ are typically built by
  applying the congruence symbol `<CODE>\%=</CODE>'
  to a pair of linear expressions.
  Congruences with modulus \p m
  are typically constructed by building a congruence \f$\pmod{1}\f$
  using the given pair of linear expressions
  and then adding the modulus \p m
  using the modulus symbol is `<CODE>/</CODE>'.

  The space dimension of a congruence is defined as the maximum
  space dimension of the arguments of its constructor.

  \par
  In the following examples it is assumed that variables
  <CODE>x</CODE>, <CODE>y</CODE> and <CODE>z</CODE>
  are defined as follows:
  \code
  Variable x(0);
  Variable y(1);
  Variable z(2);
  \endcode

  \par Example 1
  The following code builds the equality congruence
  \f$3x + 5y - z = 0\f$, having space dimension \f$3\f$:
  \code
  Congruence eq_cg((3*x + 5*y - z %= 0) / 0);
  \endcode
  The following code builds the congruence
  \f$4x = 2y - 13 \pmod{1}\f$, having space dimension \f$2\f$:
  \code
  Congruence mod1_cg(4*x %= 2*y - 13);
  \endcode
  The following code builds the congruence
  \f$4x = 2y - 13 \pmod{2}\f$, having space dimension \f$2\f$:
  \code
  Congruence mod2_cg((4*x %= 2*y - 13) / 2);
  \endcode
  An unsatisfiable congruence on the zero-dimension space \f$\Rset^0\f$
  can be specified as follows:
  \code
  Congruence false_cg = Congruence::zero_dim_false();
  \endcode
  Equivalent, but more involved ways are the following:
  \code
  Congruence false_cg1((Linear_Expression::zero() %= 1) / 0);
  Congruence false_cg2((Linear_Expression::zero() %= 1) / 2);
  \endcode
  In contrast, the following code defines an unsatisfiable congruence
  having space dimension \f$3\f$:
  \code
  Congruence false_cg3((0*z %= 1) / 0);
  \endcode

  \par How to inspect a congruence
  Several methods are provided to examine a congruence and extract
  all the encoded information: its space dimension, its modulus
  and the value of its integer coefficients.

  \par Example 2
  The following code shows how it is possible to access the modulus
  as well as each of the coefficients.
  Given a congruence with linear expression \p e and modulus \p m
  (in this case \f$x - 5y + 3z = 4 \pmod{5}\f$), we construct a new
  congruence with the same modulus \p m but where the linear
  expression is \f$2 e\f$ (\f$2x - 10y + 6z = 8 \pmod{5}\f$).
  \code
  Congruence cg1((x - 5*y + 3*z %= 4) / 5);
  cout << "Congruence cg1: " << cg1 << endl;
  const Coefficient& m = cg1.modulus();
  if (m == 0)
    cout << "Congruence cg1 is an equality." << endl;
  else {
    Linear_Expression e;
    for (dimension_type i = cg1.space_dimension(); i-- > 0; )
      e += 2 * cg1.coefficient(Variable(i)) * Variable(i);
      e += 2 * cg1.inhomogeneous_term();
    Congruence cg2((e %= 0) / m);
    cout << "Congruence cg2: " << cg2 << endl;
  }
  \endcode
  The actual output could be the following:
  \code
  Congruence cg1: A - 5*B + 3*C %= 4 / 5
  Congruence cg2: 2*A - 10*B + 6*C %= 8 / 5
  \endcode
  Note that, in general, the particular output obtained can be
  syntactically different from the (semantically equivalent)
  congruence considered.
*/
class Parma_Polyhedra_Library::Congruence {
public:

  //! The representation used for new Congruences.
  /*!
    \note The copy constructor and the copy constructor with specified size
          use the representation of the original object, so that it is
          indistinguishable from the original object.
  */
  static const Representation default_representation = SPARSE;

  //! Constructs the 0 = 0 congruence with space dimension \p 0 .
  explicit Congruence(Representation r = default_representation);

  //! Ordinary copy constructor.
  /*!
    \note The new Congruence will have the same representation as `cg',
          not default_representation, so that they are indistinguishable.
  */
  Congruence(const Congruence& cg);

  //! Copy constructor with specified representation.
  Congruence(const Congruence& cg, Representation r);

  //! Copy-constructs (modulo 0) from equality constraint \p c.
  /*!
    \exception std::invalid_argument
    Thrown if \p c is an inequality.
  */
  explicit Congruence(const Constraint& c,
                      Representation r = default_representation);

  //! Destructor.
  ~Congruence();

  //! Assignment operator.
  Congruence& operator=(const Congruence& y);

  //! Returns the current representation of *this.
  Representation representation() const;

  //! Converts *this to the specified representation.
  void set_representation(Representation r);

  //! Returns the maximum space dimension a Congruence can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  void permute_space_dimensions(const std::vector<Variable>& cycles);

  //! The type of the (adapted) internal expression.
  typedef Expression_Adapter_Transparent<Linear_Expression> expr_type;
  //! Partial read access to the (adapted) internal expression.
  expr_type expression() const;

  //! Returns the coefficient of \p v in \p *this.
  /*!
    \exception std::invalid_argument thrown if the index of \p v
    is greater than or equal to the space dimension of \p *this.
  */
  Coefficient_traits::const_reference coefficient(Variable v) const;

  //! Returns the inhomogeneous term of \p *this.
  Coefficient_traits::const_reference inhomogeneous_term() const;

  //! Returns a const reference to the modulus of \p *this.
  Coefficient_traits::const_reference modulus() const;

  //! Sets the modulus of \p *this to \p m .
  //! If \p m is 0, the congruence becomes an equality.
  void set_modulus(Coefficient_traits::const_reference m);

  //! Multiplies all the coefficients, including the modulus, by \p factor .
  void scale(Coefficient_traits::const_reference factor);

  // TODO: Document this.
  void affine_preimage(Variable v,
                       const Linear_Expression& expr,
                       Coefficient_traits::const_reference denominator);

  //! Multiplies \p k into the modulus of \p *this.
  /*!
    If called with \p *this representing the congruence \f$ e_1 = e_2
    \pmod{m}\f$, then it returns with *this representing
    the congruence \f$ e_1 = e_2 \pmod{mk}\f$.
  */
  Congruence&
  operator/=(Coefficient_traits::const_reference k);

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is a tautology
    (i.e., an always true congruence).

    A tautological congruence has one the following two forms:
    - an equality: \f$\sum_{i=0}^{n-1} 0 x_i + 0 == 0\f$; or
    - a proper congruence: \f$\sum_{i=0}^{n-1} 0 x_i + b \%= 0 / m\f$,
      where \f$b = 0 \pmod{m}\f$.
  */
  bool is_tautological() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this is inconsistent (i.e., an always false congruence).

    An inconsistent congruence has one of the following two forms:
    - an equality: \f$\sum_{i=0}^{n-1} 0 x_i + b == 0\f$
      where \f$b \neq 0\f$; or
    - a proper congruence: \f$\sum_{i=0}^{n-1} 0 x_i + b \%= 0 / m\f$,
      where \f$b \neq 0 \pmod{m}\f$.
  */
  bool is_inconsistent() const;

  //! Returns <CODE>true</CODE> if the modulus is greater than zero.
  /*!
    A congruence with a modulus of 0 is a linear equality.
  */
  bool is_proper_congruence() const;

  //! Returns <CODE>true</CODE> if \p *this is an equality.
  /*!
    A modulus of zero denotes a linear equality.
  */
  bool is_equality() const;

  //! Initializes the class.
  static void initialize();

  //! Finalizes the class.
  static void finalize();

  /*! \brief
    Returns a reference to the true (zero-dimension space) congruence
    \f$0 = 1 \pmod{1}\f$, also known as the <EM>integrality
    congruence</EM>.
  */
  static const Congruence& zero_dim_integrality();

  /*! \brief
    Returns a reference to the false (zero-dimension space) congruence
    \f$0 = 1 \pmod{0}\f$.
  */
  static const Congruence& zero_dim_false();

  //! Returns the congruence \f$e1 = e2 \pmod{1}\f$.
  static Congruence
  create(const Linear_Expression& e1, const Linear_Expression& e2,
         Representation r = default_representation);

  //! Returns the congruence \f$e = n \pmod{1}\f$.
  static Congruence
  create(const Linear_Expression& e, Coefficient_traits::const_reference n,
         Representation r = default_representation);

  //! Returns the congruence \f$n = e \pmod{1}\f$.
  static Congruence
  create(Coefficient_traits::const_reference n, const Linear_Expression& e,
         Representation r = default_representation);

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation of the internal
    representation of \p *this.
  */
  bool ascii_load(std::istream& s);

  //! Swaps \p *this with \p y.
  void m_swap(Congruence& y);

  //! Copy-constructs with the specified space dimension.
  /*!
    \note The new Congruence will have the same representation as `cg',
          not default_representation, for consistency with the copy
          constructor.
  */
  Congruence(const Congruence& cg, dimension_type new_space_dimension);

  //! Copy-constructs with the specified space dimension and representation.
  Congruence(const Congruence& cg, dimension_type new_space_dimension,
             Representation r);

  //! Copy-constructs from a constraint, with the specified space dimension
  //! and (optional) representation.
  Congruence(const Constraint& cg, dimension_type new_space_dimension,
             Representation r = default_representation);

  //! Constructs from Linear_Expression \p le, using modulus \p m.
  /*!
    Builds a congruence with modulus \p m, stealing the coefficients
    from \p le.

    \note The new Congruence will have the same representation as `le'.

    \param le
    The Linear_Expression holding the coefficients.

    \param m
    The modulus for the congruence, which must be zero or greater.
  */
  Congruence(Linear_Expression& le,
             Coefficient_traits::const_reference m, Recycle_Input);

  //! Swaps the coefficients of the variables \p v1 and \p v2 .
  void swap_space_dimensions(Variable v1, Variable v2);

  //! Sets the space dimension by \p n , adding or removing coefficients as
  //! needed.
  void set_space_dimension(dimension_type n);

  //! Shift by \p n positions the coefficients of variables, starting from
  //! the coefficient of \p v. This increases the space dimension by \p n.
  void shift_space_dimensions(Variable v, dimension_type n);

  //! Normalizes the signs.
  /*!
    The signs of the coefficients and the inhomogeneous term are
    normalized, leaving the first non-zero homogeneous coefficient
    positive.
  */
  void sign_normalize();

  //! Normalizes signs and the inhomogeneous term.
  /*!
    Applies sign_normalize, then reduces the inhomogeneous term to the
    smallest possible positive number.
  */
  void normalize();

  //! Calls normalize, then divides out common factors.
  /*!
    Strongly normalized Congruences have equivalent semantics if and
    only if they have the same syntax (as output by operator<<).
  */
  void strong_normalize();

private:
  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the false (zero-dimension space) congruence \f$0 = 1 \pmod{0}\f$.
  */
  static const Congruence* zero_dim_false_p;

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the true (zero-dimension space) congruence \f$0 = 1 \pmod{1}\f$,
    also known as the <EM>integrality congruence</EM>.
  */
  static const Congruence* zero_dim_integrality_p;

  Linear_Expression expr;

  Coefficient modulus_;

  /*! \brief
    Returns <CODE>true</CODE> if \p *this is equal to \p cg in
    dimension \p v.
  */
  bool is_equal_at_dimension(Variable v,
                             const Congruence& cg) const;

  /*! \brief
    Throws a <CODE>std::invalid_argument</CODE> exception containing
    error message \p message.
  */
  void
  throw_invalid_argument(const char* method, const char* message) const;

  /*! \brief
    Throws a <CODE>std::invalid_argument</CODE> exception containing
    the appropriate error message.
  */
  void
  throw_dimension_incompatible(const char* method,
                               const char* v_name,
                               Variable v) const;

  friend bool
  operator==(const Congruence& x, const Congruence& y);

  friend bool
  operator!=(const Congruence& x, const Congruence& y);

  friend class Scalar_Products;
  friend class Grid;
};

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operators.

/*! \relates Parma_Polyhedra_Library::Congruence */
std::ostream&
operator<<(std::ostream& s, const Congruence& c);

} // namespace IO_Operators

//! Returns the congruence \f$e1 = e2 \pmod{1}\f$.
/*! \relates Congruence */
Congruence
operator%=(const Linear_Expression& e1, const Linear_Expression& e2);

//! Returns the congruence \f$e = n \pmod{1}\f$.
/*! \relates Congruence */
Congruence
operator%=(const Linear_Expression& e, Coefficient_traits::const_reference n);

//! Returns a copy of \p cg, multiplying \p k into the copy's modulus.
/*!
    If \p cg represents the congruence \f$ e_1 = e_2
    \pmod{m}\f$, then the result represents the
    congruence \f$ e_1 = e_2 \pmod{mk}\f$.
  \relates Congruence
*/
Congruence
operator/(const Congruence& cg, Coefficient_traits::const_reference k);

//! Creates a congruence from \p c, with \p m as the modulus.
/*! \relates Congruence */
Congruence
operator/(const Constraint& c, Coefficient_traits::const_reference m);

/*! \relates Congruence */
void
swap(Congruence& x, Congruence& y);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Congruence_inlines.hh line 1. */
/* Congruence class implementation: inline functions.
*/


#include <sstream>

namespace Parma_Polyhedra_Library {

inline
Congruence::Congruence(Representation r)
  : expr(r) {
  PPL_ASSERT(OK());
}

inline
Congruence::Congruence(const Congruence& cg)
  : expr(cg.expr), modulus_(cg.modulus_) {
}

inline
Congruence::Congruence(const Congruence& cg, Representation r)
  : expr(cg.expr, r), modulus_(cg.modulus_) {
}

inline
Congruence::Congruence(const Congruence& cg,
                       dimension_type new_space_dimension)
  : expr(cg.expr, new_space_dimension), modulus_(cg.modulus_) {
  PPL_ASSERT(OK());
}

inline
Congruence::Congruence(const Congruence& cg,
                       dimension_type new_space_dimension,
                       Representation r)
  : expr(cg.expr, new_space_dimension, r), modulus_(cg.modulus_) {
  PPL_ASSERT(OK());
}

inline Representation
Congruence::representation() const {
  return expr.representation();
}

inline void
Congruence::set_representation(Representation r) {
  expr.set_representation(r);
}

inline Congruence::expr_type
Congruence::expression() const {
  return expr_type(expr);
}

inline void
Congruence::set_space_dimension(dimension_type n) {
  expr.set_space_dimension(n);
  PPL_ASSERT(OK());
}

inline void
Congruence::shift_space_dimensions(Variable v, dimension_type n) {
  expr.shift_space_dimensions(v, n);
}

inline
Congruence::~Congruence() {
}

inline
Congruence::Congruence(Linear_Expression& le,
                       Coefficient_traits::const_reference m,
                       Recycle_Input)
  : modulus_(m) {
  PPL_ASSERT(m >= 0);
  swap(expr, le);

  PPL_ASSERT(OK());
}

inline Congruence
Congruence::create(const Linear_Expression& e,
                   Coefficient_traits::const_reference n,
                   Representation r) {
  Linear_Expression diff(e, r);
  diff -= n;
  const Congruence cg(diff, 1, Recycle_Input());
  return cg;
}

inline Congruence
Congruence::create(Coefficient_traits::const_reference n,
                   const Linear_Expression& e,
                   Representation r) {
  Linear_Expression diff(e, r);
  diff -= n;
  const Congruence cg(diff, 1, Recycle_Input());
  return cg;
}

/*! \relates Parma_Polyhedra_Library::Congruence */
inline Congruence
operator%=(const Linear_Expression& e1, const Linear_Expression& e2) {
  return Congruence::create(e1, e2);
}

/*! \relates Parma_Polyhedra_Library::Congruence */
inline Congruence
operator%=(const Linear_Expression& e, Coefficient_traits::const_reference n) {
  return Congruence::create(e, n);
}

/*! \relates Parma_Polyhedra_Library::Congruence */
inline Congruence
operator/(const Congruence& cg, Coefficient_traits::const_reference k) {
  Congruence ret = cg;
  ret /= k;
  return ret;
}

inline const Congruence&
Congruence::zero_dim_integrality() {
  return *zero_dim_integrality_p;
}

inline const Congruence&
Congruence::zero_dim_false() {
  return *zero_dim_false_p;
}

inline Congruence&
Congruence::operator=(const Congruence& y) {
  Congruence tmp = y;
  swap(*this, tmp);
  return *this;
}

/*! \relates Congruence */
inline Congruence
operator/(const Constraint& c, Coefficient_traits::const_reference m) {
  Congruence ret(c);
  ret /= m;
  return ret;
}

inline Congruence&
Congruence::operator/=(Coefficient_traits::const_reference k) {
  if (k >= 0) {
    modulus_ *= k;
  }
  else {
    modulus_ *= -k;
  }
  return *this;
}

/*! \relates Congruence */
inline bool
operator==(const Congruence& x, const Congruence& y) {
  if (x.space_dimension() != y.space_dimension()) {
    return false;
  }
  Congruence x_temp(x);
  Congruence y_temp(y);
  x_temp.strong_normalize();
  y_temp.strong_normalize();
  return x_temp.expr.is_equal_to(y_temp.expr)
    && x_temp.modulus() == y_temp.modulus();
}

/*! \relates Congruence */
inline bool
operator!=(const Congruence& x, const Congruence& y) {
  return !(x == y);
}

inline dimension_type
Congruence::max_space_dimension() {
  return Linear_Expression::max_space_dimension();
}

inline dimension_type
Congruence::space_dimension() const {
  return expr.space_dimension();
}

inline Coefficient_traits::const_reference
Congruence::coefficient(const Variable v) const {
  if (v.space_dimension() > space_dimension()) {
    throw_dimension_incompatible("coefficient(v)", "v", v);
  }
  return expr.coefficient(v);
}

inline void
Congruence::permute_space_dimensions(const std::vector<Variable>& cycles) {
  expr.permute_space_dimensions(cycles);
}

inline Coefficient_traits::const_reference
Congruence::inhomogeneous_term() const {
  return expr.inhomogeneous_term();
}

inline Coefficient_traits::const_reference
Congruence::modulus() const {
  return modulus_;
}

inline void
Congruence::set_modulus(Coefficient_traits::const_reference m) {
  modulus_ = m;
  PPL_ASSERT(OK());
}

inline bool
Congruence::is_proper_congruence() const {
  return modulus() > 0;
}

inline bool
Congruence::is_equality() const {
  return modulus() == 0;
}

inline bool
Congruence::is_equal_at_dimension(Variable v,
                                  const Congruence& cg) const {
  return coefficient(v) * cg.modulus() == cg.coefficient(v) * modulus();
}

inline memory_size_type
Congruence::external_memory_in_bytes() const {
  return expr.external_memory_in_bytes()
         + Parma_Polyhedra_Library::external_memory_in_bytes(modulus_);
}

inline memory_size_type
Congruence::total_memory_in_bytes() const {
  return external_memory_in_bytes() + sizeof(*this);
}

inline void
Congruence::m_swap(Congruence& y) {
  using std::swap;
  swap(expr, y.expr);
  swap(modulus_, y.modulus_);
}

inline void
Congruence::swap_space_dimensions(Variable v1, Variable v2) {
  expr.swap_space_dimensions(v1, v2);
}

/*! \relates Congruence */
inline void
swap(Congruence& x, Congruence& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Congruence_defs.hh line 505. */

/* Automatically generated from PPL source file ../src/Linear_Expression_Impl_templates.hh line 34. */
#include <stdexcept>
#include <iostream>

namespace Parma_Polyhedra_Library {

template <typename Row>
Linear_Expression_Impl<Row>
::Linear_Expression_Impl(const Linear_Expression_Impl& e) {
  construct(e);
}

template <typename Row>
template <typename Row2>
Linear_Expression_Impl<Row>
::Linear_Expression_Impl(const Linear_Expression_Impl<Row2>& e) {
  construct(e);
}

template <typename Row>
Linear_Expression_Impl<Row>
::Linear_Expression_Impl(const Linear_Expression_Interface& e) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&e)) {
    construct(*p);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&e)) {
    construct(*p);
  }
  else {
    // Add implementations for other derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
Linear_Expression_Impl<Row>
::Linear_Expression_Impl(const Linear_Expression_Interface& e,
                         dimension_type space_dim) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&e)) {
    construct(*p, space_dim);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&e)) {
    construct(*p, space_dim);
  }
  else {
    // Add implementations for other derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
template <typename Row2>
void
Linear_Expression_Impl<Row>
::linear_combine(const Linear_Expression_Impl<Row2>& y, Variable i) {
  PPL_ASSERT(space_dimension() == y.space_dimension());
  PPL_ASSERT(i.space_dimension() <= space_dimension());
  linear_combine(y, i.space_dimension());
}

template <typename Row>
template <typename Row2>
void
Linear_Expression_Impl<Row>
::linear_combine(const Linear_Expression_Impl<Row2>& y, dimension_type i) {
  const Linear_Expression_Impl& x = *this;
  PPL_ASSERT(i < x.space_dimension() + 1);
  PPL_ASSERT(x.space_dimension() == y.space_dimension());
  Coefficient_traits::const_reference x_i = x.row.get(i);
  Coefficient_traits::const_reference y_i = y.row.get(i);
  PPL_ASSERT(x_i != 0);
  PPL_ASSERT(y_i != 0);
  PPL_DIRTY_TEMP_COEFFICIENT(normalized_x_v);
  PPL_DIRTY_TEMP_COEFFICIENT(normalized_y_v);
  normalize2(x_i, y_i, normalized_x_v, normalized_y_v);
  neg_assign(normalized_x_v);
  linear_combine(y, normalized_y_v, normalized_x_v);
  // We cannot use x_i here because it may have been invalidated by
  // linear_combine().
  assert(x.row.get(i) == 0);
  PPL_ASSERT(OK());
}

template <typename Row>
template <typename Row2>
void
Linear_Expression_Impl<Row>
::linear_combine(const Linear_Expression_Impl<Row2>& y,
                 Coefficient_traits::const_reference c1,
                 Coefficient_traits::const_reference c2) {
  PPL_ASSERT(c1 != 0);
  PPL_ASSERT(c2 != 0);
  if (space_dimension() < y.space_dimension()) {
    set_space_dimension(y.space_dimension());
  }
  linear_combine(y, c1, c2, 0, y.space_dimension() + 1);
  PPL_ASSERT(OK());
}

template <typename Row>
template <typename Row2>
void
Linear_Expression_Impl<Row>
::linear_combine_lax(const Linear_Expression_Impl<Row2>& y,
                     Coefficient_traits::const_reference c1,
                     Coefficient_traits::const_reference c2) {
  if (space_dimension() < y.space_dimension()) {
    set_space_dimension(y.space_dimension());
  }
  linear_combine_lax(y, c1, c2, 0, y.space_dimension() + 1);
  PPL_ASSERT(OK());
}

template <typename Row>
template <typename Row2>
int
Linear_Expression_Impl<Row>
::compare(const Linear_Expression_Impl<Row2>& y) const {
  const Linear_Expression_Impl& x = *this;
  // Compare all the coefficients of the row starting from position 1.
  // NOTE: x and y may be of different size.
  typename Row::const_iterator i = x.row.lower_bound(1);
  typename Row::const_iterator i_end = x.row.end();
  typename Row2::const_iterator j = y.row.lower_bound(1);
  typename Row2::const_iterator j_end = y.row.end();
  while (i != i_end && j != j_end) {
    if (i.index() < j.index()) {
      const int s = sgn(*i);
      if (s != 0) {
        return 2*s;
      }
      ++i;
      continue;
    }
    if (i.index() > j.index()) {
      const int s = sgn(*j);
      if (s != 0) {
        return -2*s;
      }
      ++j;
      continue;
    }
    PPL_ASSERT(i.index() == j.index());
    const int s = cmp(*i, *j);
    if (s < 0) {
      return -2;
    }
    if (s > 0) {
      return 2;
    }
    PPL_ASSERT(s == 0);
    ++i;
    ++j;
  }
  for ( ; i != i_end; ++i) {
    const int s = sgn(*i);
    if (s != 0) {
      return 2*s;
    }
  }
  for ( ; j != j_end; ++j) {
    const int s = sgn(*j);
    if (s != 0) {
      return -2*s;
    }
  }

  // If all the coefficients in `x' equal all the coefficients in `y'
  // (starting from position 1) we compare coefficients in position 0,
  // i.e., inhomogeneous terms.
  const int comp = cmp(x.row.get(0), y.row.get(0));
  if (comp > 0) {
    return 1;
  }
  if (comp < 0) {
    return -1;
  }
  PPL_ASSERT(comp == 0);

  // `x' and `y' are equal.
  return 0;
}

template <typename Row>
Linear_Expression_Impl<Row>::Linear_Expression_Impl(const Variable v) {
  if (v.space_dimension() > max_space_dimension()) {
    throw std::length_error("Linear_Expression_Impl::"
                            "Linear_Expression_Impl(v):\n"
                            "v exceeds the maximum allowed "
                            "space dimension.");
  }
  set_space_dimension(v.space_dimension());
  (*this) += v;
  PPL_ASSERT(OK());
}

template <typename Row>
template <typename Row2>
bool
Linear_Expression_Impl<Row>
::is_equal_to(const Linear_Expression_Impl<Row2>& x) const {
  return row == x.row;
}

template <typename Row>
void
Linear_Expression_Impl<Row>::get_row(Dense_Row& r) const {
  r = this->row;
}

template <typename Row>
void
Linear_Expression_Impl<Row>::get_row(Sparse_Row& r) const {
  r = this->row;
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::permute_space_dimensions(const std::vector<Variable>& cycle) {
  const dimension_type n = cycle.size();
  if (n < 2) {
    return;
  }

  if (n == 2) {
    row.swap_coefficients(cycle[0].space_dimension(),
                          cycle[1].space_dimension());
  }
  else {
    PPL_DIRTY_TEMP_COEFFICIENT(tmp);
    tmp = row.get(cycle.back().space_dimension());
    for (dimension_type i = n - 1; i-- > 0; ) {
      row.swap_coefficients(cycle[i + 1].space_dimension(),
                            cycle[i].space_dimension());
    }
    if (tmp == 0) {
      row.reset(cycle[0].space_dimension());
    }
    else {
      using std::swap;
      swap(tmp, row[cycle[0].space_dimension()]);
    }
  }
  PPL_ASSERT(OK());
}

template <typename Row>
template <typename Row2>
Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>::operator+=(const Linear_Expression_Impl<Row2>& e) {
  linear_combine(e, Coefficient_one(), Coefficient_one());
  return *this;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression_Impl */
template <typename Row>
Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>::operator+=(const Variable v) {
  const dimension_type v_space_dim = v.space_dimension();
  if (v_space_dim > Linear_Expression_Impl<Row>::max_space_dimension()) {
    throw std::length_error("Linear_Expression_Impl& "
                            "operator+=(e, v):\n"
                            "v exceeds the maximum allowed space dimension.");
  }
  if (space_dimension() < v_space_dim) {
    set_space_dimension(v_space_dim);
  }
  typename Row::iterator itr = row.insert(v_space_dim);
  ++(*itr);
  if (*itr == 0) {
    row.reset(itr);
  }
  PPL_ASSERT(OK());
  return *this;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression_Impl */
template <typename Row>
template <typename Row2>
Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>::operator-=(const Linear_Expression_Impl<Row2>& e2) {
  linear_combine(e2, Coefficient_one(), -1);
  return *this;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression_Impl */
template <typename Row>
Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>::operator-=(const Variable v) {
  const dimension_type v_space_dim = v.space_dimension();
  if (v_space_dim > Linear_Expression_Impl<Row>::max_space_dimension()) {
    throw std::length_error("Linear_Expression_Impl& "
                            "operator-=(e, v):\n"
                            "v exceeds the maximum allowed space dimension.");
  }
  if (space_dimension() < v_space_dim) {
    set_space_dimension(v_space_dim);
  }
  typename Row::iterator itr = row.insert(v_space_dim);
  --(*itr);
  if (*itr == 0) {
    row.reset(itr);
  }
  PPL_ASSERT(OK());
  return *this;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression_Impl */
template <typename Row>
Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>::operator*=(Coefficient_traits::const_reference n) {
  if (n == 0) {
    row.clear();
    PPL_ASSERT(OK());
    return *this;
  }
  for (typename Row::iterator i = row.begin(),
         i_end = row.end(); i != i_end; ++i) {
    (*i) *= n;
  }
  PPL_ASSERT(OK());
  return *this;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression_Impl */
template <typename Row>
Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>::operator/=(Coefficient_traits::const_reference n) {
  typename Row::iterator i = row.begin();
  const typename Row::iterator& i_end = row.end();
  while (i != i_end) {
    (*i) /= n;
    if (*i == 0) {
      i = row.reset(i);
    }
    else {
      ++i;
    }
  }
  PPL_ASSERT(OK());
  return *this;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression_Impl */
template <typename Row>
void
Linear_Expression_Impl<Row>::negate() {
  for (typename Row::iterator i = row.begin(),
         i_end = row.end(); i != i_end; ++i) {
    neg_assign(*i);
  }
  PPL_ASSERT(OK());
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression_Impl */
template <typename Row>
Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>::add_mul_assign(Coefficient_traits::const_reference n,
                                            const Variable v) {
  const dimension_type v_space_dim = v.space_dimension();
  if (v_space_dim > Linear_Expression_Impl<Row>::max_space_dimension()) {
    throw std::length_error("Linear_Expression_Impl& "
                            "add_mul_assign(e, n, v):\n"
                            "v exceeds the maximum allowed space dimension.");
  }
  if (space_dimension() < v_space_dim) {
    set_space_dimension(v_space_dim);
  }
  if (n == 0) {
    return *this;
  }
  typename Row::iterator itr = row.insert(v_space_dim);
  (*itr) += n;
  if (*itr == 0) {
    row.reset(itr);
  }
  PPL_ASSERT(OK());
  return *this;
}

/*! \relates Parma_Polyhedra_Library::Linear_Expression_Impl */
template <typename Row>
Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>
::sub_mul_assign(Coefficient_traits::const_reference n,
                 const Variable v) {
  const dimension_type v_space_dim = v.space_dimension();
  if (v_space_dim > Linear_Expression_Impl<Row>::max_space_dimension()) {
    throw std::length_error("Linear_Expression_Impl& "
                            "sub_mul_assign(e, n, v):\n"
                            "v exceeds the maximum allowed space dimension.");
  }
  if (space_dimension() < v_space_dim) {
    set_space_dimension(v_space_dim);
  }
  if (n == 0) {
    return *this;
  }
  typename Row::iterator itr = row.insert(v_space_dim);
  (*itr) -= n;
  if (*itr == 0) {
    row.reset(itr);
  }
  PPL_ASSERT(OK());
  return *this;
}

template <typename Row>
template <typename Row2>
void
Linear_Expression_Impl<Row>
::add_mul_assign(Coefficient_traits::const_reference factor,
                 const Linear_Expression_Impl<Row2>& y) {
  if (factor != 0) {
    linear_combine(y, Coefficient_one(), factor);
  }
}

template <typename Row>
template <typename Row2>
void
Linear_Expression_Impl<Row>
::sub_mul_assign(Coefficient_traits::const_reference factor,
                 const Linear_Expression_Impl<Row2>& y) {
  if (factor != 0) {
    linear_combine(y, Coefficient_one(), -factor);
  }
}

template <typename Row>
void
Linear_Expression_Impl<Row>::print(std::ostream& s) const {
  PPL_DIRTY_TEMP_COEFFICIENT(ev);
  bool first = true;
  for (typename Row::const_iterator i = row.lower_bound(1), i_end = row.end();
       i != i_end; ++i) {
    ev = *i;
    if (ev == 0) {
      continue;
    }
    if (!first) {
      if (ev > 0) {
        s << " + ";
      }
      else {
        s << " - ";
        neg_assign(ev);
      }
    }
    else {
      first = false;
    }
    if (ev == -1) {
      s << "-";
    }
    else if (ev != 1) {
      s << ev << "*";
    }
    IO_Operators::operator<<(s, Variable(i.index() - 1));
  }
  // Inhomogeneous term.
  PPL_DIRTY_TEMP_COEFFICIENT(it);
  it = row[0];
  if (it != 0) {
    if (!first) {
      if (it > 0) {
        s << " + ";
      }
      else {
        s << " - ";
        neg_assign(it);
      }
    }
    else {
      first = false;
    }
    s << it;
  }

  if (first) {
    // The null linear expression.
    s << Coefficient_zero();
  }
}

template <typename Row>
Coefficient_traits::const_reference
Linear_Expression_Impl<Row>::get(dimension_type i) const {
  return row.get(i);
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::set(dimension_type i, Coefficient_traits::const_reference n) {
  if (n == 0) {
    row.reset(i);
  }
  else {
    row.insert(i, n);
  }
  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::exact_div_assign(Coefficient_traits::const_reference c,
                   dimension_type start, dimension_type end) {
  // NOTE: Since all coefficients in [start,end) are multiple of c,
  // each of the resulting coefficients will be nonzero iff the initial
  // coefficient was.
  for (typename Row::iterator i = row.lower_bound(start),
         i_end = row.lower_bound(end); i != i_end; ++i) {
    Parma_Polyhedra_Library::exact_div_assign(*i, *i, c);
  }
  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::mul_assign(Coefficient_traits::const_reference c,
                   dimension_type start, dimension_type end) {
  if (c == 0) {
    typename Row::iterator i = row.lower_bound(start);
    const typename Row::iterator& i_end = row.end();
    while (i != i_end && i.index() < end) {
      i = row.reset(i);
    }
  }
  else {
    for (typename Row::iterator
      i = row.lower_bound(start), i_end = row.lower_bound(end);
      i != i_end; ++i) {
      (*i) *= c;
    }
  }
  PPL_ASSERT(OK());
}

template <typename Row>
template <typename Row2>
void
Linear_Expression_Impl<Row>
::linear_combine(const Linear_Expression_Impl<Row2>& y,
                 Coefficient_traits::const_reference c1,
                 Coefficient_traits::const_reference c2,
                 dimension_type start, dimension_type end) {
  Parma_Polyhedra_Library::linear_combine(row, y.row, c1, c2, start, end);
  PPL_ASSERT(OK());
}

template <typename Row>
template <typename Row2>
void
Linear_Expression_Impl<Row>
::linear_combine_lax(const Linear_Expression_Impl<Row2>& y,
                     Coefficient_traits::const_reference c1,
                     Coefficient_traits::const_reference c2,
                     dimension_type start, dimension_type end) {
  PPL_ASSERT(start <= end);
  PPL_ASSERT(end <= row.size());
  PPL_ASSERT(end <= y.row.size());
  if (c1 == 0) {
    if (c2 == 0) {
      PPL_ASSERT(c1 == 0);
      PPL_ASSERT(c2 == 0);
      typename Row::iterator i = row.lower_bound(start);
      const typename Row::iterator& i_end = row.end();
      while (i != i_end && i.index() < end) {
        i = row.reset(i);
      }
    }
    else {
      PPL_ASSERT(c1 == 0);
      PPL_ASSERT(c2 != 0);

      typename Row::iterator i = row.lower_bound(start);
      const typename Row::iterator& i_end = row.end();
      typename Row2::const_iterator j = y.row.lower_bound(start);
      typename Row2::const_iterator j_last = y.row.lower_bound(end);

      while (i != i_end && i.index() < end && j != j_last) {
        if (i.index() < j.index()) {
          i = row.reset(i);
          continue;
        }
        if (i.index() > j.index()) {
          i = row.insert(i, j.index(), *j);
          (*i) *= c2;
          ++i;
          ++j;
          continue;
        }
        PPL_ASSERT(i.index() == j.index());
        (*i) = (*j);
        (*i) *= c2;
        ++i;
        ++j;
      }
      while (i != i_end && i.index() < end) {
        i = row.reset(i);
      }
      while (j != j_last) {
        i = row.insert(i, j.index(), *j);
        (*i) *= c2;
        // No need to increment i here.
        ++j;
      }
    }
  }
  else {
    if (c2 == 0) {
      PPL_ASSERT(c1 != 0);
      PPL_ASSERT(c2 == 0);
      for (typename Row::iterator i = row.lower_bound(start),
             i_end = row.lower_bound(end); i != i_end; ++i) {
        (*i) *= c1;
      }
    }
    else {
      PPL_ASSERT(c1 != 0);
      PPL_ASSERT(c2 != 0);
      Parma_Polyhedra_Library::linear_combine(row, y.row, c1, c2, start, end);
    }
  }
  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_Expression_Impl<Row>::sign_normalize() {
  typename Row::iterator i = row.lower_bound(1);
  typename Row::iterator i_end = row.end();

  for ( ; i != i_end; ++i) {
    if (*i != 0) {
      break;
    }
  }

  if (i != i_end && *i < 0) {
    for ( ; i != i_end; ++i) {
      neg_assign(*i);
    }
    // Negate the first coefficient, too.
    typename Row::iterator first = row.begin();
    if (first != row.end() && first.index() == 0) {
      neg_assign(*first);
    }
  }
  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_Expression_Impl<Row>::negate(dimension_type first, dimension_type last) {
  PPL_ASSERT(first <= last);
  PPL_ASSERT(last <= row.size());
  typename Row::iterator i = row.lower_bound(first);
  typename Row::iterator i_end = row.lower_bound(last);
  for ( ; i != i_end; ++i) {
    neg_assign(*i);
  }
  PPL_ASSERT(OK());
}

template <typename Row>
template <typename Row2>
void
Linear_Expression_Impl<Row>::construct(const Linear_Expression_Impl<Row2>& e) {
  row = e.row;
  PPL_ASSERT(OK());
}

template <typename Row>
template <typename Row2>
void
Linear_Expression_Impl<Row>::construct(const Linear_Expression_Impl<Row2>& e,
                                       dimension_type space_dim) {
  Row x(e.row, space_dim + 1, space_dim + 1);
  swap(row, x);
  PPL_ASSERT(OK());
}

template <typename Row>
template <typename Row2>
void
Linear_Expression_Impl<Row>
::scalar_product_assign(Coefficient& result,
                        const Linear_Expression_Impl<Row2>& y,
                        dimension_type start, dimension_type end) const {
  const Linear_Expression_Impl<Row>& x = *this;
  PPL_ASSERT(start <= end);
  PPL_ASSERT(end <= x.row.size());
  PPL_ASSERT(end <= y.row.size());
  result = 0;
  typename Row ::const_iterator x_i = x.row.lower_bound(start);
  typename Row ::const_iterator x_end = x.row.lower_bound(end);
  typename Row2::const_iterator y_i = y.row.lower_bound(start);
  typename Row2::const_iterator y_end = y.row.lower_bound(end);
  while (x_i != x_end && y_i != y_end) {
    if (x_i.index() == y_i.index()) {
      Parma_Polyhedra_Library::add_mul_assign(result, *x_i, *y_i);
      ++x_i;
      ++y_i;
    }
    else {
      if (x_i.index() < y_i.index()) {
        PPL_ASSERT(y.row.get(x_i.index()) == 0);
        // (*x_i) * 0 == 0, nothing to do.
        ++x_i;
      }
      else {
        PPL_ASSERT(x.row.get(y_i.index()) == 0);
        // 0 * (*y_i) == 0, nothing to do.
        ++y_i;
      }
    }
  }
  // In the remaining positions (if any) at most one row is nonzero, so
  // there's nothing left to do.
}

template <typename Row>
template <typename Row2>
int
Linear_Expression_Impl<Row>
::scalar_product_sign(const Linear_Expression_Impl<Row2>& y,
                      dimension_type start, dimension_type end) const {
  PPL_DIRTY_TEMP_COEFFICIENT(result);
  scalar_product_assign(result, y, start, end);
  return sgn(result);
}

template <typename Row>
template <typename Row2>
bool
Linear_Expression_Impl<Row>
::is_equal_to(const Linear_Expression_Impl<Row2>& y,
              dimension_type start, dimension_type end) const {
  const Linear_Expression_Impl<Row>& x = *this;
  PPL_ASSERT(start <= end);
  PPL_ASSERT(end <= x.row.size());
  PPL_ASSERT(end <= y.row.size());

  typename Row::const_iterator i = x.row.lower_bound(start);
  typename Row::const_iterator i_end = x.row.lower_bound(end);
  typename Row2::const_iterator j = y.row.lower_bound(start);
  typename Row2::const_iterator j_end = y.row.lower_bound(end);
  while (i != i_end && j != j_end) {
    if (i.index() == j.index()) {
      if (*i != *j) {
        return false;
      }
      ++i;
      ++j;
    }
    else {
      if (i.index() < j.index()) {
        if (*i != 0) {
          return false;
        }
        ++i;
      }
      else {
        PPL_ASSERT(i.index() > j.index());
        if (*j != 0) {
          return false;
        }
        ++j;
      }
    }
  }
  for ( ; i != i_end; ++i) {
    if (*i != 0) {
      return false;
    }
  }
  for ( ; j != j_end; ++j) {
    if (*j != 0) {
      return false;
    }
  }
  return true;
}

template <typename Row>
template <typename Row2>
bool
Linear_Expression_Impl<Row>
::is_equal_to(const Linear_Expression_Impl<Row2>& y,
              Coefficient_traits::const_reference c1,
              Coefficient_traits::const_reference c2,
              dimension_type start, dimension_type end) const {
  const Linear_Expression_Impl<Row>& x = *this;
  PPL_ASSERT(start <= end);
  PPL_ASSERT(end <= x.row.size());
  PPL_ASSERT(end <= y.row.size());

  // Deal with trivial cases.
  if (c1 == 0) {
    if (c2 == 0) {
      return true;
    }
    else {
      return y.all_zeroes(start, end);
    }
  }
  if (c2 == 0) {
    return x.all_zeroes(start, end);
  }

  PPL_ASSERT(c1 != 0);
  PPL_ASSERT(c2 != 0);
  typename Row::const_iterator i = x.row.lower_bound(start);
  typename Row::const_iterator i_end = x.row.lower_bound(end);
  typename Row2::const_iterator j = y.row.lower_bound(start);
  typename Row2::const_iterator j_end = y.row.lower_bound(end);
  while (i != i_end && j != j_end) {
    if (i.index() == j.index()) {
      if ((*i) * c1 != (*j) * c2) {
        return false;
      }
      ++i;
      ++j;
    }
    else {
      if (i.index() < j.index()) {
        if (*i != 0) {
          return false;
        }
        ++i;
      }
      else {
        PPL_ASSERT(i.index() > j.index());
        if (*j != 0) {
          return false;
        }
        ++j;
      }
    }
  }
  for ( ; i != i_end; ++i) {
    if (*i != 0) {
      return false;
    }
  }
  for ( ; j != j_end; ++j) {
    if (*j != 0) {
      return false;
    }
  }
  return true;
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::linear_combine(const Linear_Expression_Interface& y, Variable v) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    linear_combine(*p, v);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    linear_combine(*p, v);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::linear_combine(const Linear_Expression_Interface& y,
                 Coefficient_traits::const_reference c1,
                 Coefficient_traits::const_reference c2) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    linear_combine(*p, c1, c2);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    linear_combine(*p, c1, c2);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::linear_combine_lax(const Linear_Expression_Interface& y,
                     Coefficient_traits::const_reference c1,
                     Coefficient_traits::const_reference c2) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    linear_combine_lax(*p, c1, c2);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    linear_combine_lax(*p, c1, c2);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
bool
Linear_Expression_Impl<Row>
::is_equal_to(const Linear_Expression_Interface& y) const {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    return is_equal_to(*p);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    return is_equal_to(*p);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
    return false;
  }
}

template <typename Row>
Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>
::operator+=(const Linear_Expression_Interface& y) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    return operator+=(*p);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    return operator+=(*p);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
    return *this;
  }
}

template <typename Row>
Linear_Expression_Impl<Row>&
Linear_Expression_Impl<Row>
::operator-=(const Linear_Expression_Interface& y) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    return operator-=(*p);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    return operator-=(*p);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
    return *this;
  }
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::add_mul_assign(Coefficient_traits::const_reference factor,
                 const Linear_Expression_Interface& y) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    add_mul_assign(factor, *p);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    add_mul_assign(factor, *p);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::sub_mul_assign(Coefficient_traits::const_reference factor,
                 const Linear_Expression_Interface& y) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    sub_mul_assign(factor, *p);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    sub_mul_assign(factor, *p);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::linear_combine(const Linear_Expression_Interface& y, dimension_type i) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    linear_combine(*p, i);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    linear_combine(*p, i);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::linear_combine(const Linear_Expression_Interface& y,
                 Coefficient_traits::const_reference c1,
                 Coefficient_traits::const_reference c2,
                 dimension_type start, dimension_type end) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    linear_combine(*p, c1, c2, start, end);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    linear_combine(*p, c1, c2, start, end);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::linear_combine_lax(const Linear_Expression_Interface& y,
                     Coefficient_traits::const_reference c1,
                     Coefficient_traits::const_reference c2,
                     dimension_type start, dimension_type end) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    linear_combine_lax(*p, c1, c2, start, end);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    linear_combine_lax(*p, c1, c2, start, end);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
int
Linear_Expression_Impl<Row>
::compare(const Linear_Expression_Interface& y) const {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    return compare(*p);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    return compare(*p);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
    return 0;
  }
}


template <typename Row>
void
Linear_Expression_Impl<Row>::construct(const Linear_Expression_Interface& y) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    return construct(*p);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    return construct(*p);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
void
Linear_Expression_Impl<Row>::construct(const Linear_Expression_Interface& y,
                                       dimension_type space_dim) {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    return construct(*p, space_dim);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    return construct(*p, space_dim);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
void
Linear_Expression_Impl<Row>
::scalar_product_assign(Coefficient& result,
                        const Linear_Expression_Interface& y,
                        dimension_type start, dimension_type end) const {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    scalar_product_assign(result, *p, start, end);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    scalar_product_assign(result, *p, start, end);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
  }
}

template <typename Row>
int
Linear_Expression_Impl<Row>
::scalar_product_sign(const Linear_Expression_Interface& y,
                      dimension_type start, dimension_type end) const {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    return scalar_product_sign(*p, start, end);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    return scalar_product_sign(*p, start, end);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
    return 0;
  }
}

template <typename Row>
bool
Linear_Expression_Impl<Row>
::is_equal_to(const Linear_Expression_Interface& y,
              dimension_type start, dimension_type end) const {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    return is_equal_to(*p, start, end);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    return is_equal_to(*p, start, end);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
    return false;
  }
}

template <typename Row>
bool
Linear_Expression_Impl<Row>
::is_equal_to(const Linear_Expression_Interface& y,
              Coefficient_traits::const_reference c1,
              Coefficient_traits::const_reference c2,
              dimension_type start, dimension_type end) const {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    return is_equal_to(*p, c1, c2, start, end);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    return is_equal_to(*p, c1, c2, start, end);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
    return false;
  }
}

template <typename Row>
bool
Linear_Expression_Impl<Row>
::have_a_common_variable(const Linear_Expression_Interface& y,
                         Variable first, Variable last) const {
  typedef const Linear_Expression_Impl<Dense_Row>* Dense_Ptr;
  typedef const Linear_Expression_Impl<Sparse_Row>* Sparse_Ptr;
  if (const Dense_Ptr p = dynamic_cast<Dense_Ptr>(&y)) {
    return have_a_common_variable(*p, first, last);
  }
  else if (const Sparse_Ptr p = dynamic_cast<Sparse_Ptr>(&y)) {
    return have_a_common_variable(*p, first, last);
  }
  else {
    // Add implementations for new derived classes here.
    PPL_UNREACHABLE;
    return false;
  }
}

template <typename Row>
Linear_Expression_Interface::const_iterator_interface*
Linear_Expression_Impl<Row>::begin() const {
  return new const_iterator(row, 1);
}

template <typename Row>
Linear_Expression_Interface::const_iterator_interface*
Linear_Expression_Impl<Row>::end() const {
  return new const_iterator(row, row.size());
}

template <typename Row>
Linear_Expression_Interface::const_iterator_interface*
Linear_Expression_Impl<Row>::lower_bound(Variable v) const {
  return new const_iterator(row, v.space_dimension());
}

template <typename Row>
Linear_Expression_Impl<Row>::const_iterator
::const_iterator(const Row& r, dimension_type i)
  : row(&r), itr(r.lower_bound(i)) {
  skip_zeroes_forward();
}

template <typename Row>
Linear_Expression_Interface::const_iterator_interface*
Linear_Expression_Impl<Row>::const_iterator
::clone() const {
  return new const_iterator(*this);
}

template <typename Row>
void
Linear_Expression_Impl<Row>::const_iterator
::operator++() {
  ++itr;
  skip_zeroes_forward();
}

template <typename Row>
void
Linear_Expression_Impl<Row>::const_iterator
::operator--() {
  --itr;
  skip_zeroes_backward();
}

template <typename Row>
typename Linear_Expression_Impl<Row>::const_iterator::reference
Linear_Expression_Impl<Row>::const_iterator
::operator*() const {
  return *itr;
}

template <typename Row>
Variable
Linear_Expression_Impl<Row>::const_iterator
::variable() const {
  const dimension_type i = itr.index();
  PPL_ASSERT(i != 0);
  return Variable(i - 1);
}

template <typename Row>
bool
Linear_Expression_Impl<Row>::const_iterator
::operator==(const const_iterator_interface& x) const {
  const const_iterator* const p = dynamic_cast<const const_iterator*>(&x);
  // Comparing iterators belonging to different rows is forbidden.
  PPL_ASSERT(p != 0);
  PPL_ASSERT(row == p->row);
  return itr == p->itr;
}

template <typename Row>
void
Linear_Expression_Impl<Row>::ascii_dump(std::ostream& s) const {
  s << "size " << (space_dimension() + 1) << " ";
  for (dimension_type i = 0; i < row.size(); ++i) {
    s << row.get(i);
    if (i != row.size() - 1) {
      s << ' ';
    }
  }
}

template <typename Row>
bool
Linear_Expression_Impl<Row>::ascii_load(std::istream& s) {
  std::string str;

  if (!(s >> str)) {
    return false;
  }
  if (str != "size") {
    return false;
  }

  dimension_type new_size;
  if (!(s >> new_size)) {
    return false;
  }

  row.resize(0);
  row.resize(new_size);

  PPL_DIRTY_TEMP_COEFFICIENT(c);

  for (dimension_type j = 0; j < new_size; ++j) {
    if (!(s >> c)) {
      return false;
    }
    if (c != 0) {
      row.insert(j, c);
    }
  }

  PPL_ASSERT(OK());
  return true;
}

template <typename Row>
bool
Linear_Expression_Impl<Row>::OK() const {
  return row.OK();
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_Expression_Impl_defs.hh line 905. */

/* Automatically generated from PPL source file ../src/Linear_Form_templates.hh line 1. */
/* Linear_Form class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/Box_defs.hh line 1. */
/* Box class declaration.
*/


/* Automatically generated from PPL source file ../src/Poly_Con_Relation_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Poly_Con_Relation;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Poly_Gen_Relation_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Poly_Gen_Relation;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Partially_Reduced_Product_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename D1, typename D2>
class Smash_Reduction;

template <typename D1, typename D2>
class Constraints_Reduction;

template <typename D1, typename D2>
class Congruences_Reduction;

template <typename D1, typename D2>
class Shape_Preserving_Reduction;

template <typename D1, typename D2>
class No_Reduction;

template <typename D1, typename D2, typename R>
class Partially_Reduced_Product;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Box_defs.hh line 50. */
#include <vector>
#include <iosfwd>

namespace Parma_Polyhedra_Library {

struct Interval_Base;

//! Swaps \p x with \p y.
/*! \relates Box */
template <typename ITV>
void swap(Box<ITV>& x, Box<ITV>& y);

//! Returns <CODE>true</CODE> if and only if \p x and \p y are the same box.
/*! \relates Box
  Note that \p x and \p y may be dimension-incompatible boxes:
  in this case, the value <CODE>false</CODE> is returned.
*/
template <typename ITV>
bool operator==(const Box<ITV>& x, const Box<ITV>& y);

//! Returns <CODE>true</CODE> if and only if \p x and \p y are not the same box.
/*! \relates Box
  Note that \p x and \p y may be dimension-incompatible boxes:
  in this case, the value <CODE>true</CODE> is returned.
*/
template <typename ITV>
bool operator!=(const Box<ITV>& x, const Box<ITV>& y);

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Box */
template <typename ITV>
std::ostream& operator<<(std::ostream& s, const Box<ITV>& box);

} // namespace IO_Operators

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates Box
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.
*/
template <typename To, typename ITV>
bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Box<ITV>& x,
                            const Box<ITV>& y,
                            Rounding_Dir dir);

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates Box
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.
*/
template <typename Temp, typename To, typename ITV>
bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Box<ITV>& x,
                            const Box<ITV>& y,
                            Rounding_Dir dir);

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates Box
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
template <typename Temp, typename To, typename ITV>
bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Box<ITV>& x,
                            const Box<ITV>& y,
                            Rounding_Dir dir,
                            Temp& tmp0,
                            Temp& tmp1,
                            Temp& tmp2);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates Box
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.
*/
template <typename To, typename ITV>
bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Box<ITV>& x,
                          const Box<ITV>& y,
                          Rounding_Dir dir);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates Box
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.
*/
template <typename Temp, typename To, typename ITV>
bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Box<ITV>& x,
                          const Box<ITV>& y,
                          Rounding_Dir dir);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates Box
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
template <typename Temp, typename To, typename ITV>
bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Box<ITV>& x,
                          const Box<ITV>& y,
                          Rounding_Dir dir,
                          Temp& tmp0,
                          Temp& tmp1,
                          Temp& tmp2);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates Box
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.
*/
template <typename To, typename ITV>
bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Box<ITV>& x,
                           const Box<ITV>& y,
                           Rounding_Dir dir);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates Box
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.
*/
template <typename Temp, typename To, typename ITV>
bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Box<ITV>& x,
                           const Box<ITV>& y,
                           Rounding_Dir dir);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates Box
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
template <typename Temp, typename To, typename ITV>
bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Box<ITV>& x,
                           const Box<ITV>& y,
                           Rounding_Dir dir,
                           Temp& tmp0,
                           Temp& tmp1,
                           Temp& tmp2);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Box
  Helper function for computing distances.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Specialization,
          typename Temp, typename To, typename ITV>
bool
l_m_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                    const Box<ITV>& x, const Box<ITV>& y,
                    Rounding_Dir dir,
                    Temp& tmp0, Temp& tmp1, Temp& tmp2);

} // namespace Parma_Polyhedra_Library

//! A not necessarily closed, iso-oriented hyperrectangle.
/*! \ingroup PPL_CXX_interface
  A Box object represents the smash product of \f$n\f$
  not necessarily closed and possibly unbounded intervals
  represented by objects of class \p ITV,
  where \f$n\f$ is the space dimension of the box.

  An <EM>interval constraint</EM> (resp., <EM>interval congruence</EM>)
  is a syntactic constraint (resp., congruence) that only mentions
  a single space dimension.

  The Box domain <EM>optimally supports</EM>:
    - tautological and inconsistent constraints and congruences;
    - the interval constraints that are optimally supported by
      the template argument class \c ITV;
    - the interval congruences that are optimally supported by
      the template argument class \c ITV.

  Depending on the method, using a constraint or congruence that is not
  optimally supported by the domain will either raise an exception or
  result in a (possibly non-optimal) upward approximation.

  The user interface for the Box domain is meant to be as similar
  as possible to the one developed for the polyhedron class C_Polyhedron.
*/
template <typename ITV>
class Parma_Polyhedra_Library::Box {
public:
  //! The type of intervals used to implement the box.
  typedef ITV interval_type;

  //! Returns the maximum space dimension that a Box can handle.
  static dimension_type max_space_dimension();

  /*! \brief
    Returns false indicating that this domain does not recycle constraints
  */
  static bool can_recycle_constraint_systems();

  /*! \brief
    Returns false indicating that this domain does not recycle congruences
  */
  static bool can_recycle_congruence_systems();

  //! \name Constructors, Assignment, Swap and Destructor
  //@{

  //! Builds a universe or empty box of the specified space dimension.
  /*!
    \param num_dimensions
    The number of dimensions of the vector space enclosing the box;

    \param kind
    Specifies whether the universe or the empty box has to be built.
  */
  explicit Box(dimension_type num_dimensions = 0,
               Degenerate_Element kind = UNIVERSE);

  //! Ordinary copy constructor.
  /*!
    The complexity argument is ignored.
  */
  Box(const Box& y,
      Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a conservative, upward approximation of \p y.
  /*!
    The complexity argument is ignored.
  */
  template <typename Other_ITV>
  explicit Box(const Box<Other_ITV>& y,
               Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a box from the system of constraints \p cs.
  /*!
    The box inherits the space dimension of \p cs.

    \param cs
    A system of constraints: constraints that are not
    \ref intervals "interval constraints"
    are ignored (even though they may have contributed
    to the space dimension).
  */
  explicit Box(const Constraint_System& cs);

  //! Builds a box recycling a system of constraints \p cs.
  /*!
    The box inherits the space dimension of \p cs.

    \param cs
    A system of constraints: constraints that are not
    \ref intervals "interval constraints"
    are ignored (even though they may have contributed
    to the space dimension).

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.
  */
  Box(const Constraint_System& cs, Recycle_Input dummy);

  //! Builds a box from the system of generators \p gs.
  /*!
    Builds the smallest box containing the polyhedron defined by \p gs.
    The box inherits the space dimension of \p gs.

    \exception std::invalid_argument
    Thrown if the system of generators is not empty but has no points.
  */
  explicit Box(const Generator_System& gs);

  //! Builds a box recycling the system of generators \p gs.
  /*!
    Builds the smallest box containing the polyhedron defined by \p gs.
    The box inherits the space dimension of \p gs.

    \param gs
    The generator system describing the polyhedron to be approximated.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.

    \exception std::invalid_argument
    Thrown if the system of generators is not empty but has no points.
  */
  Box(const Generator_System& gs, Recycle_Input dummy);

  /*!
    Builds the smallest box containing the grid defined by a
    system of congruences \p cgs.
    The box inherits the space dimension of \p cgs.

    \param cgs
    A system of congruences: congruences that are not
    non-relational equality constraints are ignored
    (though they may have contributed to the space dimension).
  */
  explicit Box(const Congruence_System& cgs);

  /*!
    Builds the smallest box containing the grid defined by a
    system of congruences \p cgs, recycling \p cgs.
    The box inherits the space dimension of \p cgs.

    \param cgs
    A system of congruences: congruences that are not
    non-relational equality constraints are ignored
    (though they will contribute to the space dimension).

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.
  */
  Box(const Congruence_System& cgs, Recycle_Input dummy);

  //! Builds a box containing the BDS \p bds.
  /*!
    Builds the smallest box containing \p bds using a polynomial algorithm.
    The \p complexity argument is ignored.
  */
  template <typename T>
  explicit Box(const BD_Shape<T>& bds,
               Complexity_Class complexity = POLYNOMIAL_COMPLEXITY);

  //! Builds a box containing the octagonal shape \p oct.
  /*!
    Builds the smallest box containing \p oct using a polynomial algorithm.
    The \p complexity argument is ignored.
  */
  template <typename T>
  explicit Box(const Octagonal_Shape<T>& oct,
               Complexity_Class complexity = POLYNOMIAL_COMPLEXITY);

  //! Builds a box containing the polyhedron \p ph.
  /*!
    Builds a box containing \p ph using algorithms whose complexity
    does not exceed the one specified by \p complexity.  If
    \p complexity is \p ANY_COMPLEXITY, then the built box is the
    smallest one containing \p ph.
  */
  explicit Box(const Polyhedron& ph,
               Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a box containing the grid \p gr.
  /*!
    Builds the smallest box containing \p gr using a polynomial algorithm.
    The \p complexity argument is ignored.
  */
  explicit Box(const Grid& gr,
               Complexity_Class complexity = POLYNOMIAL_COMPLEXITY);

  //! Builds a box containing the partially reduced product \p dp.
  /*!
    Builds a box containing \p ph using algorithms whose complexity
    does not exceed the one specified by \p complexity.
  */
  template <typename D1, typename D2, typename R>
  explicit Box(const Partially_Reduced_Product<D1, D2, R>& dp,
               Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    The assignment operator
    (\p *this and \p y can be dimension-incompatible).
  */
  Box& operator=(const Box& y);

  /*! \brief
    Swaps \p *this with \p y
    (\p *this and \p y can be dimension-incompatible).
  */
  void m_swap(Box& y);

  //@} Constructors, Assignment, Swap and Destructor

  //! \name Member Functions that Do Not Modify the Box
  //@{

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  /*! \brief
    Returns \f$0\f$, if \p *this is empty; otherwise, returns the
    \ref Affine_Independence_and_Affine_Dimension "affine dimension"
    of \p *this.
  */
  dimension_type affine_dimension() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is an empty box.
  bool is_empty() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a universe box.
  bool is_universe() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    is a topologically closed subset of the vector space.
  */
  bool is_topologically_closed() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is discrete.
  bool is_discrete() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a bounded box.
  bool is_bounded() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    contains at least one integer point.
  */
  bool contains_integer_point() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p var is constrained in
    \p *this.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  bool constrains(Variable var) const;

  //! Returns the relations holding between \p *this and the constraint \p c.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible.
  */
  Poly_Con_Relation relation_with(const Constraint& c) const;

  //! Returns the relations holding between \p *this and the congruence \p cg.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and constraint \p cg are dimension-incompatible.
  */
  Poly_Con_Relation relation_with(const Congruence& cg) const;

  //! Returns the relations holding between \p *this and the generator \p g.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and generator \p g are dimension-incompatible.
  */
  Poly_Gen_Relation relation_with(const Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p expr is
    bounded from above in \p *this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_above(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p expr is
    bounded from below in \p *this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_below(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from above in \p *this, in which case
    the supremum value is computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if and only if the supremum is also the maximum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from above,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d
    and \p maximum are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from above in \p *this, in which case
    the supremum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if and only if the supremum is also the maximum value;

    \param g
    When maximization succeeds, will be assigned the point or
    closure point where \p expr reaches its supremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from above,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d, \p maximum
    and \p g are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from below in \p *this, in which case
    the infimum value is computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if and only if the infimum is also the minimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d
    and \p minimum are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from below in \p *this, in which case
    the infimum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if and only if the infimum is also the minimum value;

    \param g
    When minimization succeeds, will be assigned a point or
    closure point where \p expr reaches its infimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d, \p minimum
    and \p g are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if there exist a
    unique value \p val such that \p *this
    saturates the equality <CODE>expr = val</CODE>.

    \param expr
    The linear expression for which the frequency is needed;

    \param freq_n
    If <CODE>true</CODE> is returned, the value is set to \f$0\f$;
    Present for interface compatibility with class Grid, where
    the \ref Grid_Frequency "frequency" can have a non-zero value;

    \param freq_d
    If <CODE>true</CODE> is returned, the value is set to \f$1\f$;

    \param val_n
    The numerator of \p val;

    \param val_d
    The denominator of \p val;

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If <CODE>false</CODE> is returned, then \p freq_n, \p freq_d,
    \p val_n and \p val_d are left untouched.
  */
  bool frequency(const Linear_Expression& expr,
                 Coefficient& freq_n, Coefficient& freq_d,
                 Coefficient& val_n, Coefficient& val_d) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this contains \p y.

    \exception std::invalid_argument
    Thrown if \p x and \p y are dimension-incompatible.
  */
  bool contains(const Box& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this strictly contains \p y.

    \exception std::invalid_argument
    Thrown if \p x and \p y are dimension-incompatible.
  */
  bool strictly_contains(const Box& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this and \p y are disjoint.

    \exception std::invalid_argument
    Thrown if \p x and \p y are dimension-incompatible.
  */
  bool is_disjoint_from(const Box& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this satisfies
    all its invariants.
  */
  bool OK() const;

  //@} Member Functions that Do Not Modify the Box

  //! \name Space-Dimension Preserving Member Functions that May Modify the Box
  //@{

  /*! \brief
    Adds a copy of constraint \p c to the system of constraints
    defining \p *this.

    \param c
    The constraint to be added.

    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible,
    or \p c is not optimally supported by the Box domain.
  */
  void add_constraint(const Constraint& c);

  /*! \brief
    Adds the constraints in \p cs to the system of constraints
    defining \p *this.

    \param  cs
    The constraints to be added.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible,
    or \p cs contains a constraint which is not optimally supported
    by the box domain.
  */
  void add_constraints(const Constraint_System& cs);

  /*! \brief
    Adds the constraints in \p cs to the system of constraints
    defining \p *this.

    \param  cs
    The constraints to be added. They may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible,
    or \p cs contains a constraint which is not optimally supported
    by the box domain.

    \warning
    The only assumption that can be made on \p cs upon successful or
    exceptional return is that it can be safely destroyed.
  */
  void add_recycled_constraints(Constraint_System& cs);

  /*! \brief
    Adds to \p *this a constraint equivalent to the congruence \p cg.

    \param cg
    The congruence to be added.

    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible,
    or \p cg is not optimally supported by the box domain.
  */
  void add_congruence(const Congruence& cg);

  /*! \brief
    Adds to \p *this constraints equivalent to the congruences in \p cgs.

    \param cgs
    The congruences to be added.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible,
    or \p cgs contains a congruence which is not optimally supported
    by the box domain.
  */
  void add_congruences(const Congruence_System& cgs);

  /*! \brief
    Adds to \p *this constraints equivalent to the congruences in \p cgs.

    \param cgs
    The congruence system to be added to \p *this.  The congruences in
    \p cgs may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible,
    or \p cgs contains a congruence which is not optimally supported
    by the box domain.

    \warning
    The only assumption that can be made on \p cgs upon successful or
    exceptional return is that it can be safely destroyed.
  */
  void add_recycled_congruences(Congruence_System& cgs);

  /*! \brief
    Use the constraint \p c to refine \p *this.

    \param c
    The constraint to be used for refinement.

    \exception std::invalid_argument
    Thrown if \p *this and \p c are dimension-incompatible.
  */
  void refine_with_constraint(const Constraint& c);

  /*! \brief
    Use the constraints in \p cs to refine \p *this.

    \param  cs
    The constraints to be used for refinement.
    To avoid termination problems, each constraint in \p cs
    will be used for a single refinement step.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible.

    \note
    The user is warned that the accuracy of this refinement operator
    depends on the order of evaluation of the constraints in \p cs,
    which is in general unpredictable. If a fine control on such an
    order is needed, the user should consider calling the method
    <code>refine_with_constraint(const Constraint& c)</code> inside
    an appropriate looping construct.
  */
  void refine_with_constraints(const Constraint_System& cs);

  /*! \brief
    Use the congruence \p cg to refine \p *this.

    \param cg
    The congruence to be used for refinement.

    \exception std::invalid_argument
    Thrown if \p *this and \p cg are dimension-incompatible.
  */
  void refine_with_congruence(const Congruence& cg);

  /*! \brief
    Use the congruences in \p cgs to refine \p *this.

    \param  cgs
    The congruences to be used for refinement.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible.
  */
  void refine_with_congruences(const Congruence_System& cgs);

  /*! \brief
    Use the constraint \p c for constraint propagation on \p *this.

    \param c
    The constraint to be used for constraint propagation.

    \exception std::invalid_argument
    Thrown if \p *this and \p c are dimension-incompatible.
  */
  void propagate_constraint(const Constraint& c);

  /*! \brief
    Use the constraints in \p cs for constraint propagation on \p *this.

    \param cs
    The constraints to be used for constraint propagation.

    \param max_iterations
    The maximum number of propagation steps for each constraint in
    \p cs.  If zero (the default), the number of propagation steps
    will be unbounded, possibly resulting in an infinite loop.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible.

    \warning
    This method may lead to non-termination if \p max_iterations is 0.
  */
  void propagate_constraints(const Constraint_System& cs,
                             dimension_type max_iterations = 0);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to space dimension \p var, assigning the result to \p *this.

    \param var
    The space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  void unconstrain(Variable var);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to the set of space dimensions \p vars,
    assigning the result to \p *this.

    \param vars
    The set of space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars.
  */
  void unconstrain(const Variables_Set& vars);

  //! Assigns to \p *this the intersection of \p *this and \p y.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void intersection_assign(const Box& y);

  /*! \brief
    Assigns to \p *this the smallest box containing the union
    of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void upper_bound_assign(const Box& y);

  /*! \brief
    If the upper bound of \p *this and \p y is exact, it is assigned
    to \p *this and <CODE>true</CODE> is returned,
    otherwise <CODE>false</CODE> is returned.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool upper_bound_assign_if_exact(const Box& y);

  /*! \brief
    Assigns to \p *this the difference of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void difference_assign(const Box& y);

  /*! \brief
    Assigns to \p *this a \ref Meet_Preserving_Simplification
    "meet-preserving simplification" of \p *this with respect to \p y.
    If \c false is returned, then the intersection is empty.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool simplify_using_context_assign(const Box& y);

  /*! \brief
    Assigns to \p *this the
    \ref Single_Update_Affine_Functions "affine image"
    of \p *this under the function mapping variable \p var to the
    affine expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is assigned;

    \param expr
    The numerator of the affine expression;

    \param denominator
    The denominator of the affine expression (optional argument with
    default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of
    \p *this.
  */
  void affine_image(Variable var,
                    const Linear_Expression& expr,
                    Coefficient_traits::const_reference denominator
                      = Coefficient_one());

  // FIXME: To be completed.
  /*! \brief
    Assigns to \p *this the \ref affine_form_relation "affine form image"
    of \p *this under the function mapping variable \p var into the
    affine expression(s) specified by \p lf.

    \param var
    The variable to which the affine expression is assigned.

    \param lf
    The linear form on intervals with floating point boundaries that
    defines the affine expression(s). ALL of its coefficients MUST be bounded.

    \exception std::invalid_argument
    Thrown if \p lf and \p *this are dimension-incompatible or if \p var
    is not a dimension of \p *this.

    This function is used in abstract interpretation to model an assignment
    of a value that is correctly overapproximated by \p lf to the
    floating point variable represented by \p var.
  */
  void affine_form_image(Variable var,
                         const Linear_Form<ITV>& lf);

  /*! \brief
    Assigns to \p *this the
    \ref Single_Update_Affine_Functions "affine preimage"
    of \p *this under the function mapping variable \p var to the
    affine expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is substituted;

    \param expr
    The numerator of the affine expression;

    \param denominator
    The denominator of the affine expression (optional argument with
    default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this.
  */
  void affine_preimage(Variable var,
                       const Linear_Expression& expr,
                       Coefficient_traits::const_reference denominator
                         = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym.

    \param var
    The left hand side variable of the generalized affine relation;

    \param relsym
    The relation symbol;

    \param expr
    The numerator of the right hand side affine expression;

    \param denominator
    The denominator of the right hand side affine expression (optional
    argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this.
  */
  void generalized_affine_image(Variable var,
                                Relation_Symbol relsym,
                                const Linear_Expression& expr,
                                Coefficient_traits::const_reference denominator
                                  = Coefficient_one());

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym.

    \param var
    The left hand side variable of the generalized affine relation;

    \param relsym
    The relation symbol;

    \param expr
    The numerator of the right hand side affine expression;

    \param denominator
    The denominator of the right hand side affine expression (optional
    argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this.
  */
  void
  generalized_affine_preimage(Variable var,
                              Relation_Symbol relsym,
                              const Linear_Expression& expr,
                              Coefficient_traits::const_reference denominator
                              = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.

    \param lhs
    The left hand side affine expression;

    \param relsym
    The relation symbol;

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs.
  */
  void generalized_affine_image(const Linear_Expression& lhs,
                                Relation_Symbol relsym,
                                const Linear_Expression& rhs);

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.

    \param lhs
    The left hand side affine expression;

    \param relsym
    The relation symbol;

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs.
  */
  void generalized_affine_preimage(const Linear_Expression& lhs,
                                   Relation_Symbol relsym,
                                   const Linear_Expression& rhs);

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_image(Variable var,
                            const Linear_Expression& lb_expr,
                            const Linear_Expression& ub_expr,
                            Coefficient_traits::const_reference denominator
                            = Coefficient_one());

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_preimage(Variable var,
                               const Linear_Expression& lb_expr,
                               const Linear_Expression& ub_expr,
                               Coefficient_traits::const_reference denominator
                               = Coefficient_one());

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref Time_Elapse_Operator "time-elapse" between \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void time_elapse_assign(const Box& y);

  //! Assigns to \p *this its topological closure.
  void topological_closure_assign();

  /*! \brief
    \ref Wrapping_Operator "Wraps" the specified dimensions of the
    vector space.

    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be wrapped.

    \param w
    The width of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param r
    The representation of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param o
    The overflow behavior of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param cs_p
    Possibly null pointer to a constraint system.  When non-null,
    the pointed-to constraint system is assumed to represent the
    conditional or looping construct guard with respect to which
    wrapping is performed.  Since wrapping requires the computation
    of upper bounds and due to non-distributivity of constraint
    refinement over upper bounds, passing a constraint system in this
    way can be more precise than refining the result of the wrapping
    operation with the constraints in <CODE>*cs_p</CODE>.

    \param complexity_threshold
    A precision parameter which is ignored for the Box domain.

    \param wrap_individually
    A precision parameter which is ignored for the Box domain.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars or with <CODE>*cs_p</CODE>.
  */
  void wrap_assign(const Variables_Set& vars,
                   Bounded_Integer_Type_Width w,
                   Bounded_Integer_Type_Representation r,
                   Bounded_Integer_Type_Overflow o,
                   const Constraint_System* cs_p = 0,
                   unsigned complexity_threshold = 16,
                   bool wrap_individually = true);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates for the space dimensions corresponding to \p vars.

    \param vars
    Points with non-integer coordinates for these variables/space-dimensions
    can be discarded.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(const Variables_Set& vars,
                                    Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref CC76_extrapolation "CC76-widening" between \p *this and \p y.

    \param y
    A box that <EM>must</EM> be contained in \p *this.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  template <typename T>
  typename Enable_If<Is_Same<T, Box>::value
                     && Is_Same_Or_Derived<Interval_Base, ITV>::value,
                     void>::type
  CC76_widening_assign(const T& y, unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref CC76_extrapolation "CC76-widening" between \p *this and \p y.

    \param y
    A box that <EM>must</EM> be contained in \p *this.

    \param first
    An iterator that points to the first stop-point.

    \param last
    An iterator that points one past the last stop-point.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  template <typename T, typename Iterator>
  typename Enable_If<Is_Same<T, Box>::value
                     && Is_Same_Or_Derived<Interval_Base, ITV>::value,
                     void>::type
  CC76_widening_assign(const T& y,
                       Iterator first, Iterator last);

  //! Same as CC76_widening_assign(y, tp).
  void widening_assign(const Box& y, unsigned* tp = 0);

  /*! \brief
    Improves the result of the \ref CC76_extrapolation "CC76-extrapolation"
    computation by also enforcing those constraints in \p cs that are
    satisfied by all the points of \p *this.

    \param y
    A box that <EM>must</EM> be contained in \p *this.

    \param cs
    The system of constraints used to improve the widened box.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cs are dimension-incompatible or
    if \p cs contains a strict inequality.
  */
  void limited_CC76_extrapolation_assign(const Box& y,
                                         const Constraint_System& cs,
                                         unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of restoring in \p y the constraints
    of \p *this that were lost by
    \ref CC76_extrapolation "CC76-extrapolation" applications.

    \param y
    A Box that <EM>must</EM> contain \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.

    \note
    As was the case for widening operators, the argument \p y is meant to
    denote the value computed in the previous iteration step, whereas
    \p *this denotes the value computed in the current iteration step
    (in the <EM>decreasing</EM> iteration sequence). Hence, the call
    <CODE>x.CC76_narrowing_assign(y)</CODE> will assign to \p x
    the result of the computation \f$\mathtt{y} \Delta \mathtt{x}\f$.
  */
  template <typename T>
  typename Enable_If<Is_Same<T, Box>::value
                     && Is_Same_Or_Derived<Interval_Base, ITV>::value,
                     void>::type
  CC76_narrowing_assign(const T& y);

  //@} Space-Dimension Preserving Member Functions that May Modify [...]

  //! \name Member Functions that May Modify the Dimension of the Vector Space
  //@{

  //! Adds \p m new dimensions and embeds the old box into the new space.
  /*!
    \param m
    The number of dimensions to add.

    The new dimensions will be those having the highest indexes in the new
    box, which is defined by a system of interval constraints in which the
    variables running through the new dimensions are unconstrained.
    For instance, when starting from the box \f$\cB \sseq \Rset^2\f$
    and adding a third dimension, the result will be the box
    \f[
      \bigl\{\,
        (x, y, z)^\transpose \in \Rset^3
      \bigm|
        (x, y)^\transpose \in \cB
      \,\bigr\}.
    \f]
  */
  void add_space_dimensions_and_embed(dimension_type m);

  /*! \brief
    Adds \p m new dimensions to the box and does not embed it in
    the new vector space.

    \param m
    The number of dimensions to add.

    The new dimensions will be those having the highest indexes in the
    new box, which is defined by a system of bounded differences in
    which the variables running through the new dimensions are all
    constrained to be equal to 0.
    For instance, when starting from the box \f$\cB \sseq \Rset^2\f$
    and adding a third dimension, the result will be the box
    \f[
      \bigl\{\,
        (x, y, 0)^\transpose \in \Rset^3
      \bigm|
        (x, y)^\transpose \in \cB
      \,\bigr\}.
    \f]
  */
  void add_space_dimensions_and_project(dimension_type m);

  /*! \brief
    Seeing a box as a set of tuples (its points),
    assigns to \p *this all the tuples that can be obtained by concatenating,
    in the order given, a tuple of \p *this with a tuple of \p y.

    Let \f$B \sseq \Rset^n\f$ and \f$D \sseq \Rset^m\f$ be the boxes
    corresponding, on entry, to \p *this and \p y, respectively.
    Upon successful completion, \p *this will represent the box
    \f$R \sseq \Rset^{n+m}\f$ such that
    \f[
      R \defeq
          \Bigl\{\,
            (x_1, \ldots, x_n, y_1, \ldots, y_m)^\transpose
          \Bigm|
            (x_1, \ldots, x_n)^\transpose \in B,
            (y_1, \ldots, y_m)^\transpose \in D
          \,\Bigl\}.
    \f]
    Another way of seeing it is as follows: first increases the space
    dimension of \p *this by adding \p y.space_dimension() new
    dimensions; then adds to the system of constraints of \p *this a
    renamed-apart version of the constraints of \p y.
  */
  void concatenate_assign(const Box& y);

  //! Removes all the specified dimensions.
  /*!
    \param vars
    The set of Variable objects corresponding to the dimensions to be removed.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the Variable
    objects contained in \p vars.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  /*! \brief
    Removes the higher dimensions so that the resulting space
    will have dimension \p new_dimension.

    \exception std::invalid_argument
    Thrown if \p new_dimension is greater than the space dimension
    of \p *this.
  */
  void remove_higher_space_dimensions(dimension_type new_dimension);

  /*! \brief
    Remaps the dimensions of the vector space according to
    a \ref Mapping_the_Dimensions_of_the_Vector_Space "partial function".

    \param pfunc
    The partial function specifying the destiny of each dimension.

    The template type parameter Partial_Function must provide
    the following methods.
    \code
      bool has_empty_codomain() const
    \endcode
    returns <CODE>true</CODE> if and only if the represented partial
    function has an empty co-domain (i.e., it is always undefined).
    The <CODE>has_empty_codomain()</CODE> method will always be called
    before the methods below.  However, if
    <CODE>has_empty_codomain()</CODE> returns <CODE>true</CODE>, none
    of the functions below will be called.
    \code
      dimension_type max_in_codomain() const
    \endcode
    returns the maximum value that belongs to the co-domain
    of the partial function.
    \code
      bool maps(dimension_type i, dimension_type& j) const
    \endcode
    Let \f$f\f$ be the represented function and \f$k\f$ be the value
    of \p i.  If \f$f\f$ is defined in \f$k\f$, then \f$f(k)\f$ is
    assigned to \p j and <CODE>true</CODE> is returned.
    If \f$f\f$ is undefined in \f$k\f$, then <CODE>false</CODE> is
    returned.

    The result is undefined if \p pfunc does not encode a partial
    function with the properties described in the
    \ref Mapping_the_Dimensions_of_the_Vector_Space
    "specification of the mapping operator".
  */
  template <typename Partial_Function>
  void map_space_dimensions(const Partial_Function& pfunc);

  //! Creates \p m copies of the space dimension corresponding to \p var.
  /*!
    \param var
    The variable corresponding to the space dimension to be replicated;

    \param m
    The number of replicas to be created.

    \exception std::invalid_argument
    Thrown if \p var does not correspond to a dimension of the vector space.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the
    vector space to exceed dimension <CODE>max_space_dimension()</CODE>.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    and <CODE>var</CODE> has space dimension \f$k \leq n\f$,
    then the \f$k\f$-th space dimension is
    \ref expand_space_dimension "expanded" to \p m new space dimensions
    \f$n\f$, \f$n+1\f$, \f$\dots\f$, \f$n+m-1\f$.
  */
  void expand_space_dimension(Variable var, dimension_type m);

  //! Folds the space dimensions in \p vars into \p dest.
  /*!
    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be folded;

    \param dest
    The variable corresponding to the space dimension that is the
    destination of the folding operation.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p dest or with
    one of the Variable objects contained in \p vars.
    Also thrown if \p dest is contained in \p vars.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    <CODE>dest</CODE> has space dimension \f$k \leq n\f$,
    \p vars is a set of variables whose maximum space dimension
    is also less than or equal to \f$n\f$, and \p dest is not a member
    of \p vars, then the space dimensions corresponding to
    variables in \p vars are \ref fold_space_dimensions "folded"
    into the \f$k\f$-th space dimension.
  */
  void fold_space_dimensions(const Variables_Set& vars, Variable dest);

  //@} // Member Functions that May Modify the Dimension of the Vector Space

  /*! \brief
    Returns a reference the interval that bounds \p var.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  const ITV& get_interval(Variable var) const;

  /*! \brief
    Sets to \p i the interval that bounds \p var.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  void set_interval(Variable var, const ITV& i);

  /*! \brief
    If the space dimension of \p var is unbounded below, return
    <CODE>false</CODE>. Otherwise return <CODE>true</CODE> and set
    \p n, \p d and \p closed accordingly.

    \note
    It is assumed that <CODE>*this</CODE> is a non-empty box
    having space dimension greater than or equal to that of \p var.
    An undefined behavior is obtained if this assumption is not met.
    \if Include_Implementation_Details
    To be more precise, if <CODE>*this</CODE> is an <EM>empty</EM> box
    (having space dimension greater than or equal to that of \p var)
    such that <CODE>!marked_empty()</CODE> holds, then the method can be
    called without incurring in undefined behavior: it will return
    <EM>unspecified</EM> boundary values that, if queried systematically
    on all space dimensions, will encode the box emptiness.
    \endif

    Let \f$I\f$ be the interval corresponding to variable \p var
    in the non-empty box <CODE>*this</CODE>.
    If \f$I\f$ is not bounded from below, simply return <CODE>false</CODE>
    (leaving all other parameters unchanged).
    Otherwise, set \p n, \p d and \p closed as follows:
     - \p n and \p d are assigned the integers \f$n\f$ and \f$d\f$ such
       that the fraction \f$n/d\f$ corresponds to the greatest lower bound
       of \f$I\f$. The fraction \f$n/d\f$ is in canonical form, meaning
       that \f$n\f$ and \f$d\f$ have no common factors, \f$d\f$ is positive,
       and if \f$n\f$ is zero then \f$d\f$ is one;
     - \p closed is set to <CODE>true</CODE> if and only if the lower
       boundary of \f$I\f$ is closed (i.e., it is included in the interval).
  */
  bool has_lower_bound(Variable var,
                       Coefficient& n, Coefficient& d, bool& closed) const;

  /*! \brief
    If the space dimension of \p var is unbounded above, return
    <CODE>false</CODE>. Otherwise return <CODE>true</CODE> and set
    \p n, \p d and \p closed accordingly.

    \note
    It is assumed that <CODE>*this</CODE> is a non-empty box
    having space dimension greater than or equal to that of \p var.
    An undefined behavior is obtained if this assumption is not met.
    \if Include_Implementation_Details
    To be more precise, if <CODE>*this</CODE> is an <EM>empty</EM> box
    (having space dimension greater than or equal to that of \p var)
    such that <CODE>!marked_empty()</CODE> holds, then the method can be
    called without incurring in undefined behavior: it will return
    <EM>unspecified</EM> boundary values that, if queried systematically
    on all space dimensions, will encode the box emptiness.
    \endif

    Let \f$I\f$ be the interval corresponding to variable \p var
    in the non-empty box <CODE>*this</CODE>.
    If \f$I\f$ is not bounded from above, simply return <CODE>false</CODE>
    (leaving all other parameters unchanged).
    Otherwise, set \p n, \p d and \p closed as follows:
     - \p n and \p d are assigned the integers \f$n\f$ and \f$d\f$ such
       that the fraction \f$n/d\f$ corresponds to the least upper bound
       of \f$I\f$. The fraction \f$n/d\f$ is in canonical form, meaning
       that \f$n\f$ and \f$d\f$ have no common factors, \f$d\f$ is positive,
       and if \f$n\f$ is zero then \f$d\f$ is one;
     - \p closed is set to <CODE>true</CODE> if and only if the upper
       boundary of \f$I\f$ is closed (i.e., it is included in the interval).
  */
  bool has_upper_bound(Variable var,
                       Coefficient& n, Coefficient& d, bool& closed) const;

  //! Returns a system of constraints defining \p *this.
  Constraint_System constraints() const;

  //! Returns a minimized system of constraints defining \p *this.
  Constraint_System minimized_constraints() const;

  //! Returns a system of congruences approximating \p *this.
  Congruence_System congruences() const;

  //! Returns a minimized system of congruences approximating \p *this.
  Congruence_System minimized_congruences() const;

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns a 32-bit hash code for \p *this.

    If <CODE>x</CODE> and <CODE>y</CODE> are such that <CODE>x == y</CODE>,
    then <CODE>x.hash_code() == y.hash_code()</CODE>.
  */
  int32_t hash_code() const;

  PPL_OUTPUT_DECLARATIONS

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  bool ascii_load(std::istream& s);

private:
  template <typename Other_ITV>
  friend class Parma_Polyhedra_Library::Box;

  friend bool
  operator==<ITV>(const Box<ITV>& x, const Box<ITV>& y);

  friend std::ostream&
  Parma_Polyhedra_Library
  ::IO_Operators::operator<<<>(std::ostream& s, const Box<ITV>& box);

  template <typename Specialization, typename Temp, typename To, typename I>
  friend bool Parma_Polyhedra_Library
  ::l_m_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                        const Box<I>& x, const Box<I>& y,
                        const Rounding_Dir dir,
                        Temp& tmp0, Temp& tmp1, Temp& tmp2);

  //! The type of sequence used to implement the box.
  typedef std::vector<ITV> Sequence;

  /*! \brief
    The type of intervals used by inner computations when trying to limit
    the cumulative effect of approximation errors.
  */
  typedef ITV Tmp_Interval_Type;

  //! A sequence of intervals, one for each dimension of the vector space.
  Sequence seq;

#define PPL_IN_Box_CLASS
/* Automatically generated from PPL source file ../src/Box_Status_idefs.hh line 1. */
/* Box<ITV>::Status class declaration.
*/


#ifndef PPL_IN_Box_CLASS
#error "Do not include Box_Status_idefs.hh directly; use Box_defs.hh instead"
#endif

//! A conjunctive assertion about a Box<ITV> object.
/*! \ingroup PPL_CXX_interface
  The assertions supported are:
  - <EM>empty up-to-date</EM>: the empty flag is meaningful;
  - <EM>empty</EM>: the box is the empty set.
  - <EM>universe</EM>: the box is universe \f$n\f$-dimensional vector space
     \f$\Rset^n\f$.

  Not all the conjunctions of these elementary assertions constitute
  a legal Status.  In fact:
  - <EM>empty up-to-date</EM> and <EM>empty</EM> excludes <EM>universe</EM>.
*/
class Status;

class Status {
public:
  //! By default Status is the empty set of assertion.
  Status();

  //! Ordinary copy constructor.
  Status(const Status& y);

  //! Copy constructor from a box of different type.
  template <typename Other_ITV>
  Status(const typename Box<Other_ITV>::Status& y);

  //! \name Test, remove or add an individual assertion from the conjunction.
  //@{
  bool test_empty_up_to_date() const;
  void reset_empty_up_to_date();
  void set_empty_up_to_date();

  bool test_empty() const;
  void reset_empty();
  void set_empty();

  bool test_universe() const;
  void reset_universe();
  void set_universe();
  //@}

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

private:
  //! Status is implemented by means of a finite bitset.
  typedef unsigned int flags_t;

  //! \name Bit-masks for the individual assertions.
  //@{
  static const flags_t NONE             = 0U;
  static const flags_t EMPTY_UP_TO_DATE = 1U << 0;
  static const flags_t EMPTY            = 1U << 1;
  static const flags_t UNIVERSE         = 1U << 2;
  //@}

  //! This holds the current bitset.
  flags_t flags;

  //! Construct from a bit-mask.
  Status(flags_t mask);

  //! Check whether <EM>all</EM> bits in \p mask are set.
  bool test_all(flags_t mask) const;

  //! Check whether <EM>at least one</EM> bit in \p mask is set.
  bool test_any(flags_t mask) const;

  //! Set the bits in \p mask.
  void set(flags_t mask);

  //! Reset the bits in \p mask.
  void reset(flags_t mask);
};

/* Automatically generated from PPL source file ../src/Box_defs.hh line 1769. */
#undef PPL_IN_Box_CLASS

  //! The status flags to keep track of the internal state.
  Status status;

  /*! \brief
    Returns <CODE>true</CODE> if and only if the box is known to be empty.

    The return value <CODE>false</CODE> does not necessarily
    implies that \p *this is non-empty.
  */
  bool marked_empty() const;

public:
  //! Causes the box to become empty, i.e., to represent the empty set.
  void set_empty();

private:
  //! Marks \p *this as definitely not empty.
  void set_nonempty();

  //! Asserts the validity of the empty flag of \p *this.
  void set_empty_up_to_date();

  //! Invalidates empty flag of \p *this.
  void reset_empty_up_to_date();

  /*! \brief
    Checks the hard way whether \p *this is an empty box:
    returns <CODE>true</CODE> if and only if it is so.
  */
  bool check_empty() const;

   /*! \brief
     Returns a reference the interval that bounds
     the box on the <CODE>k</CODE>-th space dimension.
   */
  const ITV& operator[](dimension_type k) const;

  /*! \brief
    WRITE ME.
  */
  static I_Result
  refine_interval_no_check(ITV& itv,
                           Constraint::Type type,
                           Coefficient_traits::const_reference numer,
                           Coefficient_traits::const_reference denom);

  /*! \brief
    WRITE ME.
  */
  void
  add_interval_constraint_no_check(dimension_type var_id,
                                   Constraint::Type type,
                                   Coefficient_traits::const_reference numer,
                                   Coefficient_traits::const_reference denom);

  /*! \brief
    WRITE ME.
  */
  void add_constraint_no_check(const Constraint& c);

  /*! \brief
    WRITE ME.
  */
  void add_constraints_no_check(const Constraint_System& cs);

  /*! \brief
    WRITE ME.
  */
  void add_congruence_no_check(const Congruence& cg);

  /*! \brief
    WRITE ME.
  */
  void add_congruences_no_check(const Congruence_System& cgs);

  /*! \brief
    Uses the constraint \p c to refine \p *this.

    \param c
    The constraint to be used for the refinement.

    \warning
    If \p c and \p *this are dimension-incompatible,
    the behavior is undefined.
  */
  void refine_no_check(const Constraint& c);

  /*! \brief
    Uses the constraints in \p cs to refine \p *this.

    \param cs
    The constraints to be used for the refinement.
    To avoid termination problems, each constraint in \p cs
    will be used for a single refinement step.

    \warning
    If \p cs and \p *this are dimension-incompatible,
    the behavior is undefined.
  */
  void refine_no_check(const Constraint_System& cs);

  /*! \brief
    Uses the congruence \p cg to refine \p *this.

    \param cg
    The congruence to be added.
    Nontrivial proper congruences are ignored.

    \warning
    If \p cg and \p *this are dimension-incompatible,
    the behavior is undefined.
  */
  void refine_no_check(const Congruence& cg);

  /*! \brief
    Uses the congruences in \p cgs to refine \p *this.

    \param cgs
    The congruences to be added.
    Nontrivial proper congruences are ignored.

    \warning
    If \p cgs and \p *this are dimension-incompatible,
    the behavior is undefined.
  */
  void refine_no_check(const Congruence_System& cgs);

  /*! \brief
    Propagates the constraint \p c to refine \p *this.

    \param c
    The constraint to be propagated.

    \warning
    If \p c and \p *this are dimension-incompatible,
    the behavior is undefined.

    \warning
    This method may lead to non-termination.

    \if Include_Implementation_Details

    For any expression \f$e\f$, we denote by
    \f$\left\uparrow e \right\uparrow\f$ (resp., \f$\left\downarrow e
    \right\downarrow\f$) the result of any computation that is
    guaranteed to yield an upper (resp., lower) approximation of
    \f$e\f$.  So there exists \f$\epsilon \in \Rset\f$ with
    \f$\epsilon \geq 0\f$ such that
    \f$\left\uparrow e \right\uparrow = e + \epsilon\f$.
    If \f$\epsilon = 0\f$ we say that the computation of
    \f$\left\uparrow e \right\uparrow\f$ is <EM>exact</EM>;
    we say it is <EM>inexact</EM> otherwise.
    Similarly for \f$\left\downarrow e \right\downarrow\f$.

    Consider a constraint of the general form
    \f[
      z + \sum_{i \in I}{a_ix_i} \relsym 0,
    \f]
    where \f$z \in \Zset\f$, \f$I\f$ is a set of indices,
    \f$a_i \in \Zset\f$ with \f$a_i \neq 0\f$ for each \f$i \in I\f$, and
    \f$\mathord{\relsym} \in \{ \mathord{\geq}, \mathord{>}, \mathord{=} \}\f$.
    The set \f$I\f$ is subdivided into the disjoint sets \f$P\f$ and \f$N\f$
    such that, for each \f$i \in I\f$, \f$a_i > 0\f$ if \f$i \in P\f$ and
    \f$a_i < 0\f$ if \f$i \in N\f$.
    Suppose that, for each \f$i \in P \union N\f$ a variation interval
    \f$\chi_i \sseq \Rset\f$ is known for \f$x_i\f$ and that the infimum
    and the supremum of \f$\chi_i\f$ are denoted, respectively,
    by \f$\chi_i^\mathrm{l}\f$ and \f$\chi_i^\mathrm{u}\f$, where
    \f$\chi_i^\mathrm{l}, \chi_i^\mathrm{u} \in \Rset \union \{ -\infty, +\infty \}\f$.

    For each \f$k \in P\f$, we have
    \f[
      x_k
        \relsym
          \frac{1}{a_k}
            \Biggl(
              - z
              - \sum_{i \in N}{a_ix_i}
              - \sum_{\genfrac{}{}{0pt}{}
                              {\scriptstyle i \in P}
                              {\scriptstyle i \neq k}}{a_ix_i}
            \Biggr).
    \f]
    Thus, if \f$\chi_i^\mathrm{l} \in \Rset\f$ for each \f$i \in N\f$ and
    \f$\chi_i^\mathrm{u} \in \Rset\f$ for each \f$i \in P \setdiff \{ k \}\f$,
    we have
    \f[
      x_k
        \geq
          \Biggl\downarrow
          \frac{1}{a_k}
            \Biggl(
              - z
              - \sum_{i \in N}{a_i\chi_i^\mathrm{l}}
              - \sum_{\genfrac{}{}{0pt}{}
                              {\scriptstyle i \in P}
                              {\scriptstyle i \neq k}}{a_i\chi_i^\mathrm{u}}
            \Biggr)
          \Biggr\downarrow
    \f]
    and, if \f$\mathord{\relsym} \in \{ \mathord{=} \}\f$,
    \f$\chi_i^\mathrm{u} \in \Rset\f$ for each \f$i \in N\f$ and
    \f$\chi_i^\mathrm{l} \in \Rset\f$ for each \f$P \setdiff \{ k \}\f$,
    \f[
      x_k
        \leq
          \Biggl\uparrow
          \frac{1}{a_k}
            \Biggl(
              - z
              - \sum_{i \in N}{a_i\chi_i^\mathrm{u}}
              - \sum_{\genfrac{}{}{0pt}{}
                              {\scriptstyle i \in P}
                              {\scriptstyle i \neq k}}{a_i\chi_i^\mathrm{l}}
            \Biggr)
          \Biggl\uparrow.
    \f]
    In the first inequality, the relation is strict if
    \f$\mathord{\relsym} \in \{ \mathord{>} \}\f$, or if
    \f$\chi_i^\mathrm{l} \notin \chi_i\f$ for some \f$i \in N\f$, or if
    \f$\chi_i^\mathrm{u} \notin \chi_i\f$ for some
    \f$i \in P \setdiff \{ k \}\f$, or if the computation is inexact.
    In the second inequality, the relation is strict if
    \f$\chi_i^\mathrm{u} \notin \chi_i\f$ for some \f$i \in N\f$, or if
    \f$\chi_i^\mathrm{l} \notin \chi_i\f$ for some
    \f$i \in P \setdiff \{ k \}\f$, or if the computation is inexact.

    For each \f$k \in N\f$, we have
    \f[
      \frac{1}{a_k}
        \Biggl(
          - z
          - \sum_{\genfrac{}{}{0pt}{}
                          {\scriptstyle i \in N}
                          {\scriptstyle i \neq k}}{a_ix_i}
          - \sum_{i \in P}{a_ix_i}
        \Biggr)
          \relsym
            x_k.
    \f]
    Thus, if
    \f$\chi_i^\mathrm{l} \in \Rset\f$
    for each \f$i \in N \setdiff \{ k \}\f$ and
    \f$\chi_i^\mathrm{u} \in \Rset\f$ for each \f$i \in P\f$,
    we have
    \f[
      \Biggl\uparrow
      \frac{1}{a_k}
        \Biggl(
          - z
          - \sum_{\genfrac{}{}{0pt}{}
                          {\scriptstyle i \in N}
                          {\scriptstyle i \neq k}}{a_i\chi_i^\mathrm{l}}
          - \sum_{i \in P}{a_i\chi_i^\mathrm{u}}
        \Biggr)
      \Biggl\uparrow
        \geq
          x_k
    \f]
    and, if \f$\mathord{\relsym} \in \{ \mathord{=} \}\f$,
    \f$\chi_i^\mathrm{u} \in \Rset\f$ for each \f$i \in N \setdiff \{ k \}\f$
    and \f$\chi_i^\mathrm{l} \in \Rset\f$ for each \f$i \in P\f$,
    \f[
      \Biggl\downarrow
      \frac{1}{a_k}
        \Biggl(
          - z
          - \sum_{\genfrac{}{}{0pt}{}
                          {\scriptstyle i \in N}
                          {\scriptstyle i \neq k}}{a_i\chi_i^\mathrm{u}}
          - \sum_{i \in P}{a_i\chi_i^\mathrm{l}}
        \Biggr)
      \Biggl\downarrow
        \leq
          x_k.
    \f]
    In the first inequality, the relation is strict if
    \f$\mathord{\relsym} \in \{ \mathord{>} \}\f$, or if
    \f$\chi_i^\mathrm{u} \notin \chi_i\f$ for some \f$i \in P\f$, or if
    \f$\chi_i^\mathrm{l} \notin \chi_i\f$ for some
    \f$i \in N \setdiff \{ k \}\f$, or if the computation is inexact.
    In the second inequality, the relation is strict if
    \f$\chi_i^\mathrm{l} \notin \chi_i\f$ for some \f$i \in P\f$, or if
    \f$\chi_i^\mathrm{u} \notin \chi_i\f$ for some
    \f$i \in N \setdiff \{ k \}\f$, or if the computation is inexact.
    \endif
  */
  void propagate_constraint_no_check(const Constraint& c);

  /*! \brief
    Propagates the constraints in \p cs to refine \p *this.

    \param  cs
    The constraints to be propagated.

    \param max_iterations
    The maximum number of propagation steps for each constraint in \p cs.
    If zero, the number of propagation steps will be unbounded, possibly
    resulting in an infinite loop.

    \warning
    If \p cs and \p *this are dimension-incompatible,
    the behavior is undefined.

    \warning
    This method may lead to non-termination if \p max_iterations is 0.
  */
  void propagate_constraints_no_check(const Constraint_System& cs,
                                      dimension_type max_iterations);

  //! Checks if and how \p expr is bounded in \p *this.
  /*!
    Returns <CODE>true</CODE> if and only if \p from_above is
    <CODE>true</CODE> and \p expr is bounded from above in \p *this,
    or \p from_above is <CODE>false</CODE> and \p expr is bounded
    from below in \p *this.

    \param expr
    The linear expression to test;

    \param from_above
    <CODE>true</CODE> if and only if the boundedness of interest is
    "from above".

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds(const Linear_Expression& expr, bool from_above) const;

  //! Maximizes or minimizes \p expr subject to \p *this.
  /*!
    \param expr
    The linear expression to be maximized or minimized subject to \p *this;

    \param maximize
    <CODE>true</CODE> if maximization is what is wanted;

    \param ext_n
    The numerator of the extremum value;

    \param ext_d
    The denominator of the extremum value;

    \param included
    <CODE>true</CODE> if and only if the extremum of \p expr can
    actually be reached in \p *this;

    \param g
    When maximization or minimization succeeds, will be assigned
    a point or closure point where \p expr reaches the
    corresponding extremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded in the appropriate
    direction, <CODE>false</CODE> is returned and \p ext_n, \p ext_d,
    \p included and \p g are left untouched.
  */
  bool max_min(const Linear_Expression& expr,
               bool maximize,
               Coefficient& ext_n, Coefficient& ext_d, bool& included,
               Generator& g) const;

  //! Maximizes or minimizes \p expr subject to \p *this.
  /*!
    \param expr
    The linear expression to be maximized or minimized subject to \p *this;

    \param maximize
    <CODE>true</CODE> if maximization is what is wanted;

    \param ext_n
    The numerator of the extremum value;

    \param ext_d
    The denominator of the extremum value;

    \param included
    <CODE>true</CODE> if and only if the extremum of \p expr can
    actually be reached in \p * this;

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded in the appropriate
    direction, <CODE>false</CODE> is returned and \p ext_n, \p ext_d,
    \p included and \p point are left untouched.
  */
  bool max_min(const Linear_Expression& expr,
               bool maximize,
               Coefficient& ext_n, Coefficient& ext_d, bool& included) const;

  /*! \brief
    Adds to \p limiting_box the interval constraints in \p cs
    that are satisfied by \p *this.
  */
  void get_limiting_box(const Constraint_System& cs,
                        Box& limiting_box) const;

  //! \name Exception Throwers
  //@{
  void throw_dimension_incompatible(const char* method,
                                    const Box& y) const;

  void throw_dimension_incompatible(const char* method,
                                    dimension_type required_dim) const;

  void throw_dimension_incompatible(const char* method,
                                    const Constraint& c) const;

  void throw_dimension_incompatible(const char* method,
                                    const Congruence& cg) const;

  void throw_dimension_incompatible(const char* method,
                                    const Constraint_System& cs) const;

  void throw_dimension_incompatible(const char* method,
                                    const Congruence_System& cgs) const;

  void throw_dimension_incompatible(const char* method,
                                    const Generator& g) const;

  void throw_dimension_incompatible(const char* method,
                                    const char* le_name,
                                    const Linear_Expression& le) const;

  template <typename C>
  void throw_dimension_incompatible(const char* method,
                                    const char* lf_name,
                                    const Linear_Form<C>& lf) const;

  static void throw_constraint_incompatible(const char* method);

  static void throw_expression_too_complex(const char* method,
                                           const Linear_Expression& le);

  static void throw_invalid_argument(const char* method, const char* reason);
  //@} // Exception Throwers
};

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Returns the relations holding between an interval and
  an interval constraint.

  \param i
  The interval;

  \param constraint_type
  The constraint type;

  \param numer
  The numerator of the constraint bound;

  \param denom
  The denominator of the constraint bound

  The interval constraint has the form
  <CODE>denom * Variable(0) relsym numer</CODE>
  where relsym is  <CODE>==</CODE>,  <CODE>></CODE> or  <CODE>>=</CODE>
  depending on the <CODE>constraint_type</CODE>.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename ITV>
Poly_Con_Relation
interval_relation(const ITV& i,
                  const Constraint::Type constraint_type,
                  Coefficient_traits::const_reference numer,
                  Coefficient_traits::const_reference denom
                  = Coefficient_one());

class Box_Helpers {
public:
  // This is declared here so that Linear_Expression needs to be friend of
  // Box_Helpers only, and doesn't need to be friend of this, too.
  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! Decodes the constraint \p c as an interval constraint.
  /*! \relates Box
    \return
    <CODE>true</CODE> if the constraint \p c is an
    \ref intervals "interval constraint";
    <CODE>false</CODE> otherwise.

    \param c
    The constraint to be decoded.

    \param c_num_vars
    If <CODE>true</CODE> is returned, then it will be set to the number
    of variables having a non-zero coefficient. The only legal values
    will therefore be 0 and 1.

    \param c_only_var
    If <CODE>true</CODE> is returned and if \p c_num_vars is not set to 0,
    then it will be set to the index of the only variable having
    a non-zero coefficient in \p c.
  */
  #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  static bool extract_interval_constraint(const Constraint& c,
                                          dimension_type& c_num_vars,
                                          dimension_type& c_only_var);

  // This is declared here so that Linear_Expression needs to be friend of
  // Box_Helpers only, and doesn't need to be friend of this, too.
  static bool extract_interval_congruence(const Congruence& cg,
                                          dimension_type& cg_num_vars,
                                          dimension_type& cg_only_var);
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Box_Status_inlines.hh line 1. */
/* Box<ITV>::Status class implementation: inline functions.
*/


#include <string>

namespace Parma_Polyhedra_Library {

template <typename ITV>
inline
Box<ITV>::Status::Status(flags_t mask)
  : flags(mask) {
}

template <typename ITV>
inline
Box<ITV>::Status::Status(const Status& y)
  : flags(y.flags) {
}

template <typename ITV>
template <typename Other_ITV>
inline
Box<ITV>::Status::Status(const typename Box<Other_ITV>::Status& y)
  : flags(y.flags) {
}

template <typename ITV>
inline
Box<ITV>::Status::Status()
  : flags(NONE) {
}

template <typename ITV>
inline bool
Box<ITV>::Status::test_all(flags_t mask) const {
  return (flags & mask) == mask;
}

template <typename ITV>
inline bool
Box<ITV>::Status::test_any(flags_t mask) const {
  return (flags & mask) != 0;
}

template <typename ITV>
inline void
Box<ITV>::Status::set(flags_t mask) {
  flags |= mask;
}

template <typename ITV>
inline void
Box<ITV>::Status::reset(flags_t mask) {
  flags &= ~mask;
}

template <typename ITV>
inline bool
Box<ITV>::Status::test_empty_up_to_date() const {
  return test_any(EMPTY_UP_TO_DATE);
}

template <typename ITV>
inline void
Box<ITV>::Status::reset_empty_up_to_date() {
  reset(EMPTY_UP_TO_DATE);
}

template <typename ITV>
inline void
Box<ITV>::Status::set_empty_up_to_date() {
  set(EMPTY_UP_TO_DATE);
}

template <typename ITV>
inline bool
Box<ITV>::Status::test_empty() const {
  return test_any(EMPTY);
}

template <typename ITV>
inline void
Box<ITV>::Status::reset_empty() {
  reset(EMPTY);
}

template <typename ITV>
inline void
Box<ITV>::Status::set_empty() {
  set(EMPTY);
}

template <typename ITV>
inline bool
Box<ITV>::Status::test_universe() const {
  return test_any(UNIVERSE);
}

template <typename ITV>
inline void
Box<ITV>::Status::reset_universe() {
  reset(UNIVERSE);
}

template <typename ITV>
inline void
Box<ITV>::Status::set_universe() {
  set(UNIVERSE);
}

template <typename ITV>
bool
Box<ITV>::Status::OK() const {
  if (test_empty_up_to_date()
      && test_empty()
      && test_universe()) {
#ifndef NDEBUG
    std::cerr
      << "The status asserts emptiness and universality at the same time."
      << std::endl;
#endif
    return false;
  }

  // Any other case is OK.
  return true;
}


namespace Implementation {

namespace Boxes {

// These are the keywords that indicate the individual assertions.
extern const char* empty_up_to_date;
extern const char* empty;
extern const char* universe;
const char yes = '+';
const char no = '-';
const char separator = ' ';

/*! \relates Parma_Polyhedra_Library::Box::Status
  Reads a keyword and its associated on/off flag from \p s.
  Returns <CODE>true</CODE> if the operation is successful,
  returns <CODE>false</CODE> otherwise.
  When successful, \p positive is set to <CODE>true</CODE> if the flag
  is on; it is set to <CODE>false</CODE> otherwise.
*/
inline bool
get_field(std::istream& s, const char* keyword, bool& positive) {
  std::string str;
  if (!(s >> str)
      || (str[0] != yes && str[0] != no)
      || str.substr(1) != keyword) {
    return false;
  }
  positive = (str[0] == yes);
  return true;
}

} // namespace Boxes

} // namespace Implementation

template <typename ITV>
void
Box<ITV>::Status::ascii_dump(std::ostream& s) const {
  using namespace Implementation::Boxes;
  s << (test_empty_up_to_date() ? yes : no) << empty_up_to_date << separator
    << (test_empty() ? yes : no) << empty << separator
    << (test_universe() ? yes : no) << universe << separator;
}

PPL_OUTPUT_TEMPLATE_DEFINITIONS_ASCII_ONLY(ITV, Box<ITV>::Status)

template <typename ITV>
bool
Box<ITV>::Status::ascii_load(std::istream& s) {
  using namespace Implementation::Boxes;
  PPL_UNINITIALIZED(bool, positive);

  if (!get_field(s, Implementation::Boxes::empty_up_to_date, positive)) {
    return false;
  }
  if (positive) {
    set_empty_up_to_date();
  }

  if (!get_field(s, Implementation::Boxes::empty, positive)) {
    return false;
  }
  if (positive) {
    set_empty();
  }
  if (!get_field(s, universe, positive)) {
    return false;
  }
  if (positive) {
    set_universe();
  }
  else {
    reset_universe();
  }

  // Check invariants.
  PPL_ASSERT(OK());
  return true;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Box_inlines.hh line 1. */
/* Box class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Constraint_System_defs.hh line 1. */
/* Constraint_System class declaration.
*/


/* Automatically generated from PPL source file ../src/Constraint_System_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/Linear_System_defs.hh line 1. */
/* Linear_System class declaration.
*/


/* Automatically generated from PPL source file ../src/Linear_System_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/Swapping_Vector_defs.hh line 1. */
/* Swapping_Vector class declaration.
*/


/* Automatically generated from PPL source file ../src/Swapping_Vector_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename T>
class Swapping_Vector;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Swapping_Vector_defs.hh line 29. */
#include <vector>

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A wrapper for std::vector that calls a swap() method instead of copying
//! elements, when possible.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Swapping_Vector {

public:
  typedef typename std::vector<T>::const_iterator const_iterator;
  typedef typename std::vector<T>::iterator iterator;
  typedef typename std::vector<T>::size_type size_type;

  Swapping_Vector();
  explicit Swapping_Vector(dimension_type new_size);
  Swapping_Vector(dimension_type new_size, const T& x);

  void clear();
  void reserve(dimension_type new_capacity);
  void resize(dimension_type new_size);
  void resize(dimension_type new_size, const T& x);

  dimension_type size() const;
  dimension_type capacity() const;
  bool empty() const;

  void m_swap(Swapping_Vector& v);

  T& operator[](dimension_type i);
  const T& operator[](dimension_type i) const;

  T& back();
  const T& back() const;

  void push_back(const T& x);
  void pop_back();

  iterator begin();
  iterator end();
  const_iterator begin() const;
  const_iterator end() const;

  iterator erase(iterator itr);
  iterator erase(iterator first, iterator last);

  // This is defined only if T has an external_memory_in_bytes() method.
  memory_size_type external_memory_in_bytes() const;

  dimension_type max_num_rows();

private:
  std::vector<T> impl;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Swapping_Vector */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
void swap(Swapping_Vector<T>& x, Swapping_Vector<T>& y);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Swapping_Vector_inlines.hh line 1. */
/* Swapping_Vector class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Swapping_Vector_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename T>
inline
Swapping_Vector<T>::Swapping_Vector()
  : impl() {
}

template <typename T>
inline
Swapping_Vector<T>::Swapping_Vector(dimension_type i)
  : impl() {
  // NOTE: This is not the same as constructing impl as `impl(i)', because
  // this implementation calls compute_capacity().
  resize(i);
}

template <typename T>
inline
Swapping_Vector<T>::Swapping_Vector(dimension_type new_size, const T& x)
  : impl() {
  resize(new_size, x);
}

template <typename T>
inline void
Swapping_Vector<T>::clear() {
  impl.clear();
}

template <typename T>
inline void
Swapping_Vector<T>::reserve(dimension_type new_capacity) {
  if (impl.capacity() < new_capacity) {
    // Reallocation will take place.
    std::vector<T> new_impl;

    new_impl.reserve(compute_capacity(new_capacity, max_num_rows()));
    new_impl.resize(impl.size());

    using std::swap;

    // Steal the old elements.
    for (dimension_type i = impl.size(); i-- > 0; ) {
      swap(new_impl[i], impl[i]);
    }

    // Put the new vector into place.
    swap(impl, new_impl);
  }
}

template <typename T>
inline void
Swapping_Vector<T>::resize(dimension_type new_size) {
  reserve(new_size);
  impl.resize(new_size);
}

template <typename T>
inline void
Swapping_Vector<T>::resize(dimension_type new_size, const T& x) {
  reserve(new_size);
  impl.resize(new_size, x);
}

template <typename T>
inline dimension_type
Swapping_Vector<T>::size() const {
  return impl.size();
}

template <typename T>
inline dimension_type
Swapping_Vector<T>::capacity() const {
  return impl.capacity();
}

template <typename T>
inline bool
Swapping_Vector<T>::empty() const {
  return impl.empty();
}

template <typename T>
inline void
Swapping_Vector<T>::m_swap(Swapping_Vector& v) {
  using std::swap;
  swap(impl, v.impl);
}

template <typename T>
inline T&
Swapping_Vector<T>::operator[](dimension_type i) {
  return impl[i];
}

template <typename T>
inline const T&
Swapping_Vector<T>::operator[](dimension_type i) const {
  return impl[i];
}

template <typename T>
inline T&
Swapping_Vector<T>::back() {
  return impl.back();
}

template <typename T>
inline const T&
Swapping_Vector<T>::back() const {
  return impl.back();
}

template <typename T>
inline void
Swapping_Vector<T>::push_back(const T& x) {
  reserve(size() + 1);
  impl.push_back(x);
}

template <typename T>
inline void
Swapping_Vector<T>::pop_back() {
  impl.pop_back();
}

template <typename T>
inline memory_size_type
Swapping_Vector<T>::external_memory_in_bytes() const {
  // Estimate the size of vector.
  memory_size_type n = impl.capacity() * sizeof(T);
  for (const_iterator i = begin(), i_end = end(); i != i_end; ++i) {
    n += i->external_memory_in_bytes();
  }
  return n;
}

template <typename T>
inline typename Swapping_Vector<T>::iterator
Swapping_Vector<T>::begin() {
  return impl.begin();
}

template <typename T>
inline typename Swapping_Vector<T>::iterator
Swapping_Vector<T>::end() {
  return impl.end();
}

template <typename T>
inline typename Swapping_Vector<T>::const_iterator
Swapping_Vector<T>::begin() const {
  return impl.begin();
}

template <typename T>
inline typename Swapping_Vector<T>::const_iterator
Swapping_Vector<T>::end() const {
  return impl.end();
}

template <typename T>
inline typename Swapping_Vector<T>::iterator
Swapping_Vector<T>::erase(iterator itr) {
  PPL_ASSERT(itr >= begin());
  PPL_ASSERT(itr < end());
  const dimension_type old_i = itr - begin();
  dimension_type i = old_i;
  ++i;
  while (i != size()) {
    swap(impl[i-1], impl[i]);
  }
  impl.pop_back();
  return begin() + old_i;
}

template <typename T>
inline typename Swapping_Vector<T>::iterator
Swapping_Vector<T>::erase(iterator first, iterator last) {
  PPL_ASSERT(begin() <= first);
  PPL_ASSERT(first <= last);
  PPL_ASSERT(last <= end());
  const iterator old_first = first;
  typedef typename std::iterator_traits<iterator>::difference_type diff_t;
  const diff_t k = last - first;
  const dimension_type n = static_cast<dimension_type>(end() - last);
  using std::swap;
  for (dimension_type i = 0; i < n; ++i, ++first) {
    swap(*first, *(first + k));
  }
  impl.erase(end() - k, end());
  return old_first;
}

template <typename T>
inline dimension_type
Swapping_Vector<T>::max_num_rows() {
  return impl.max_size();
}

template <typename T>
inline void
swap(Swapping_Vector<T>& vec1, Swapping_Vector<T>& vec2) {
  vec1.m_swap(vec2);
}

} // namespace Parma_Polyhedra_Library


/* Automatically generated from PPL source file ../src/Swapping_Vector_defs.hh line 95. */

/* Automatically generated from PPL source file ../src/Linear_System_defs.hh line 33. */

/* Automatically generated from PPL source file ../src/Bit_Row_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Bit_Row;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Bit_Matrix_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Bit_Matrix;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_System_defs.hh line 39. */

// TODO: Check how much of this description is still true.
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! The base class for systems of constraints and generators.
/*! \ingroup PPL_CXX_interface
  An object of this class represents either a constraint system
  or a generator system. Each Linear_System object can be viewed
  as a finite sequence of strong-normalized Row objects,
  where each Row implements a constraint or a generator.
  Linear systems are characterized by the matrix of coefficients,
  also encoding the number, size and capacity of Row objects,
  as well as a few additional information, including:
   - the topological kind of (all) the rows;
   - an indication of whether or not some of the rows in the Linear_System
     are <EM>pending</EM>, meaning that they still have to undergo
     an (unspecified) elaboration; if there are pending rows, then these
     form a proper suffix of the overall sequence of rows;
   - a Boolean flag that, when <CODE>true</CODE>, ensures that the
     non-pending prefix of the sequence of rows is sorted.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Row>
class Parma_Polyhedra_Library::Linear_System {
public:

  // NOTE: `iterator' is actually a const_iterator.
  typedef typename Swapping_Vector<Row>::const_iterator iterator;
  typedef typename Swapping_Vector<Row>::const_iterator const_iterator;

  //! Builds an empty linear system with specified topology.
  /*!
    Rows size and capacity are initialized to \f$0\f$.
  */
  Linear_System(Topology topol, Representation r);

  //! Builds a system with specified topology and dimensions.
  /*!
    \param topol
    The topology of the system that will be created;

    \param space_dim
    The number of space dimensions of the system that will be created.

    \param r
    The representation for system's rows.

    Creates a \p n_rows \f$\times\f$ \p space_dim system whose
    coefficients are all zero and with the given topology.
  */
  Linear_System(Topology topol, dimension_type space_dim, Representation r);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! A tag class.
  /*! \ingroup PPL_CXX_interface
    Tag class to differentiate the Linear_System copy constructor that
    copies pending rows as pending from the one that transforms
    pending rows into non-pending ones.
  */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  struct With_Pending {
  };

  //! Copy constructor: pending rows are transformed into non-pending ones.
  Linear_System(const Linear_System& y);

  //! Copy constructor with specified representation. Pending rows are
  //! transformed into non-pending ones.
  Linear_System(const Linear_System& y, Representation r);

  //! Full copy constructor: pending rows are copied as pending.
  Linear_System(const Linear_System& y, With_Pending);

  //! Full copy constructor: pending rows are copied as pending.
  Linear_System(const Linear_System& y, Representation r, With_Pending);

  //! Assignment operator: pending rows are transformed into non-pending ones.
  Linear_System& operator=(const Linear_System& y);

  //! Full assignment operator: pending rows are copied as pending.
  void assign_with_pending(const Linear_System& y);

  //! Swaps \p *this with \p y.
  void m_swap(Linear_System& y);

  //! Returns the current representation of *this.
  Representation representation() const;

  //! Converts *this to the specified representation.
  void set_representation(Representation r);

  //! Returns the maximum space dimension a Linear_System can handle.
  static dimension_type max_space_dimension();

  //! Returns the space dimension of the rows in the system.
  /*!
    The computation of the space dimension correctly ignores
    the column encoding the inhomogeneous terms of constraint
    (resp., the divisors of generators);
    if the system topology is <CODE>NOT_NECESSARILY_CLOSED</CODE>,
    also the column of the \f$\epsilon\f$-dimension coefficients
    will be ignored.
  */
  dimension_type space_dimension() const;

  //! Sets the space dimension of the rows in the system to \p space_dim .
  void set_space_dimension(dimension_type space_dim);

  //! Makes the system shrink by removing its \p n trailing rows.
  void remove_trailing_rows(dimension_type n);

  //! Makes the system shrink by removing its i-th row.
  /*!
    When \p keep_sorted is \p true and the system is sorted, sortedness will
    be preserved, but this method costs O(n).

    Otherwise, this method just swaps the i-th row with the last and then
    removes it, so it costs O(1).
  */
  void remove_row(dimension_type i, bool keep_sorted = false);

  //! Makes the system shrink by removing the rows in [first,last).
  /*!
    When \p keep_sorted is \p true and the system is sorted, sortedness will
    be preserved, but this method costs O(num_rows()).

    Otherwise, this method just swaps the rows with the last ones and then
    removes them, so it costs O(last - first).
  */
  void remove_rows(dimension_type first, dimension_type last,
                  bool keep_sorted = false);

  // TODO: Consider removing this.
  //! Removes the specified rows. The row ordering of remaining rows is
  //! preserved.
  /*!
    \param indexes specifies a list of row indexes.
                   It must be sorted.
  */
  void remove_rows(const std::vector<dimension_type>& indexes);

  // TODO: Consider making this private.
  //! Removes all the specified dimensions from the system.
  /*!
    The space dimension of the variable with the highest space
    dimension in \p vars must be at most the space dimension
    of \p this.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  //! Shift by \p n positions the coefficients of variables, starting from
  //! the coefficient of \p v. This increases the space dimension by \p n.
  void shift_space_dimensions(Variable v, dimension_type n);

  // TODO: Consider making this private.
  //! Permutes the space dimensions of the matrix.
  /*
    \param cycle
    A vector representing a cycle of the permutation according to which the
    space dimensions must be rearranged.

    The \p cycle vector represents a cycle of a permutation of space
    dimensions.
    For example, the permutation
    \f$ \{ x_1 \mapsto x_2, x_2 \mapsto x_3, x_3 \mapsto x_1 \}\f$ can be
    represented by the vector containing \f$ x_1, x_2, x_3 \f$.
  */
  void permute_space_dimensions(const std::vector<Variable>& cycle);

  //! Swaps the coefficients of the variables \p v1 and \p v2 .
  void swap_space_dimensions(Variable v1, Variable v2);

  //! \name Subscript operators
  //@{
  //! Returns a const reference to the \p k-th row of the system.
  const Row& operator[](dimension_type k) const;
  //@} // Subscript operators

  iterator begin();
  iterator end();
  const_iterator begin() const;
  const_iterator end() const;

  bool has_no_rows() const;
  dimension_type num_rows() const;

  //! Strongly normalizes the system.
  void strong_normalize();

  //! Sign-normalizes the system.
  void sign_normalize();

  //! \name Accessors
  //@{
  //! Returns the system topology.
  Topology topology() const;

  //! Returns the value of the sortedness flag.
  bool is_sorted() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    the system topology is <CODE>NECESSARILY_CLOSED</CODE>.
  */
  bool is_necessarily_closed() const;

  /*! \brief
    Returns the number of rows in the system
    that represent either lines or equalities.
  */
  dimension_type num_lines_or_equalities() const;

  //! Returns the index of the first pending row.
  dimension_type first_pending_row() const;

  //! Returns the number of rows that are in the pending part of the system.
  dimension_type num_pending_rows() const;
  //@} // Accessors

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is sorted,
    without checking for duplicates.
  */
  bool check_sorted() const;

  //! Sets the system topology to \p t .
  void set_topology(Topology t);

  //! Sets the system topology to <CODE>NECESSARILY_CLOSED</CODE>.
  void set_necessarily_closed();

  //! Sets the system topology to <CODE>NOT_NECESSARILY_CLOSED</CODE>.
  void set_not_necessarily_closed();

  // TODO: Consider removing this, or making it private.
  //! Marks the epsilon dimension as a standard dimension.
  /*!
    The system topology is changed to <CODE>NOT_NECESSARILY_CLOSED</CODE>, and
    the number of space dimensions is increased by 1.
  */
  void mark_as_necessarily_closed();

  // TODO: Consider removing this, or making it private.
  //! Marks the last dimension as the epsilon dimension.
  /*!
    The system topology is changed to <CODE>NECESSARILY_CLOSED</CODE>, and
    the number of space dimensions is decreased by 1.
  */
  void mark_as_not_necessarily_closed();

  //! Sets the index to indicate that the system has no pending rows.
  void unset_pending_rows();

  //! Sets the index of the first pending row to \p i.
  void set_index_first_pending_row(dimension_type i);

  //! Sets the sortedness flag of the system to \p b.
  void set_sorted(bool b);

  //! Adds \p n rows and space dimensions to the system.
  /*!
    \param n
    The number of rows and space dimensions to be added: must be strictly
    positive.

    Turns the system \f$M \in \Rset^r \times \Rset^c\f$ into
    the system \f$N \in \Rset^{r+n} \times \Rset^{c+n}\f$
    such that
    \f$N = \bigl(\genfrac{}{}{0pt}{}{0}{M}\genfrac{}{}{0pt}{}{J}{o}\bigr)\f$,
    where \f$J\f$ is the specular image
    of the \f$n \times n\f$ identity matrix.
  */
  void add_universe_rows_and_space_dimensions(dimension_type n);

  /*! \brief
    Adds a copy of \p r to the system,
    automatically resizing the system or the row's copy, if needed.
  */
  void insert(const Row& r);

  /*! \brief
    Adds a copy of the given row to the pending part of the system,
    automatically resizing the system or the row, if needed.
  */
  void insert_pending(const Row& r);

  /*! \brief
    Adds \p r to the system, stealing its contents and
    automatically resizing the system or the row, if needed.
  */
  void insert(Row& r, Recycle_Input);

  /*! \brief
    Adds the given row to the pending part of the system, stealing its
    contents and automatically resizing the system or the row, if needed.
  */
  void insert_pending(Row& r, Recycle_Input);

  //! Adds to \p *this a copy of  the rows of \p y.
  /*!
    It is assumed that \p *this has no pending rows.
  */
  void insert(const Linear_System& y);

  //! Adds a copy of the rows of `y' to the pending part of `*this'.
  void insert_pending(const Linear_System& r);

  //! Adds to \p *this a the rows of `y', stealing them from `y'.
  /*!
    It is assumed that \p *this has no pending rows.
  */
  void insert(Linear_System& r, Recycle_Input);

  //! Adds the rows of `y' to the pending part of `*this', stealing them from
  //! `y'.
  void insert_pending(Linear_System& r, Recycle_Input);

  /*! \brief
    Sorts the non-pending rows (in growing order) and eliminates
    duplicated ones.
  */
  void sort_rows();

  /*! \brief
    Sorts the rows (in growing order) form \p first_row to
    \p last_row and eliminates duplicated ones.
  */
  void sort_rows(dimension_type first_row, dimension_type last_row);

  /*! \brief
    Assigns to \p *this the result of merging its rows with
    those of \p y, obtaining a sorted system.

    Duplicated rows will occur only once in the result.
    On entry, both systems are assumed to be sorted and have
    no pending rows.
  */
  void merge_rows_assign(const Linear_System& y);

  /*! \brief
    Sorts the pending rows and eliminates those that also occur
    in the non-pending part of the system.
  */
  void sort_pending_and_remove_duplicates();

  /*! \brief
    Sorts the system, removing duplicates, keeping the saturation
    matrix consistent.

    \param sat
    Bit matrix with rows corresponding to the rows of \p *this.
  */
  void sort_and_remove_with_sat(Bit_Matrix& sat);

  //! Minimizes the subsystem of equations contained in \p *this.
  /*!
    This method works only on the equalities of the system:
    the system is required to be partially sorted, so that
    all the equalities are grouped at its top; it is assumed that
    the number of equalities is exactly \p n_lines_or_equalities.
    The method finds a minimal system for the equalities and
    returns its rank, i.e., the number of linearly independent equalities.
    The result is an upper triangular subsystem of equalities:
    for each equality, the pivot is chosen starting from
    the right-most space dimensions.
  */
  dimension_type gauss(dimension_type n_lines_or_equalities);

  /*! \brief
    Back-substitutes the coefficients to reduce
    the complexity of the system.

    Takes an upper triangular system having \p n_lines_or_equalities rows.
    For each row, starting from the one having the minimum number of
    coefficients different from zero, computes the expression of an element
    as a function of the remaining ones and then substitutes this expression
    in all the other rows.
  */
  void back_substitute(dimension_type n_lines_or_equalities);

  /*! \brief
    Applies Gaussian elimination and back-substitution so as to
    simplify the linear system.
  */
  void simplify();

  //! Clears the system deallocating all its rows.
  void clear();

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.

    Reads into a Linear_System object the information produced by the
    output of ascii_dump(std::ostream&) const.  The specialized methods
    provided by Constraint_System and Generator_System take care of
    properly reading the contents of the system.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! The vector that contains the rows.
  /*!
    \note This is public for convenience. Clients that modify if must preserve
          the class invariant.
  */
  Swapping_Vector<Row> rows;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

private:
  //! Makes the system shrink by removing its i-th row.
  /*!
    When \p keep_sorted is \p true and the system is sorted, sortedness will
    be preserved, but this method costs O(n).

    Otherwise, this method just swaps the i-th row with the last and then
    removes it, so it costs O(1).

    This method is for internal use, it does *not* assert OK() at the end,
    so it can be used for invalid systems.
  */
  void remove_row_no_ok(dimension_type i, bool keep_sorted = false);

  /*! \brief
    Adds \p r to the pending part of the system, stealing its contents and
    automatically resizing the system or the row, if needed.

    This method is for internal use, it does *not* assert OK() at the end,
    so it can be used for invalid systems.
  */
  void insert_pending_no_ok(Row& r, Recycle_Input);

  /*! \brief
    Adds \p r to the system, stealing its contents and
    automatically resizing the system or the row, if needed.

    This method is for internal use, it does *not* assert OK() at the end,
    so it can be used for invalid systems.
  */
  void insert_no_ok(Row& r, Recycle_Input);

  //! Sets the space dimension of the rows in the system to \p space_dim .
  /*!
    This method is for internal use, it does *not* assert OK() at the end,
    so it can be used for invalid systems.
  */
  void set_space_dimension_no_ok(dimension_type space_dim);

  //! Swaps the [first,last) row interval with the
  //! [first + offset, last + offset) interval.
  /*!
    These intervals may not be disjunct.

    Sorting of these intervals is *not* preserved.

    Either both intervals contain only not-pending rows, or they both
    contain pending rows.
  */
  void swap_row_intervals(dimension_type first, dimension_type last,
                          dimension_type offset);

  //! The space dimension of each row. All rows must have this number of
  //! space dimensions.
  dimension_type space_dimension_;

  //! The topological kind of the rows in the system. All rows must have this
  //! topology.
  Topology row_topology;

  //! The index of the first pending row.
  dimension_type index_first_pending;

  /*! \brief
    <CODE>true</CODE> if rows are sorted in the ascending order as defined by
    <CODE>bool compare(const Row&, const Row&)</CODE>.
    If <CODE>false</CODE> may not be sorted.
  */
  bool sorted;

  Representation representation_;

  //! Ordering predicate (used when implementing the sort algorithm).
  struct Row_Less_Than {
    bool operator()(const Row& x, const Row& y) const;
  };

  //! Comparison predicate (used when implementing the unique algorithm).
  struct Unique_Compare {
    Unique_Compare(const Swapping_Vector<Row>& cont,
                   dimension_type base = 0);

    bool operator()(dimension_type i, dimension_type j) const;

    const Swapping_Vector<Row>& container;
    const dimension_type base_index;
  };

  friend class Polyhedron;
  friend class Generator_System;
};

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
/*! \relates Linear_System */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Row>
void swap(Parma_Polyhedra_Library::Linear_System<Row>& x,
          Parma_Polyhedra_Library::Linear_System<Row>& y);

} // namespace std

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are identical.
/*! \relates Linear_System */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Row>
bool operator==(const Linear_System<Row>& x, const Linear_System<Row>& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are different.
/*! \relates Linear_System */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Row>
bool operator!=(const Linear_System<Row>& x, const Linear_System<Row>& y);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_System_inlines.hh line 1. */
/* Linear_System class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Bit_Row_defs.hh line 1. */
/* Bit_Row class declaration.
*/


/* Automatically generated from PPL source file ../src/Bit_Row_defs.hh line 29. */
#include <iosfwd>
#include <gmpxx.h>
#include <vector>

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
/*! \relates Bit_Row */
void swap(Bit_Row& x, Bit_Row& y);
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps objects referred by \p x and \p y.
/*! \relates Bit_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void
iter_swap(std::vector<Bit_Row>::iterator x,
          std::vector<Bit_Row>::iterator y);

// Put them in the namespace here to declare them friends later.

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are equal.
/*! \relates Bit_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator==(const Bit_Row& x, const Bit_Row& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are not equal.
/*! \relates Bit_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator!=(const Bit_Row& x, const Bit_Row& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! The basic comparison function.
/*! \relates Bit_Row
  Compares \p x with \p y starting from the least significant bits.
  The ordering is total and has the following property: if \p x and \p y
  are two rows seen as sets of naturals, if \p x is a strict subset
  of \p y, then \p x comes before \p y.

  Returns
  - -1 if \p x comes before \p y in the ordering;
  -  0 if \p x and \p y are equal;
  -  1 if \p x comes after \p y in the ordering.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
int compare(const Bit_Row& x, const Bit_Row& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Set-theoretic inclusion test.
/*! \relates Bit_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool subset_or_equal(const Bit_Row& x, const Bit_Row& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Set-theoretic inclusion test: sets \p strict_subset to a Boolean
  indicating whether the inclusion is strict or not.

  \relates Bit_Row
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool subset_or_equal(const Bit_Row& x, const Bit_Row& y,
                     bool& strict_subset);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Set-theoretic strict inclusion test.
/*! \relates Bit_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool strict_subset(const Bit_Row& x, const Bit_Row& y);

} // namespace Parma_Polyhedra_Library

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A row in a matrix of bits.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
class Parma_Polyhedra_Library::Bit_Row {
public:
  //! Default constructor.
  Bit_Row();

  //! Copy constructor.
  Bit_Row(const Bit_Row& y);

  //! Set-union constructor.
  /*!
    Constructs an object containing the set-union of \p y and \p z.
  */
  Bit_Row(const Bit_Row& y, const Bit_Row& z);

  //! Destructor.
  ~Bit_Row();

  //! Assignment operator.
  Bit_Row& operator=(const Bit_Row& y);

  //! Swaps \p *this with \p y.
  void m_swap(Bit_Row& y);

  //! Returns the truth value corresponding to the bit in position \p k.
  bool operator[](unsigned long k) const;

  //! Sets the bit in position \p k.
  void set(unsigned long k);

  //! Sets bits up to position \p k (excluded).
  void set_until(unsigned long k);

  //! Clears the bit in position \p k.
  void clear(unsigned long k);

  //! Clears bits from position \p k (included) onward.
  void clear_from(unsigned long k);

  //! Clears all the bits of the row.
  void clear();

  //! Assigns to \p *this the set-theoretic union of \p x and \p y.
  void union_assign(const Bit_Row& x, const Bit_Row& y);

  //! Assigns to \p *this the set-theoretic intersection of \p x and \p y.
  void intersection_assign(const Bit_Row& x, const Bit_Row& y);

  //! Assigns to \p *this the set-theoretic difference of \p x and \p y.
  void difference_assign(const Bit_Row& x, const Bit_Row& y);


  friend int compare(const Bit_Row& x, const Bit_Row& y);
  friend bool operator==(const Bit_Row& x, const Bit_Row& y);
  friend bool operator!=(const Bit_Row& x, const Bit_Row& y);
  friend bool subset_or_equal(const Bit_Row& x, const Bit_Row& y);
  friend bool subset_or_equal(const Bit_Row& x, const Bit_Row& y,
                              bool& strict_subset);
  friend bool strict_subset(const Bit_Row& x, const Bit_Row& y);

  //! Returns the index of the first set bit or ULONG_MAX if no bit is set.
  unsigned long first() const;

  /*! \brief
    Returns the index of the first set bit after \p position
    or ULONG_MAX if no bit after \p position is set.
  */
  unsigned long next(unsigned long position) const;

  //! Returns the index of the last set bit or ULONG_MAX if no bit is set.
  unsigned long last() const;

  /*! \brief
    Returns the index of the first set bit before \p position
    or ULONG_MAX if no bits before \p position is set.
  */
  unsigned long prev(unsigned long position) const;

  //! Returns the number of set bits in the row.
  unsigned long count_ones() const;

  //! Returns <CODE>true</CODE> if no bit is set in the row.
  bool empty() const;

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Checks if all the invariants are satisfied
  bool OK() const;

private:
  //! Bit-vector representing the row.
  mpz_t vec;

  //! Assigns to \p *this the union of \p y and \p z.
  /*!
    The size of \p y must be be less than or equal to the size of \p z.
    Upon entry, \p vec must have allocated enough space to contain the result.
  */
  void union_helper(const Bit_Row& y, const Bit_Row& z);
};

/* Automatically generated from PPL source file ../src/Bit_Row_inlines.hh line 1. */
/* Bit_Row class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Bit_Row_inlines.hh line 30. */

// For the declaration of ffs(3).
#if defined(PPL_HAVE_STRINGS_H)
# include <strings.h>
#elif defined(PPL_HAVE_STRING_H)
# include <string.h>
#endif

#define PPL_BITS_PER_GMP_LIMB sizeof_to_bits(PPL_SIZEOF_MP_LIMB_T)

namespace Parma_Polyhedra_Library {

inline
Bit_Row::Bit_Row() {
  mpz_init(vec);
}

inline
Bit_Row::Bit_Row(const Bit_Row& y) {
  mpz_init_set(vec, y.vec);
}

inline
Bit_Row::Bit_Row(const Bit_Row& y, const Bit_Row& z) {
  const mp_size_t y_size = y.vec->_mp_size;
  PPL_ASSERT(y_size >= 0);
  const mp_size_t z_size = z.vec->_mp_size;
  PPL_ASSERT(z_size >= 0);
  if (y_size < z_size) {
    PPL_ASSERT(static_cast<unsigned long>(z_size)
               <= C_Integer<unsigned long>::max / PPL_BITS_PER_GMP_LIMB);
    mpz_init2(vec, static_cast<unsigned long>(z_size) * PPL_BITS_PER_GMP_LIMB);
    union_helper(y, z);
  }
  else {
    PPL_ASSERT(static_cast<unsigned long>(y_size)
               <= C_Integer<unsigned long>::max / PPL_BITS_PER_GMP_LIMB);
    mpz_init2(vec, static_cast<unsigned long>(y_size) * PPL_BITS_PER_GMP_LIMB);
    union_helper(z, y);
  }
}

inline
Bit_Row::~Bit_Row() {
  mpz_clear(vec);
}

inline Bit_Row&
Bit_Row::operator=(const Bit_Row& y) {
  mpz_set(vec, y.vec);
  return *this;
}

inline void
Bit_Row::set(const unsigned long k) {
  mpz_setbit(vec, k);
}

inline void
Bit_Row::clear(const unsigned long k) {
  mpz_clrbit(vec, k);
}

inline void
Bit_Row::clear_from(const unsigned long k) {
  mpz_tdiv_r_2exp(vec, vec, k);
}

inline unsigned long
Bit_Row::count_ones() const {
  const mp_size_t x_size = vec->_mp_size;
  PPL_ASSERT(x_size >= 0);
  return (x_size == 0) ? 0 : mpn_popcount(vec->_mp_d, x_size);
}

inline bool
Bit_Row::empty() const {
  return mpz_sgn(vec) == 0;
}

inline void
Bit_Row::m_swap(Bit_Row& y) {
  mpz_swap(vec, y.vec);
}

inline void
Bit_Row::clear() {
  mpz_set_ui(vec, 0UL);
}

inline memory_size_type
Bit_Row::external_memory_in_bytes() const {
  return static_cast<memory_size_type>(vec[0]._mp_alloc) * PPL_SIZEOF_MP_LIMB_T;
}

inline memory_size_type
Bit_Row::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

inline void
Bit_Row::union_assign(const Bit_Row& x, const Bit_Row& y) {
  const mp_size_t x_size = x.vec->_mp_size;
  PPL_ASSERT(x_size >= 0);
  const mp_size_t y_size = y.vec->_mp_size;
  PPL_ASSERT(y_size >= 0);
  if (x_size < y_size) {
    PPL_ASSERT(static_cast<unsigned long>(y_size)
               <= C_Integer<unsigned long>::max / PPL_BITS_PER_GMP_LIMB);
    mpz_realloc2(vec, static_cast<unsigned long>(y_size) * PPL_BITS_PER_GMP_LIMB);
    union_helper(x, y);
  }
  else {
    PPL_ASSERT(static_cast<unsigned long>(x_size)
               <= C_Integer<unsigned long>::max / PPL_BITS_PER_GMP_LIMB);
    mpz_realloc2(vec, static_cast<unsigned long>(x_size) * PPL_BITS_PER_GMP_LIMB);
    union_helper(y, x);
  }
}

inline void
Bit_Row::intersection_assign(const Bit_Row& x, const Bit_Row& y) {
  mpz_and(vec, x.vec, y.vec);
}

inline void
Bit_Row::difference_assign(const Bit_Row& x, const Bit_Row& y) {
  PPL_DIRTY_TEMP(mpz_class, complement_y);
  mpz_com(complement_y.get_mpz_t(), y.vec);
  mpz_and(vec, x.vec, complement_y.get_mpz_t());
}

namespace Implementation {

/*! \brief
  Assuming \p u is nonzero, returns the index of the first set bit in \p u.
*/
inline unsigned int
first_one(unsigned int u) {
  return ctz(u);
}

/*! \brief
  Assuming \p ul is nonzero, returns the index of the first set bit in
  \p ul.
*/
inline unsigned int
first_one(unsigned long ul) {
  return ctz(ul);
}

/*! \brief
  Assuming \p ull is nonzero, returns the index of the first set bit in
  \p ull.
*/
inline unsigned int
first_one(unsigned long long ull) {
  return ctz(ull);
}

/*! \brief
  Assuming \p u is nonzero, returns the index of the last set bit in \p u.
*/
inline unsigned int
last_one(unsigned int u) {
  return static_cast<unsigned int>(sizeof_to_bits(sizeof(u)))
    - 1U - clz(u);
}

/*! \brief
  Assuming \p ul is nonzero, returns the index of the last set bit in
  \p ul.
*/
inline unsigned int
last_one(unsigned long ul) {
  return static_cast<unsigned int>(sizeof_to_bits(sizeof(ul)))
    - 1U - clz(ul);
}

/*! \brief
  Assuming \p ull is nonzero, returns the index of the last set bit in
  \p ull.
*/
inline unsigned int
last_one(unsigned long long ull) {
  return static_cast<unsigned int>(sizeof_to_bits(sizeof(ull)))
    - 1U - clz(ull);
}

} // namespace Implementation

/*! \relates Bit_Row */
inline void
swap(Bit_Row& x, Bit_Row& y) {
  x.m_swap(y);
}

/*! \relates Bit_Row */
inline void
iter_swap(std::vector<Bit_Row>::iterator x,
          std::vector<Bit_Row>::iterator y) {
  swap(*x, *y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Bit_Row_defs.hh line 213. */

/* Automatically generated from PPL source file ../src/Linear_System_inlines.hh line 29. */

#include <algorithm>

namespace Parma_Polyhedra_Library {

template <typename Row>
inline memory_size_type
Linear_System<Row>::external_memory_in_bytes() const {
  return rows.external_memory_in_bytes();
}

template <typename Row>
inline memory_size_type
Linear_System<Row>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename Row>
inline bool
Linear_System<Row>::is_sorted() const {
  // The flag `sorted' does not really reflect the sortedness status
  // of a system (if `sorted' evaluates to `false' nothing is known).
  // This assertion is used to ensure that the system
  // is actually sorted when `sorted' value is 'true'.
  PPL_ASSERT(!sorted || check_sorted());
  return sorted;
}

template <typename Row>
inline void
Linear_System<Row>::set_sorted(const bool b) {
  sorted = b;
  PPL_ASSERT(OK());
}

template <typename Row>
inline
Linear_System<Row>::Linear_System(Topology topol, Representation r)
  : rows(),
    space_dimension_(0),
    row_topology(topol),
    index_first_pending(0),
    sorted(true),
    representation_(r) {

  PPL_ASSERT(OK());
}

template <typename Row>
inline
Linear_System<Row>::Linear_System(Topology topol,
                                  dimension_type space_dim,
                                  Representation r)
  : rows(),
    space_dimension_(0),
    row_topology(topol),
    index_first_pending(0),
    sorted(true),
    representation_(r) {
  set_space_dimension(space_dim);
  PPL_ASSERT(OK());
}

template <typename Row>
inline dimension_type
Linear_System<Row>::first_pending_row() const {
  return index_first_pending;
}

template <typename Row>
inline dimension_type
Linear_System<Row>::num_pending_rows() const {
  PPL_ASSERT(num_rows() >= first_pending_row());
  return num_rows() - first_pending_row();
}

template <typename Row>
inline void
Linear_System<Row>::unset_pending_rows() {
  index_first_pending = num_rows();
  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Linear_System<Row>::set_index_first_pending_row(const dimension_type i) {
  index_first_pending = i;
  PPL_ASSERT(OK());
}

template <typename Row>
inline
Linear_System<Row>::Linear_System(const Linear_System& y)
  : rows(y.rows),
    space_dimension_(y.space_dimension_),
    row_topology(y.row_topology),
    representation_(y.representation_) {
  // Previously pending rows may violate sortedness.
  sorted = (y.num_pending_rows() > 0) ? false : y.sorted;
  unset_pending_rows();
  PPL_ASSERT(OK());
}

template <typename Row>
inline
Linear_System<Row>::Linear_System(const Linear_System& y, Representation r)
  : rows(),
    space_dimension_(y.space_dimension_),
    row_topology(y.row_topology),
    representation_(r) {
  rows.resize(y.num_rows());
  for (dimension_type i = 0; i < y.num_rows(); ++i) {
    // Create the copies with the right representation.
    Row row(y.rows[i], r);
    swap(rows[i], row);
  }
  // Previously pending rows may violate sortedness.
  sorted = (y.num_pending_rows() > 0) ? false : y.sorted;
  unset_pending_rows();
  PPL_ASSERT(OK());
}

template <typename Row>
inline
Linear_System<Row>::Linear_System(const Linear_System& y, With_Pending)
  : rows(y.rows),
    space_dimension_(y.space_dimension_),
    row_topology(y.row_topology),
    index_first_pending(y.index_first_pending),
    sorted(y.sorted),
    representation_(y.representation_) {
  PPL_ASSERT(OK());
}

template <typename Row>
inline
Linear_System<Row>::Linear_System(const Linear_System& y, Representation r,
                                  With_Pending)
  : rows(),
    space_dimension_(y.space_dimension_),
    row_topology(y.row_topology),
    index_first_pending(y.index_first_pending),
    sorted(y.sorted),
    representation_(r) {
  rows.resize(y.num_rows());
  for (dimension_type i = 0; i < y.num_rows(); ++i) {
    // Create the copies with the right representation.
    Row row(y.rows[i], r);
    swap(rows[i], row);
  }
  PPL_ASSERT(OK());
}

template <typename Row>
inline Linear_System<Row>&
Linear_System<Row>::operator=(const Linear_System& y) {
  // NOTE: Pending rows are transformed into non-pending ones.
  Linear_System<Row> tmp = y;
  swap(*this, tmp);
  return *this;
}

template <typename Row>
inline void
Linear_System<Row>::assign_with_pending(const Linear_System& y) {
  Linear_System<Row> tmp(y, With_Pending());
  swap(*this, tmp);
}

template <typename Row>
inline void
Linear_System<Row>::m_swap(Linear_System& y) {
  using std::swap;
  swap(rows, y.rows);
  swap(space_dimension_, y.space_dimension_);
  swap(row_topology, y.row_topology);
  swap(index_first_pending, y.index_first_pending);
  swap(sorted, y.sorted);
  swap(representation_, y.representation_);
  PPL_ASSERT(OK());
  PPL_ASSERT(y.OK());
}

template <typename Row>
inline void
Linear_System<Row>::clear() {
  // Note: do NOT modify the value of `row_topology' and `representation'.
  rows.clear();
  index_first_pending = 0;
  sorted = true;
  space_dimension_ = 0;

  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Linear_System<Row>::mark_as_necessarily_closed() {
  PPL_ASSERT(topology() == NOT_NECESSARILY_CLOSED);
  row_topology = NECESSARILY_CLOSED;
  ++space_dimension_;
  for (dimension_type i = num_rows(); i-- > 0; ) {
    rows[i].mark_as_necessarily_closed();
  }
}

template <typename Row>
inline void
Linear_System<Row>::mark_as_not_necessarily_closed() {
  PPL_ASSERT(topology() == NECESSARILY_CLOSED);
  PPL_ASSERT(space_dimension() > 0);
  row_topology = NOT_NECESSARILY_CLOSED;
  --space_dimension_;
  for (dimension_type i = num_rows(); i-- > 0; ) {
    rows[i].mark_as_not_necessarily_closed();
  }
}

template <typename Row>
inline void
Linear_System<Row>::set_topology(Topology t) {
  if (topology() == t) {
    return;
  }
  for (dimension_type i = num_rows(); i-- > 0; ) {
    rows[i].set_topology(t);
  }
  row_topology = t;
  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Linear_System<Row>::set_necessarily_closed() {
  set_topology(NECESSARILY_CLOSED);
}

template <typename Row>
inline void
Linear_System<Row>::set_not_necessarily_closed() {
  set_topology(NOT_NECESSARILY_CLOSED);
}

template <typename Row>
inline bool
Linear_System<Row>::is_necessarily_closed() const {
  return row_topology == NECESSARILY_CLOSED;
}

template <typename Row>
inline const Row&
Linear_System<Row>::operator[](const dimension_type k) const {
  return rows[k];
}

template <typename Row>
inline typename Linear_System<Row>::iterator
Linear_System<Row>::begin() {
  return rows.begin();
}

template <typename Row>
inline typename Linear_System<Row>::iterator
Linear_System<Row>::end() {
  return rows.end();
}

template <typename Row>
inline typename Linear_System<Row>::const_iterator
Linear_System<Row>::begin() const {
  return rows.begin();
}

template <typename Row>
inline typename Linear_System<Row>::const_iterator
Linear_System<Row>::end() const {
  return rows.end();
}

template <typename Row>
inline bool
Linear_System<Row>::has_no_rows() const {
  return rows.empty();
}

template <typename Row>
inline dimension_type
Linear_System<Row>::num_rows() const {
  return rows.size();
}

template <typename Row>
inline Topology
Linear_System<Row>::topology() const {
  return row_topology;
}

template <typename Row>
inline Representation
Linear_System<Row>::representation() const {
  return representation_;
}

template <typename Row>
inline void
Linear_System<Row>::set_representation(Representation r) {
  representation_ = r;
  for (dimension_type i = 0; i < rows.size(); ++i) {
    rows[i].set_representation(r);
  }
  PPL_ASSERT(OK());
}

template <typename Row>
inline dimension_type
Linear_System<Row>::max_space_dimension() {
  return Row::max_space_dimension();
}

template <typename Row>
inline dimension_type
Linear_System<Row>::space_dimension() const {
  return space_dimension_;
}

template <typename Row>
inline void
Linear_System<Row>::set_space_dimension_no_ok(dimension_type space_dim) {
  for (dimension_type i = rows.size(); i-- > 0; ) {
    rows[i].set_space_dimension_no_ok(space_dim);
  }
  space_dimension_ = space_dim;
}

template <typename Row>
inline void
Linear_System<Row>::set_space_dimension(dimension_type space_dim) {
  set_space_dimension_no_ok(space_dim);
  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Linear_System<Row>::remove_row_no_ok(const dimension_type i,
                                     const bool keep_sorted) {
  PPL_ASSERT(i < num_rows());
  const bool was_pending = (i >= index_first_pending);

  if (sorted && keep_sorted && !was_pending) {
    for (dimension_type j = i + 1; j < rows.size(); ++j) {
      swap(rows[j], rows[j-1]);
    }
    rows.pop_back();
  }
  else {
    if (!was_pending) {
      sorted = false;
    }
    const bool last_row_is_pending = (num_rows() - 1 >= index_first_pending);
    if (was_pending == last_row_is_pending) {
      // Either both rows are pending or both rows are not pending.
      swap(rows[i], rows.back());
    }
    else {
      // Pending rows are stored after the non-pending ones.
      PPL_ASSERT(!was_pending);
      PPL_ASSERT(last_row_is_pending);

      // Swap the row with the last non-pending row.
      swap(rows[i], rows[index_first_pending - 1]);

      // Now the (non-pending) row that has to be deleted is between the
      // non-pending and the pending rows.
      swap(rows[i], rows.back());
    }
    rows.pop_back();
  }
  if (!was_pending) {
    // A non-pending row has been removed.
    --index_first_pending;
  }
}

template <typename Row>
inline void
Linear_System<Row>::remove_row(const dimension_type i, bool keep_sorted) {
  remove_row_no_ok(i, keep_sorted);
  PPL_ASSERT(OK());
}


template <typename Row>
inline void
Linear_System<Row>::remove_rows(dimension_type first,
                                dimension_type last,
                                bool keep_sorted) {
  PPL_ASSERT(first <= last);
  PPL_ASSERT(last <= num_rows());
  const dimension_type n = last - first;

  if (n == 0) {
    return;
  }

  // All the rows that have to be removed must have the same (pending or
  // non-pending) status.
  PPL_ASSERT(first >= index_first_pending || last <= index_first_pending);

  const bool were_pending = (first >= index_first_pending);

  // Move the rows in [first,last) at the end of the system.
  if (sorted && keep_sorted && !were_pending) {
    // Preserve the row ordering.
    for (dimension_type i = last; i < rows.size(); ++i) {
      swap(rows[i], rows[i - n]);
    }

    rows.resize(rows.size() - n);

    // `n' non-pending rows have been removed.
    index_first_pending -= n;

    PPL_ASSERT(OK());
    return;
  }

  // We can ignore the row ordering, but we must not mix pending and
  // non-pending rows.

  const dimension_type offset = rows.size() - n - first;
  // We want to swap the rows in [first, last) and
  // [first + offset, last + offset) (note that these intervals may not be
  // disjunct).

  if (index_first_pending == num_rows()) {
    // There are no pending rows.
    PPL_ASSERT(!were_pending);

    swap_row_intervals(first, last, offset);

    rows.resize(rows.size() - n);

    // `n' non-pending rows have been removed.
    index_first_pending -= n;
  }
  else {
    // There are some pending rows in [first + offset, last + offset).
    if (were_pending) {
      // Both intervals contain only pending rows, because the second
      // interval is after the first.

      swap_row_intervals(first, last, offset);

      rows.resize(rows.size() - n);

      // `n' non-pending rows have been removed.
      index_first_pending -= n;
    }
    else {
      PPL_ASSERT(rows.size() - n < index_first_pending);
      PPL_ASSERT(rows.size() > index_first_pending);
      PPL_ASSERT(!were_pending);
      // In the [size() - n, size()) interval there are some non-pending
      // rows and some pending ones. Be careful not to mix them.

      PPL_ASSERT(index_first_pending >= last);
      swap_row_intervals(first, last, index_first_pending - last);

      // Mark the rows that must be deleted as pending.
      index_first_pending -= n;
      first = index_first_pending;
      last = first + n;

      // Move them at the end of the system.
      swap_row_intervals(first, last, num_rows() - last);

      // Actually remove the rows.
      rows.resize(rows.size() - n);
    }
  }

  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Linear_System<Row>::swap_row_intervals(dimension_type first,
                                       dimension_type last,
                                       dimension_type offset) {
  PPL_ASSERT(first <= last);
  PPL_ASSERT(last + offset <= num_rows());
#ifndef NDEBUG
  if (first < last) {
    bool first_interval_has_pending_rows = (last > index_first_pending);
    bool second_interval_has_pending_rows = (last + offset > index_first_pending);
    bool first_interval_has_not_pending_rows = (first < index_first_pending);
    bool second_interval_has_not_pending_rows = (first + offset < index_first_pending);
    PPL_ASSERT(first_interval_has_not_pending_rows
               == !first_interval_has_pending_rows);
    PPL_ASSERT(second_interval_has_not_pending_rows
               == !second_interval_has_pending_rows);
    PPL_ASSERT(first_interval_has_pending_rows
               == second_interval_has_pending_rows);
  }
#endif
  if (first + offset < last) {
    // The intervals are not disjunct, make them so.
    const dimension_type k = last - first - offset;
    last -= k;
    offset += k;
  }

  if (first == last) {
    // Nothing to do.
    return;
  }

  for (dimension_type i = first; i < last; ++i) {
    swap(rows[i], rows[i + offset]);
  }

  if (first < index_first_pending) {
    // The swaps involved not pending rows, so they may not be sorted anymore.
    set_sorted(false);
  }

  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Linear_System<Row>::remove_rows(const std::vector<dimension_type>& indexes) {
#ifndef NDEBUG
  {
    // Check that `indexes' is sorted.
    std::vector<dimension_type> sorted_indexes = indexes;
    std::sort(sorted_indexes.begin(), sorted_indexes.end());
    PPL_ASSERT(indexes == sorted_indexes);

    // Check that the last index (if any) is lower than num_rows().
    // This guarantees that all indexes are in [0, num_rows()).
    if (!indexes.empty()) {
      PPL_ASSERT(indexes.back() < num_rows());
    }
  }
#endif

  if (indexes.empty()) {
    return;
  }

  const dimension_type rows_size = rows.size();
  typedef std::vector<dimension_type>::const_iterator itr_t;

  // `i' and last_unused_row' start with the value `indexes[0]' instead
  // of `0', because the loop would just increment `last_unused_row' in the
  // preceding iterations.
  dimension_type last_unused_row = indexes[0];
  dimension_type i = indexes[0];
  itr_t itr = indexes.begin();
  itr_t itr_end = indexes.end();
  while (itr != itr_end) {
    // i <= *itr < rows_size
    PPL_ASSERT(i < rows_size);
    if (*itr == i) {
      // The current row has to be removed, don't increment last_unused_row.
      ++itr;
    }
    else {
      // The current row must not be removed, swap it after the last used row.
      swap(rows[last_unused_row], rows[i]);
      ++last_unused_row;
    }
    ++i;
  }

  // Move up the remaining rows, if any.
  for ( ; i < rows_size; ++i) {
    swap(rows[last_unused_row], rows[i]);
    ++last_unused_row;
  }

  PPL_ASSERT(last_unused_row == num_rows() - indexes.size());

  // The rows that have to be removed are now at the end of the system, just
  // remove them.
  rows.resize(last_unused_row);

  // Adjust index_first_pending.
  if (indexes[0] >= index_first_pending) {
    // Removing pending rows only.
  }
  else {
    if (indexes.back() < index_first_pending) {
      // Removing non-pending rows only.
      index_first_pending -= indexes.size();
    }
    else {
      // Removing some pending and some non-pending rows, count the
      // non-pending rows that must be removed.
      // This exploits the fact that `indexes' is sorted by using binary
      // search.
      itr_t j = std::lower_bound(indexes.begin(), indexes.end(),
                                 index_first_pending);
      std::iterator_traits<itr_t>::difference_type
        non_pending = j - indexes.begin();
      index_first_pending -= static_cast<dimension_type>(non_pending);
    }
  }

  // NOTE: This method does *not* call set_sorted(false), because it preserves
  // the relative row ordering.

  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Linear_System<Row>::remove_trailing_rows(const dimension_type n) {
  PPL_ASSERT(rows.size() >= n);
  rows.resize(rows.size() - n);
  if (first_pending_row() > rows.size()) {
    index_first_pending = rows.size();
  }
  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Linear_System<Row>
::permute_space_dimensions(const std::vector<Variable>& cycle) {
  for (dimension_type i = num_rows(); i-- > 0; ) {
    rows[i].permute_space_dimensions(cycle);
  }
  sorted = false;
  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Linear_System<Row>
::swap_space_dimensions(Variable v1, Variable v2) {
  PPL_ASSERT(v1.space_dimension() <= space_dimension());
  PPL_ASSERT(v2.space_dimension() <= space_dimension());
  for (dimension_type k = num_rows(); k-- > 0; ) {
    rows[k].swap_space_dimensions(v1, v2);
  }
  sorted = false;
  PPL_ASSERT(OK());
}

/*! \relates Linear_System */
template <typename Row>
inline bool
operator!=(const Linear_System<Row>& x, const Linear_System<Row>& y) {
  return !(x == y);
}

template <typename Row>
inline bool
Linear_System<Row>::Row_Less_Than::operator()(const Row& x,
                                              const Row& y) const {
  return compare(x, y) < 0;
}

template <typename Row>
inline
Linear_System<Row>::Unique_Compare
::Unique_Compare(const Swapping_Vector<Row>& cont,
                 dimension_type base)
  : container(cont), base_index(base) {
}

template <typename Row>
inline bool
Linear_System<Row>::Unique_Compare
::operator()(dimension_type i, dimension_type j) const {
  return container[base_index + i].is_equal_to(container[base_index + j]);
}

/*! \relates Linear_System */
template <typename Row>
inline void
swap(Linear_System<Row>& x, Linear_System<Row>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_System_templates.hh line 1. */
/* Linear_System class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/Bit_Matrix_defs.hh line 1. */
/* Bit_Matrix class declaration.
*/


/* Automatically generated from PPL source file ../src/Bit_Matrix_defs.hh line 30. */
#include <vector>
#include <iosfwd>

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
/*! \relates Bit_Matrix */
void swap(Bit_Matrix& x, Bit_Matrix& y);
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)

} // namespace Parma_Polyhedra_Library

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A matrix of bits.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
class Parma_Polyhedra_Library::Bit_Matrix {
public:
  //! Default constructor.
  Bit_Matrix();

  //! Construct a bit matrix with \p n_rows rows and \p n_columns columns.
  Bit_Matrix(dimension_type n_rows, dimension_type n_columns);

  //! Copy constructor.
  Bit_Matrix(const Bit_Matrix& y);

  //! Destructor.
  ~Bit_Matrix();

  //! Assignment operator.
  Bit_Matrix& operator=(const Bit_Matrix& y);

  //! Swaps \p *this with \p y.
  void m_swap(Bit_Matrix& y);

  //! Subscript operator.
  Bit_Row& operator[](dimension_type k);

  //! Constant subscript operator.
  const Bit_Row& operator[](dimension_type k) const;

  //! Clears the matrix deallocating all its rows.
  void clear();

  //! Transposes the matrix.
  void transpose();

  //! Makes \p *this a transposed copy of \p y.
  void transpose_assign(const Bit_Matrix& y);

  //! Returns the maximum number of rows of a Bit_Matrix.
  static dimension_type max_num_rows();

  //! Returns the number of columns of \p *this.
  dimension_type num_columns() const;

  //! Returns the number of rows of \p *this.
  dimension_type num_rows() const;

  //! Sorts the rows and removes duplicates.
  void sort_rows();

  //! Looks for \p row in \p *this, which is assumed to be sorted.
  /*!
    \return
    <CODE>true</CODE> if \p row belongs to \p *this, false otherwise.

    \param row
    The row that will be searched for in the matrix.

    Given a sorted bit matrix (this ensures better efficiency),
    tells whether it contains the given row.
  */
  bool sorted_contains(const Bit_Row& row) const;

  //! Adds \p row to \p *this.
  /*!
    \param row
    The row whose implementation will be recycled.

    The only thing that can be done with \p row upon return is destruction.
  */
  void add_recycled_row(Bit_Row& row);

  //! Removes the last \p n rows.
  void remove_trailing_rows(dimension_type n);

  //! Removes the last \p n columns.
  /*!
    The last \p n columns of the matrix are all made of zeros.
    If such an assumption is not met, the behavior is undefined.
  */
  void remove_trailing_columns(dimension_type n);

  //! Resizes the matrix copying the old contents.
  void resize(dimension_type new_n_rows, dimension_type new_n_columns);

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

#ifndef NDEBUG
  //! Checks whether \p *this is sorted. It does NOT check for duplicates.
  bool check_sorted() const;
#endif

private:
  //! Contains the rows of the matrix.
  std::vector<Bit_Row> rows;

  //! Size of the initialized part of each row.
  dimension_type row_size;

  //! Ordering predicate (used when implementing the sort algorithm).
  /*! \ingroup PPL_CXX_interface */
  struct Bit_Row_Less_Than {
    bool operator()(const Bit_Row& x, const Bit_Row& y) const;
  };

  template <typename Row>
  friend class Parma_Polyhedra_Library::Linear_System;
};

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are equal.
/*! \relates Bit_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator==(const Bit_Matrix& x, const Bit_Matrix& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are not equal.
/*! \relates Bit_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator!=(const Bit_Matrix& x, const Bit_Matrix& y);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Bit_Matrix_inlines.hh line 1. */
/* Bit_Matrix class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Bit_Matrix_inlines.hh line 28. */
#include <algorithm>

namespace Parma_Polyhedra_Library {

inline
Bit_Matrix::Bit_Matrix()
  : rows(),
    row_size(0) {
}

inline dimension_type
Bit_Matrix::max_num_rows() {
  return std::vector<Bit_Row>().max_size();
}

inline
Bit_Matrix::Bit_Matrix(const dimension_type n_rows,
                       const dimension_type n_columns)
  : rows(n_rows),
    row_size(n_columns) {
}

inline
Bit_Matrix::Bit_Matrix(const Bit_Matrix& y)
  : rows(y.rows),
    row_size(y.row_size) {
}

inline
Bit_Matrix::~Bit_Matrix() {
}

inline void
Bit_Matrix::remove_trailing_rows(const dimension_type n) {
  // The number of rows to be erased cannot be greater
  // than the actual number of the rows of the matrix.
  PPL_ASSERT(n <= rows.size());
  if (n != 0) {
    rows.resize(rows.size() - n);
  }
  PPL_ASSERT(OK());
}

inline void
Bit_Matrix::remove_trailing_columns(const dimension_type n) {
  // The number of columns to be erased cannot be greater
  // than the actual number of the columns of the matrix.
  PPL_ASSERT(n <= row_size);
  row_size -= n;
  PPL_ASSERT(OK());
}

inline void
Bit_Matrix::m_swap(Bit_Matrix& y) {
  std::swap(row_size, y.row_size);
  std::swap(rows, y.rows);
}

inline Bit_Row&
Bit_Matrix::operator[](const dimension_type k) {
  PPL_ASSERT(k < rows.size());
  return rows[k];
}

inline const Bit_Row&
Bit_Matrix::operator[](const dimension_type k) const {
  PPL_ASSERT(k < rows.size());
  return rows[k];
}

inline dimension_type
Bit_Matrix::num_columns() const {
  return row_size;
}

inline dimension_type
Bit_Matrix::num_rows() const {
  return rows.size();
}

inline void
Bit_Matrix::clear() {
  // Clear `rows' and minimize its capacity.
  std::vector<Bit_Row> tmp;
  std::swap(tmp, rows);
  row_size = 0;
}

inline memory_size_type
Bit_Matrix::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

inline bool
Bit_Matrix::Bit_Row_Less_Than::
operator()(const Bit_Row& x, const Bit_Row& y) const {
  return compare(x, y) < 0;
}

inline bool
Bit_Matrix::sorted_contains(const Bit_Row& row) const {
  PPL_ASSERT(check_sorted());
  return std::binary_search(rows.begin(), rows.end(), row,
                            Bit_Row_Less_Than());
}

/*! \relates Bit_Matrix */
inline bool
operator!=(const Bit_Matrix& x, const Bit_Matrix& y) {
  return !(x == y);
}

/*! \relates Bit_Matrix */
inline void
swap(Bit_Matrix& x, Bit_Matrix& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Bit_Matrix_defs.hh line 186. */

/* Automatically generated from PPL source file ../src/Scalar_Products_defs.hh line 1. */
/* Scalar_Products class definition.
*/


/* Automatically generated from PPL source file ../src/Scalar_Products_defs.hh line 34. */

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A class implementing various scalar product functions.
/*! \ingroup PPL_CXX_interface
  When computing the scalar product of (Linear_Expression or Constraint or
  Generator) objects <CODE>x</CODE> and <CODE>y</CODE>, it is assumed
  that the space dimension of the first object <CODE>x</CODE> is less
  than or equal to the space dimension of the second object <CODE>y</CODE>.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
class Parma_Polyhedra_Library::Scalar_Products {
public:
  //! Computes the scalar product of \p x and \p y and assigns it to \p z.
  static void assign(Coefficient& z,
                     const Linear_Expression& x, const Linear_Expression& y);

  //! Computes the scalar product of \p c and \p g and assigns it to \p z.
  static void assign(Coefficient& z, const Constraint& c, const Generator& g);

  //! Computes the scalar product of \p g and \p c and assigns it to \p z.
  static void assign(Coefficient& z, const Generator& g, const Constraint& c);

  //! Computes the scalar product of \p c and \p g and assigns it to \p z.
  static void assign(Coefficient& z,
                     const Constraint& c, const Grid_Generator& gg);

  //! Computes the scalar product of \p g and \p cg and assigns it to \p z.
  static void assign(Coefficient& z,
                     const Grid_Generator& gg, const Congruence& cg);

  //! Computes the scalar product of \p cg and \p g and assigns it to \p z.
  static void assign(Coefficient& z,
                     const Congruence& cg, const Grid_Generator& gg);

  //! Returns the sign of the scalar product between \p x and \p y.
  static int sign(const Linear_Expression& x, const Linear_Expression& y);

  //! Returns the sign of the scalar product between \p c and \p g.
  static int sign(const Constraint& c, const Generator& g);

  //! Returns the sign of the scalar product between \p g and \p c.
  static int sign(const Generator& g, const Constraint& c);

  //! Returns the sign of the scalar product between \p c and \p g.
  static int sign(const Constraint& c, const Grid_Generator& g);

  /*! \brief
    Computes the \e reduced scalar product of \p x and \p y,
    where the \f$\epsilon\f$ coefficient of \p x is ignored,
    and assigns the result to \p z.
  */
  static void reduced_assign(Coefficient& z,
                             const Linear_Expression& x,
                             const Linear_Expression& y);

  /*! \brief
    Computes the \e reduced scalar product of \p c and \p g,
    where the \f$\epsilon\f$ coefficient of \p c is ignored,
    and assigns the result to \p z.
  */
  static void reduced_assign(Coefficient& z,
                             const Constraint& c, const Generator& g);

  /*! \brief
    Computes the \e reduced scalar product of \p g and \p c,
    where the \f$\epsilon\f$ coefficient of \p g is ignored,
    and assigns the result to \p z.
  */
  static void reduced_assign(Coefficient& z,
                             const Generator& g, const Constraint& c);

  //! \brief
  //! Computes the \e reduced scalar product of \p g and \p cg,
  //! where the \f$\epsilon\f$ coefficient of \p g is ignored,
  //! and assigns the result to \p z.
  static void reduced_assign(Coefficient& z,
                             const Grid_Generator& gg, const Congruence& cg);

  /*! \brief
    Returns the sign of the \e reduced scalar product of \p x and \p y,
    where the \f$\epsilon\f$ coefficient of \p x is ignored.
  */
  static int reduced_sign(const Linear_Expression& x,
                          const Linear_Expression& y);

  /*! \brief
    Returns the sign of the \e reduced scalar product of \p c and \p g,
    where the \f$\epsilon\f$ coefficient of \p c is ignored.
  */
  static int reduced_sign(const Constraint& c, const Generator& g);

  /*! \brief
    Returns the sign of the \e reduced scalar product of \p g and \p c,
    where the \f$\epsilon\f$ coefficient of \p g is ignored.
  */
  static int reduced_sign(const Generator& g, const Constraint& c);

  /*! \brief
    Computes the \e homogeneous scalar product of \p x and \p y,
    where the inhomogeneous terms are ignored,
    and assigns the result to \p z.
  */
  static void homogeneous_assign(Coefficient& z,
                                 const Linear_Expression& x,
                                 const Linear_Expression& y);
  /*! \brief
    Computes the \e homogeneous scalar product of \p e and \p g,
    where the inhomogeneous terms are ignored,
    and assigns the result to \p z.
  */
  static void homogeneous_assign(Coefficient& z,
                                 const Linear_Expression& e,
                                 const Generator& g);

  //! \brief
  //! Computes the \e homogeneous scalar product of \p gg and \p c,
  //! where the inhomogeneous terms are ignored,
  //! and assigns the result to \p z.
  static void homogeneous_assign(Coefficient& z,
                                 const Grid_Generator& gg,
                                 const Constraint& c);

  //! \brief
  //! Computes the \e homogeneous scalar product of \p g and \p cg,
  //! where the inhomogeneous terms are ignored,
  //! and assigns the result to \p z.
  static void homogeneous_assign(Coefficient& z,
                                 const Grid_Generator& gg,
                                 const Congruence& cg);

  //! \brief
  //! Computes the \e homogeneous scalar product of \p e and \p g,
  //! where the inhomogeneous terms are ignored,
  //! and assigns the result to \p z.
  static void homogeneous_assign(Coefficient& z,
                                 const Linear_Expression& e,
                                 const Grid_Generator& g);

  /*! \brief
    Returns the sign of the \e homogeneous scalar product of \p x and \p y,
    where the inhomogeneous terms are ignored.
  */
  static int homogeneous_sign(const Linear_Expression& x,
                              const Linear_Expression& y);

  /*! \brief
    Returns the sign of the \e homogeneous scalar product of \p e and \p g,
    where the inhomogeneous terms are ignored.
  */
  static int homogeneous_sign(const Linear_Expression& e, const Generator& g);

  //! \brief
  //! Returns the sign of the \e homogeneous scalar product of \p e and \p g,
  //! where the inhomogeneous terms are ignored,
  static int homogeneous_sign(const Linear_Expression& e,
                              const Grid_Generator& g);

  //! \brief
  //! Returns the sign of the \e homogeneous scalar product of \p g and \p c,
  //! where the inhomogeneous terms are ignored,
  static int homogeneous_sign(const Grid_Generator& g, const Constraint& c);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Scalar product sign function object depending on topology.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
class Parma_Polyhedra_Library::Topology_Adjusted_Scalar_Product_Sign {
public:
  //! Constructs the function object according to the topology of \p c.
  Topology_Adjusted_Scalar_Product_Sign(const Constraint& c);

  //! Constructs the function object according to the topology of \p g.
  Topology_Adjusted_Scalar_Product_Sign(const Generator& g);

  //! Computes the (topology adjusted) scalar product sign of \p c and \p g.
  int operator()(const Constraint&, const Generator&) const;

  //! Computes the (topology adjusted) scalar product sign of \p g and \p c.
  int operator()(const Generator&, const Constraint&) const;

private:
  //! The type of the scalar product sign function pointer.
  typedef int (* const SPS_type)(const Linear_Expression&,
                                 const Linear_Expression&);

  //! The scalar product sign function pointer.
  SPS_type sps_fp;
};

// NOTE: Scalar_Products_inlines.hh is NOT included here, to avoid cyclic
// include dependencies.

/* Automatically generated from PPL source file ../src/Scalar_Products_inlines.hh line 1. */
/* Scalar_Products class implementation (inline functions).
*/


/* Automatically generated from PPL source file ../src/Scalar_Products_inlines.hh line 32. */

namespace Parma_Polyhedra_Library {

inline int
Scalar_Products::sign(const Linear_Expression& x, const Linear_Expression& y) {
  PPL_DIRTY_TEMP_COEFFICIENT(z);
  assign(z, x, y);
  return sgn(z);
}

inline int
Scalar_Products::reduced_sign(const Linear_Expression& x,
                              const Linear_Expression& y) {
  PPL_DIRTY_TEMP_COEFFICIENT(z);
  reduced_assign(z, x, y);
  return sgn(z);
}

inline int
Scalar_Products::homogeneous_sign(const Linear_Expression& x,
                                  const Linear_Expression& y) {
  PPL_DIRTY_TEMP_COEFFICIENT(z);
  homogeneous_assign(z, x, y);
  return sgn(z);
}

inline int
Scalar_Products::sign(const Constraint& c, const Generator& g) {
  return sign(c.expr, g.expr);
}

inline int
Scalar_Products::sign(const Generator& g, const Constraint& c) {
  return sign(g.expr, c.expr);
}

inline int
Scalar_Products::sign(const Constraint& c, const Grid_Generator& g) {
  PPL_DIRTY_TEMP_COEFFICIENT(z);
  assign(z, c, g);
  return sgn(z);
}

inline int
Scalar_Products::reduced_sign(const Constraint& c, const Generator& g) {
  // The reduced scalar product is only defined if the topology of `c' is
  // NNC.
  PPL_ASSERT(!c.is_necessarily_closed());
  return reduced_sign(c.expr, g.expr);
}

inline int
Scalar_Products::reduced_sign(const Generator& g, const Constraint& c) {
  // The reduced scalar product is only defined if the topology of `g' is
  // NNC.
  PPL_ASSERT(!c.is_necessarily_closed());
  return reduced_sign(g.expr, c.expr);
}

inline void
Scalar_Products::homogeneous_assign(Coefficient& z,
                                    const Linear_Expression& e,
                                    const Generator& g) {
  homogeneous_assign(z, e, g.expr);
}

inline void
Scalar_Products::homogeneous_assign(Coefficient& z,
                                    const Linear_Expression& e,
                                    const Grid_Generator& g) {
  homogeneous_assign(z, e, g.expr);
}

inline int
Scalar_Products::homogeneous_sign(const Linear_Expression& e,
                                  const Generator& g) {
  return homogeneous_sign(e, g.expr);
}

inline int
Scalar_Products::homogeneous_sign(const Linear_Expression& e,
                                  const Grid_Generator& g) {
  return homogeneous_sign(e, g.expr);
}

inline int
Scalar_Products::homogeneous_sign(const Grid_Generator& g,
                                  const Constraint& c) {
  PPL_DIRTY_TEMP_COEFFICIENT(z);
  homogeneous_assign(z, g, c);
  return sgn(z);
}

inline
Topology_Adjusted_Scalar_Product_Sign
::Topology_Adjusted_Scalar_Product_Sign(const Constraint& c)
  : sps_fp(c.is_necessarily_closed()
           ? static_cast<SPS_type>(&Scalar_Products::sign)
           : static_cast<SPS_type>(&Scalar_Products::reduced_sign)) {
}

inline
Topology_Adjusted_Scalar_Product_Sign
::Topology_Adjusted_Scalar_Product_Sign(const Generator& g)
  : sps_fp(g.is_necessarily_closed()
           ? static_cast<SPS_type>(&Scalar_Products::sign)
           : static_cast<SPS_type>(&Scalar_Products::reduced_sign)) {
}

inline int
Topology_Adjusted_Scalar_Product_Sign::operator()(const Constraint& c,
                                                  const Generator& g) const {
  PPL_ASSERT(c.space_dimension() <= g.space_dimension());
  PPL_ASSERT(sps_fp == (c.is_necessarily_closed()
                    ? static_cast<SPS_type>(&Scalar_Products::sign)
                    : static_cast<SPS_type>(&Scalar_Products::reduced_sign)));
  return sps_fp(c.expr, g.expr);
}

inline int
Topology_Adjusted_Scalar_Product_Sign::operator()(const Generator& g,
                                                  const Constraint& c) const {
  PPL_ASSERT(g.space_dimension() <= c.space_dimension());
  PPL_ASSERT(sps_fp == (g.is_necessarily_closed()
                    ? static_cast<SPS_type>(&Scalar_Products::sign)
                    : static_cast<SPS_type>(&Scalar_Products::reduced_sign)));
  return sps_fp(g.expr, c.expr);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/swapping_sort_templates.hh line 1. */
/* Sorting objects for which copies cost more than swaps.
*/


#include <vector>
#include <algorithm>

namespace Parma_Polyhedra_Library {

namespace Implementation {

template <typename RA_Container, typename Compare>
struct Indirect_Sort_Compare {
  typedef typename RA_Container::size_type size_type;

  Indirect_Sort_Compare(const RA_Container& cont,
                        size_type base = 0,
                        Compare comp = Compare())
    : container(cont), base_index(base), compare(comp) {
  }

  bool operator()(size_type i, size_type j) const {
    return compare(container[base_index + i], container[base_index + j]);
  }

  const RA_Container& container;
  const size_type base_index;
  const Compare compare;
}; // struct Indirect_Sort_Compare

template <typename RA_Container>
struct Indirect_Unique_Compare {
  typedef typename RA_Container::size_type size_type;

  Indirect_Unique_Compare(const RA_Container& cont, size_type base = 0)
    : container(cont), base_index(base) {
  }

  bool operator()(size_type i, size_type j) const {
    return container[base_index + i] == container[base_index + j];
  }

  const RA_Container& container;
  const size_type base_index;
}; // struct Indirect_Unique_Compare

template <typename RA_Container>
struct Indirect_Swapper {
  typedef typename RA_Container::size_type size_type;

  Indirect_Swapper(RA_Container& cont, size_type base = 0)
    : container(cont), base_index(base) {
  }

  void operator()(size_type i, size_type j) const {
    using std::swap;
    swap(container[base_index + i], container[base_index + j]);
  }

  RA_Container& container;
  const size_type base_index;
}; // struct Indirect_Swapper

template <typename RA_Container1, typename RA_Container2>
struct Indirect_Swapper2 {
  typedef typename RA_Container1::size_type size_type;

  Indirect_Swapper2(RA_Container1& cont1, RA_Container2& cont2)
    : container1(cont1), container2(cont2) {
  }

  void operator()(size_type i, size_type j) const {
    using std::swap;
    swap(container1[i], container1[j]);
    swap(container2[i], container2[j]);
  }

  RA_Container1& container1;
  RA_Container2& container2;
}; // struct Indirect_Swapper2

template <typename Sort_Comparer, typename Unique_Comparer, typename Swapper>
typename Sort_Comparer::size_type
indirect_sort_and_unique(typename Sort_Comparer::size_type num_elems,
                         Sort_Comparer sort_cmp,
                         Unique_Comparer unique_cmp,
                         Swapper indirect_swap) {
  typedef typename Sort_Comparer::size_type index_type;
  // `iv' is a vector of indices for the portion of rows to be sorted.
  PPL_ASSERT(num_elems >= 2);
  std::vector<index_type> iv;
  iv.reserve(num_elems);
  for (index_type i = 0, i_end = num_elems; i != i_end; ++i) {
    iv.push_back(i);
  }

  typedef typename std::vector<index_type>::iterator Iter;
  const Iter iv_begin = iv.begin();
  Iter iv_end = iv.end();

  // Sort `iv' by comparing the rows indexed by its elements.
  std::sort(iv_begin, iv_end, sort_cmp);

  // Swap the indexed rows according to `iv':
  // for each index `i', the element that should be placed in
  // position dst = i is the one placed in position src = iv[i].
  for (index_type i = num_elems; i-- > 0; ) {
    if (i != iv[i]) {
      index_type dst = i;
      index_type src = iv[i];
      do {
        indirect_swap(src, dst);
        iv[dst] = dst;
        dst = src;
        src = iv[dst];
      } while (i != src);
      iv[dst] = dst;
    }
  }

  // Restore `iv' indices to 0 .. num_elems-1 for the call to unique.
  for (index_type i = num_elems; i-- > 0; ) {
    iv[i] = i;
  }

  // Unique `iv' by comparing the rows indexed by its elements.
  iv_end = std::unique(iv_begin, iv_end, unique_cmp);

  const index_type num_sorted = static_cast<index_type>(iv_end - iv_begin);
  const index_type num_duplicates = num_elems - num_sorted;
  if (num_duplicates == 0) {
    return 0;
  }

  // There were duplicates: swap the rows according to `iv'.
  index_type dst = 0;
  while (dst < num_sorted && dst == iv[dst]) {
    ++dst;
  }
  if (dst == num_sorted) {
    return num_duplicates;
  }
  do {
    const index_type src = iv[dst];
    indirect_swap(src, dst);
    ++dst;
  }
  while (dst < num_sorted);
  return num_duplicates;
}

template <typename Iter>
Iter
swapping_unique(Iter first, Iter last) {
  return swapping_unique(first, last, std::iter_swap<Iter, Iter>);
}

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_System_templates.hh line 32. */
#include <algorithm>
#include <iostream>
#include <string>
#include <deque>

namespace Parma_Polyhedra_Library {

template <typename Row>
dimension_type
Linear_System<Row>::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;
}

template <typename Row>
void
Linear_System<Row>::merge_rows_assign(const Linear_System& y) {
  PPL_ASSERT(space_dimension() >= y.space_dimension());
  // 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...
  Swapping_Vector<Row> tmp;
  // ... with enough capacity not to require any reallocations.
  tmp.reserve(compute_capacity(x.rows.size() + y.rows.size(),
                               tmp.max_num_rows()));

  dimension_type xi = 0;
  const dimension_type x_num_rows = x.num_rows();
  dimension_type yi = 0;
  const 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'
      tmp.resize(tmp.size() + 1);
      swap(tmp.back(), x.rows[xi++]);
      tmp.back().set_representation(representation());
      if (comp == 0) {
        // A duplicate element.
        ++yi;
      }
    }
    else {
      // (comp > 0)
      tmp.resize(tmp.size() + 1);
      Row copy(y[yi++], space_dimension(), representation());
      swap(tmp.back(), copy);
    }
  }
  // Insert what is left.
  if (xi < x_num_rows) {
    while (xi < x_num_rows) {
      tmp.resize(tmp.size() + 1);
      swap(tmp.back(), x.rows[xi++]);
      tmp.back().set_representation(representation());
    }
  }
  else {
    while (yi < y_num_rows) {
      tmp.resize(tmp.size() + 1);
      Row copy(y[yi++], space_dimension(), representation());
      swap(tmp.back(), copy);
    }
  }

  // We get the result matrix and let the old one be destroyed.
  swap(tmp, rows);
  // There are no pending rows.
  unset_pending_rows();
  PPL_ASSERT(check_sorted());
  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_System<Row>::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.
  s << "topology " << (is_necessarily_closed()
                       ? "NECESSARILY_CLOSED"
                       : "NOT_NECESSARILY_CLOSED")
    << "\n"
    << num_rows() << " x " << space_dimension() << " ";
  Parma_Polyhedra_Library::ascii_dump(s, representation());
  s << " " << (sorted ? "(sorted)" : "(not_sorted)")
    << "\n"
    << "index_first_pending " << first_pending_row()
    << "\n";
  for (dimension_type i = 0; i < rows.size(); ++i) {
    rows[i].ascii_dump(s);
  }
}

PPL_OUTPUT_TEMPLATE_DEFINITIONS_ASCII_ONLY(Row, Linear_System<Row>)

template <typename Row>
bool
Linear_System<Row>::ascii_load(std::istream& s) {
  std::string str;
  if (!(s >> str) || str != "topology") {
    return false;
  }
  if (!(s >> str)) {
    return false;
  }

  clear();

  Topology t;
  if (str == "NECESSARILY_CLOSED") {
    t = NECESSARILY_CLOSED;
  }
  else {
    if (str != "NOT_NECESSARILY_CLOSED") {
      return false;
    }
    t = NOT_NECESSARILY_CLOSED;
  }

  set_topology(t);

  dimension_type nrows;
  dimension_type space_dims;
  if (!(s >> nrows)) {
    return false;
  }
  if (!(s >> str) || str != "x") {
    return false;
  }
  if (!(s >> space_dims)) {
    return false;
  }

  space_dimension_ = space_dims;

  if (!Parma_Polyhedra_Library::ascii_load(s, representation_)) {
    return false;
  }

  if (!(s >> str) || (str != "(sorted)" && str != "(not_sorted)")) {
    return false;
  }
  const bool sortedness = (str == "(sorted)");
  dimension_type index;
  if (!(s >> str) || str != "index_first_pending") {
    return false;
  }
  if (!(s >> index)) {
    return false;
  }

  Row row;
  for (dimension_type i = 0; i < nrows; ++i) {
    if (!row.ascii_load(s)) {
      return false;
    }
    insert(row, Recycle_Input());
  }
  index_first_pending = index;
  sorted = sortedness;

  // Check invariants.
  PPL_ASSERT(OK());
  return true;
}

template <typename Row>
void
Linear_System<Row>::insert(const Row& r) {
  Row tmp(r, representation());
  insert(tmp, Recycle_Input());
}

template <typename Row>
void
Linear_System<Row>::insert(Row& r, Recycle_Input) {
  insert_no_ok(r, Recycle_Input());
  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_System<Row>::insert_no_ok(Row& r, Recycle_Input) {
  PPL_ASSERT(topology() == r.topology());
  // This method is only used when the system has no pending rows.
  PPL_ASSERT(num_pending_rows() == 0);

  const bool was_sorted = is_sorted();

  insert_pending_no_ok(r, Recycle_Input());

  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.
      sorted = (compare(rows[nrows-2], rows[nrows-1]) <= 0);
    }
    else {
      // A system having only one row is sorted.
      sorted = true;
    }
  }

  unset_pending_rows();
}

template <typename Row>
void
Linear_System<Row>::insert_pending_no_ok(Row& r, Recycle_Input) {
  // TODO: A Grid_Generator_System may contain non-normalized lines that
  // represent parameters, so this check is disabled. Consider re-enabling it
  // when it's possibile.
#if 0
  // 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());
#endif

  PPL_ASSERT(r.topology() == topology());

  r.set_representation(representation());

  if (space_dimension() < r.space_dimension()) {
    set_space_dimension_no_ok(r.space_dimension());
  }
  else {
    r.set_space_dimension_no_ok(space_dimension());
  }

  rows.resize(rows.size() + 1);
  swap(rows.back(), r);
}

template <typename Row>
void
Linear_System<Row>::insert_pending(const Row& r) {
  Row tmp(r, representation());
  insert_pending(tmp, Recycle_Input());
}

template <typename Row>
void
Linear_System<Row>::insert_pending(Row& r, Recycle_Input) {
  insert_pending_no_ok(r, Recycle_Input());
  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_System<Row>::insert_pending(const Linear_System& y) {
  Linear_System tmp(y, representation(), With_Pending());
  insert_pending(tmp, Recycle_Input());
}

template <typename Row>
void
Linear_System<Row>::insert_pending(Linear_System& y, Recycle_Input) {
  Linear_System& x = *this;
  PPL_ASSERT(x.space_dimension() == y.space_dimension());

  // Steal the rows of `y'.
  // This loop must use an increasing index (instead of a decreasing one) to
  // preserve the row ordering.
  for (dimension_type i = 0; i < y.num_rows(); ++i) {
    x.insert_pending(y.rows[i], Recycle_Input());
  }

  y.clear();

  PPL_ASSERT(x.OK());
}

template <typename Row>
void
Linear_System<Row>::insert(const Linear_System& y) {
  Linear_System tmp(y, representation(), With_Pending());
  insert(tmp, Recycle_Input());
}

template <typename Row>
void
Linear_System<Row>::insert(Linear_System& y, Recycle_Input) {
  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) {
      sorted = false;
    }
    else {
      // `y' is sorted and has no pending rows.
      const dimension_type n_rows = num_rows();
      if (n_rows > 0) {
        sorted = (compare(rows[n_rows-1], y[0]) <= 0);
      }
    }
  }

  // Add the rows of `y' as if they were pending.
  insert_pending(y, Recycle_Input());

  // TODO: May y have pending rows? Should they remain pending?

  // There are no pending_rows.
  unset_pending_rows();

  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_System<Row>::remove_space_dimensions(const Variables_Set& vars) {
  // Dimension-compatibility assertion.
  PPL_ASSERT(space_dimension() >= vars.space_dimension());

  // The removal of no dimensions from any system is a no-op.  This
  // case also captures the only legal removal of dimensions from a
  // 0-dim system.
  if (vars.empty()) {
    return;
  }

  // NOTE: num_rows() is *not* constant, because it may be decreased by
  // remove_row_no_ok().
  for (dimension_type i = 0; i < num_rows(); ) {
    const bool valid = rows[i].remove_space_dimensions(vars);
    if (!valid) {
      // Remove the current row.
      // We can't call remove_row(i) here, because the system is not OK as
      // some rows already have the new space dimension and others still have
      // the old one.
      remove_row_no_ok(i, false);
    }
    else {
      ++i;
    }
  }

  space_dimension_ -= vars.size();

  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_System<Row>::shift_space_dimensions(Variable v, dimension_type n) {
  // NOTE: v.id() may be equal to the space dimension of the system
  // (when no space dimension need to be shifted).
  PPL_ASSERT(v.id() <= space_dimension());
  for (dimension_type i = rows.size(); i-- > 0; ) {
    rows[i].shift_space_dimensions(v, n);
  }
  space_dimension_ += n;
  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_System<Row>::sort_rows() {
  // We sort the non-pending rows only.
  sort_rows(0, first_pending_row());
  sorted = true;
  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_System<Row>::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 bool sorting_pending = (first_row >= first_pending_row());
  const dimension_type old_num_pending = num_pending_rows();

  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.
  using namespace Implementation;
  typedef Swapping_Vector<Row> Cont;
  typedef Indirect_Sort_Compare<Cont, Row_Less_Than> Sort_Compare;
  typedef Indirect_Swapper<Cont> Swapper;
  const dimension_type num_duplicates
    = indirect_sort_and_unique(num_elems,
                               Sort_Compare(rows, first_row),
                               Unique_Compare(rows, first_row),
                               Swapper(rows, first_row));

  if (num_duplicates > 0) {
    typedef typename Cont::iterator Iter;
    typedef typename std::iterator_traits<Iter>::difference_type diff_t;
    Iter last = rows.begin() + static_cast<diff_t>(last_row);
    Iter first = last - + static_cast<diff_t>(num_duplicates);
    rows.erase(first, last);
  }

  if (sorting_pending) {
    PPL_ASSERT(old_num_pending >= num_duplicates);
    index_first_pending = num_rows() - (old_num_pending - num_duplicates);
  }
  else {
    index_first_pending = num_rows() - old_num_pending;
  }

  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_System<Row>::strong_normalize() {
  const dimension_type nrows = rows.size();
  // We strongly normalize also the pending rows.
  for (dimension_type i = nrows; i-- > 0; ) {
    rows[i].strong_normalize();
  }
  sorted = (nrows <= 1);
  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_System<Row>::sign_normalize() {
  const dimension_type nrows = rows.size();
  // We sign-normalize also the pending rows.
  for (dimension_type i = nrows; i-- > 0; ) {
    rows[i].sign_normalize();
  }
  sorted = (nrows <= 1);
  PPL_ASSERT(OK());
}

/*! \relates Parma_Polyhedra_Library::Linear_System */
template <typename Row>
bool
operator==(const Linear_System<Row>& x, const Linear_System<Row>& y) {
  if (x.space_dimension() != y.space_dimension()) {
    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;
  }
  // TODO: Check if the following comment is up to date.
  // Notice that calling operator==(const Swapping_Vector<Row>&,
  //                                const Swapping_Vector<Row>&)
  // 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;
}

template <typename Row>
void
Linear_System<Row>::sort_and_remove_with_sat(Bit_Matrix& sat) {
  // We can only sort the non-pending part of the system.
  PPL_ASSERT(first_pending_row() == sat.num_rows());
  if (first_pending_row() <= 1) {
    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 Swapping_Vector<Row> Cont;
  const Implementation::Indirect_Sort_Compare<Cont, Row_Less_Than>
    sort_cmp(rows);
  const Unique_Compare unique_cmp(rows);
  const Implementation::Indirect_Swapper2<Cont, Bit_Matrix> swapper(rows, sat);

  const dimension_type num_duplicates
    = Implementation::indirect_sort_and_unique(num_elems, sort_cmp,
                                               unique_cmp, swapper);

  const dimension_type new_first_pending_row
    = first_pending_row() - num_duplicates;

  if (num_pending_rows() > 0) {
    // In this case, we must put the duplicates after the pending rows.
    const dimension_type n_rows = num_rows() - 1;
    for (dimension_type i = 0; i < num_duplicates; ++i) {
      swap(rows[new_first_pending_row + i], rows[n_rows - i]);
    }
  }

  // Erasing the duplicated rows...
  rows.resize(rows.size() - num_duplicates);
  index_first_pending = new_first_pending_row;
  // ... and the corresponding rows of the saturation matrix.
  sat.remove_trailing_rows(num_duplicates);

  // Now the system is sorted.
  sorted = true;

  PPL_ASSERT(OK());
}

template <typename Row>
dimension_type
Linear_System<Row>::gauss(const dimension_type n_lines_or_equalities) {
  // This method is only applied to a 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(num_pending_rows() == 0);
  PPL_ASSERT(n_lines_or_equalities == num_lines_or_equalities());
#ifndef NDEBUG
  for (dimension_type i = n_lines_or_equalities; i-- > 0; ) {
    PPL_ASSERT((*this)[i].is_line_or_equality());
  }
#endif

  dimension_type rank = 0;
  // Will keep track of the variations on the system of equalities.
  bool changed = false;
  // TODO: Don't use the number of columns.
  const dimension_type num_cols
    = is_necessarily_closed() ? space_dimension() + 1 : space_dimension() + 2;
  // TODO: Consider exploiting the row (possible) sparseness of rows in the
  // following loop, if needed. It would probably make it more cache-efficient
  // for dense rows, too.
  for (dimension_type j = num_cols; 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 ((*this)[i].expr.get(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) {
        swap(rows[i], rows[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 (rows[k].expr.get(Variable(j - 1)) != 0) {
          rows[k].linear_combine(rows[rank], j);
          changed = true;
        }
      }
      // Already dealt with the rank-th row.
      ++rank;
      // Consider another column index `j'.
      break;
    }
  }
  if (changed) {
    sorted = false;
  }

  PPL_ASSERT(OK());
  return rank;
}

template <typename Row>
void
Linear_System<Row>
::back_substitute(const dimension_type n_lines_or_equalities) {
  // This method is only applied to a 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(num_pending_rows() == 0);
  PPL_ASSERT(n_lines_or_equalities <= num_lines_or_equalities());
#ifndef NDEBUG
  for (dimension_type i = n_lines_or_equalities; i-- > 0; ) {
    PPL_ASSERT((*this)[i].is_line_or_equality());
  }
#endif

  const dimension_type nrows = num_rows();
  // Trying to keep sortedness.
  bool still_sorted = 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.
    Row& row_k = rows[k];
    const dimension_type j = row_k.expr.last_nonzero();
    // TODO: Check this.
    PPL_ASSERT(j != 0);

    // Go through the equalities above `row_k'.
    for (dimension_type i = k; i-- > 0; ) {
      Row& row_i = rows[i];
      if (row_i.expr.get(Variable(j - 1)) != 0) {
        // Combine linearly `row_i' with `row_k'
        // so that `row_i[j]' becomes zero.
        row_i.linear_combine(row_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 `row_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 = (row_k.expr.get(Variable(j - 1)) < 0);
    if (have_to_negate) {
      neg_assign(row_k.expr);
    }

    // NOTE: Here row_k will *not* be ok if we have negated it.

    // 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) {
      Row& row_i = rows[i];
      if (row_i.expr.get(Variable(j - 1)) != 0) {
        // Combine linearly the `row_i' with `row_k'
        // so that `row_i[j]' becomes zero.
        row_i.linear_combine(row_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 `row_k' to restore strong-normalization.
      neg_assign(row_k.expr);
    }

    PPL_ASSERT(row_k.OK());
  }

  // 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 `(*this)[i]' with respect to `(*this)[i+1]'.
      still_sorted = (compare((*this)[i], (*this)[i+1]) <= 0);
    }
  }

  // Set the sortedness flag.
  sorted = still_sorted;

  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_System<Row>::simplify() {
  // This method is only applied to a system having no pending rows.
  PPL_ASSERT(num_pending_rows() == 0);

  // Partially sort the linear system so that all lines/equalities come first.
  const dimension_type old_nrows = num_rows();
  dimension_type nrows = old_nrows;
  dimension_type n_lines_or_equalities = 0;
  for (dimension_type i = 0; i < nrows; ++i) {
    if ((*this)[i].is_line_or_equality()) {
      if (n_lines_or_equalities < i) {
        swap(rows[i], rows[n_lines_or_equalities]);
        // The system was not sorted.
        PPL_ASSERT(!sorted);
      }
      ++n_lines_or_equalities;
    }
  }
  // Apply Gaussian elimination to the subsystem of lines/equalities.
  const dimension_type rank = 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; ) {
      swap(rows[--nrows], rows[rank + i]);
    }
    remove_trailing_rows(old_nrows - nrows);
    if (n_rays_or_points_or_inequalities > num_swaps) {
      set_sorted(false);
    }
    unset_pending_rows();
    n_lines_or_equalities = rank;
  }
  // Apply back-substitution to the system of rays/points/inequalities.
  back_substitute(n_lines_or_equalities);

  PPL_ASSERT(OK());
}

template <typename Row>
void
Linear_System<Row>
::add_universe_rows_and_space_dimensions(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_space_dim
    = is_necessarily_closed() ? space_dimension() : space_dimension() + 1;
  set_space_dimension(space_dimension() + n);
  rows.resize(rows.size() + n);
  // The old system is moved to the bottom.
  for (dimension_type i = old_n_rows; i-- > 0; ) {
    swap(rows[i], rows[i + n]);
  }
  for (dimension_type i = n, c = old_space_dim; 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.
    if (Variable(c).space_dimension() <= space_dimension()) {
      // Variable(c) is a user variable.
      Linear_Expression le(representation());
      le.set_space_dimension(space_dimension());
      le += Variable(c);
      Row r(le, Row::LINE_OR_EQUALITY, row_topology);
      swap(r, rows[i]);
    }
    else {
      // Variable(c) is the epsilon dimension.
      PPL_ASSERT(row_topology == NOT_NECESSARILY_CLOSED);
      Linear_Expression le(Variable(c), representation());
      Row r(le, Row::LINE_OR_EQUALITY, NECESSARILY_CLOSED);
      r.mark_as_not_necessarily_closed();
      swap(r, rows[i]);
      // Note: `r' is strongly normalized.
    }
    ++c;
  }
  // If the old system was empty, the last row added is either
  // a positivity constraint or a point.
  if (was_sorted) {
    sorted = (compare(rows[n-1], rows[n]) <= 0);
  }

  // If the system is not necessarily closed, move the epsilon coefficients to
  // the last column.
  if (!is_necessarily_closed()) {
    // Try to preserve sortedness of `gen_sys'.
    PPL_ASSERT(old_space_dim != 0);
    if (!is_sorted()) {
      for (dimension_type i = n; i-- > 0; ) {
        rows[i].expr.swap_space_dimensions(Variable(old_space_dim - 1),
                                           Variable(old_space_dim - 1 + n));
        PPL_ASSERT(rows[i].OK());
      }
    }
    else {
      dimension_type old_eps_index = old_space_dim - 1;
      // The upper-right corner of `rows' contains the J matrix:
      // swap coefficients to preserve sortedness.
      for (dimension_type i = n; i-- > 0; ++old_eps_index) {
        rows[i].expr.swap_space_dimensions(Variable(old_eps_index),
                                           Variable(old_eps_index + 1));
        PPL_ASSERT(rows[i].OK());
      }

      sorted = true;
    }
  }
  // NOTE: this already checks for OK().
  set_index_first_pending_row(index_first_pending + n);
}

template <typename Row>
void
Linear_System<Row>::sort_pending_and_remove_duplicates() {
  PPL_ASSERT(num_pending_rows() > 0);
  PPL_ASSERT(is_sorted());

  // The non-pending part of the system is already sorted.
  // Now sorting the pending part..
  const dimension_type first_pending = first_pending_row();
  sort_rows(first_pending, num_rows());
  // Recompute the number of rows, because we may have removed
  // some rows occurring more than once in the pending part.
  const dimension_type old_num_rows = num_rows();
  dimension_type num_rows = old_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(rows[k1], rows[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) {
        swap(rows[k2], rows[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) {
        swap(rows[k2], rows[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)  {
        swap(rows[k2], rows[k2 + num_duplicates]);
      }
    }
    rows.resize(num_rows);
  }
  sorted = true;
  PPL_ASSERT(OK());
}

template <typename Row>
bool
Linear_System<Row>::check_sorted() const {
  for (dimension_type i = first_pending_row(); i-- > 1; ) {
    if (compare(rows[i], rows[i-1]) < 0) {
      return false;
    }
  }
  return true;
}

template <typename Row>
bool
Linear_System<Row>::OK() const {
#ifndef NDEBUG
  using std::endl;
  using std::cerr;
#endif

  for (dimension_type i = rows.size(); i-- > 0; ) {
    if (rows[i].representation() != representation()) {
#ifndef NDEBUG
      cerr << "Linear_System has a row with the wrong representation!"
           << endl;
#endif
      return false;
    }
    if (rows[i].space_dimension() != space_dimension()) {
#ifndef NDEBUG
      cerr << "Linear_System has a row with the wrong number of space dimensions!"
           << endl;
#endif
      return false;
    }
  }

  for (dimension_type i = rows.size(); i-- > 0; ) {
    if (rows[i].topology() != topology()) {
#ifndef NDEBUG
      cerr << "Linear_System has a row with the wrong topology!"
           << endl;
#endif
      return false;
    }

  }
  // `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;
  }

  // Check for topology mismatches.
  const dimension_type n_rows = num_rows();
  for (dimension_type i = 0; i < n_rows; ++i) {
    if (topology() != rows[i].topology()) {
#ifndef NDEBUG
      cerr << "Topology mismatch between the system "
           << "and one of its rows!"
           << 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;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Linear_System_defs.hh line 581. */

/* Automatically generated from PPL source file ../src/Constraint_System_defs.hh line 31. */

/* Automatically generated from PPL source file ../src/Constraint_System_defs.hh line 38. */
#include <iterator>
#include <iosfwd>
#include <cstddef>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*!
  \relates Parma_Polyhedra_Library::Constraint_System
  Writes <CODE>true</CODE> if \p cs is empty.  Otherwise, writes on
  \p s the constraints of \p cs, all in one row and separated by ", ".
*/
std::ostream& operator<<(std::ostream& s, const Constraint_System& cs);

} // namespace IO_Operators

// TODO: Consider removing this.
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are identical.
/*! \relates Constraint_System */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator==(const Constraint_System& x, const Constraint_System& y);

// TODO: Consider removing this.
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are different.
/*! \relates Constraint_System */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator!=(const Constraint_System& x, const Constraint_System& y);

/*! \relates Constraint_System */
void
swap(Constraint_System& x, Constraint_System& y);

} // namespace Parma_Polyhedra_Library

//! A system of constraints.
/*! \ingroup PPL_CXX_interface
    An object of the class Constraint_System is a system of constraints,
    i.e., a multiset of objects of the class Constraint.
    When inserting constraints in a system, space dimensions are
    automatically adjusted so that all the constraints in the system
    are defined on the same vector space.

    \par
    In all the examples it is assumed that variables
    <CODE>x</CODE> and <CODE>y</CODE> are defined as follows:
    \code
  Variable x(0);
  Variable y(1);
    \endcode

    \par Example 1
    The following code builds a system of constraints corresponding to
    a square in \f$\Rset^2\f$:
    \code
  Constraint_System cs;
  cs.insert(x >= 0);
  cs.insert(x <= 3);
  cs.insert(y >= 0);
  cs.insert(y <= 3);
    \endcode
    Note that:
    the constraint system is created with space dimension zero;
    the first and third constraint insertions increase the space
    dimension to \f$1\f$ and \f$2\f$, respectively.

    \par Example 2
    By adding four strict inequalities to the constraint system
    of the previous example, we can remove just the four
    vertices from the square defined above.
    \code
  cs.insert(x + y > 0);
  cs.insert(x + y < 6);
  cs.insert(x - y < 3);
  cs.insert(y - x < 3);
    \endcode

    \par Example 3
    The following code builds a system of constraints corresponding to
    a half-strip in \f$\Rset^2\f$:
    \code
  Constraint_System cs;
  cs.insert(x >= 0);
  cs.insert(x - y <= 0);
  cs.insert(x - y + 1 >= 0);
    \endcode

    \note
    After inserting a multiset of constraints in a constraint system,
    there are no guarantees that an <EM>exact</EM> copy of them
    can be retrieved:
    in general, only an <EM>equivalent</EM> constraint system
    will be available, where original constraints may have been
    reordered, removed (if they are trivial, duplicate or
    implied by other constraints), linearly combined, etc.
*/
class Parma_Polyhedra_Library::Constraint_System {
public:
  typedef Constraint row_type;

  static const Representation default_representation = SPARSE;

  //! Default constructor: builds an empty system of constraints.
  explicit Constraint_System(Representation r = default_representation);

  //! Builds the singleton system containing only constraint \p c.
  explicit Constraint_System(const Constraint& c,
                             Representation r = default_representation);

  //! Builds a system containing copies of any equalities in \p cgs.
  explicit Constraint_System(const Congruence_System& cgs,
                             Representation r = default_representation);

  //! Ordinary copy constructor.
  /*!
    \note The copy will have the same representation as `cs', to make it
          indistinguishable from `cs'.
  */
  Constraint_System(const Constraint_System& cs);

  //! Copy constructor with specified representation.
  Constraint_System(const Constraint_System& cs, Representation r);

  //! Destructor.
  ~Constraint_System();

  //! Assignment operator.
  Constraint_System& operator=(const Constraint_System& y);

  //! Returns the current representation of *this.
  Representation representation() const;

  //! Converts *this to the specified representation.
  void set_representation(Representation r);

  //! Returns the maximum space dimension a Constraint_System can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  //! Sets the space dimension of the rows in the system to \p space_dim .
  void set_space_dimension(dimension_type space_dim);

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    contains one or more equality constraints.
  */
  bool has_equalities() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    contains one or more strict inequality constraints.
  */
  bool has_strict_inequalities() const;

  /*! \brief
    Inserts in \p *this a copy of the constraint \p c,
    increasing the number of space dimensions if needed.
  */
  void insert(const Constraint& c);

  //! Initializes the class.
  static void initialize();

  //! Finalizes the class.
  static void finalize();

  /*! \brief
    Returns the singleton system containing only Constraint::zero_dim_false().
  */
  static const Constraint_System& zero_dim_empty();

  typedef Constraint_System_const_iterator const_iterator;

  //! Returns <CODE>true</CODE> if and only if \p *this has no constraints.
  bool empty() const;

  /*! \brief
    Removes all the constraints from the constraint system
    and sets its space dimension to 0.
  */
  void clear();

  /*! \brief
    Returns the const_iterator pointing to the first constraint,
    if \p *this is not empty;
    otherwise, returns the past-the-end const_iterator.
  */
  const_iterator begin() const;

  //! Returns the past-the-end const_iterator.
  const_iterator end() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Swaps \p *this with \p y.
  void m_swap(Constraint_System& y);

private:
  Linear_System<Constraint> sys;

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the singleton system containing only Constraint::zero_dim_false().
  */
  static const Constraint_System* zero_dim_empty_p;

  friend class Constraint_System_const_iterator;

  friend bool operator==(const Constraint_System& x,
                         const Constraint_System& y);

  //! Builds an empty system of constraints having the specified topology.
  explicit Constraint_System(Topology topol,
                             Representation r = default_representation);

  /*! \brief
    Builds a system of constraints on a \p space_dim dimensional space. If
    \p topol is <CODE>NOT_NECESSARILY_CLOSED</CODE> the \f$\epsilon\f$
    dimension is added.
  */
  Constraint_System(Topology topol, dimension_type space_dim,
                    Representation r = default_representation);

  //! Returns the number of equality constraints.
  dimension_type num_equalities() const;

  //! Returns the number of inequality constraints.
  dimension_type num_inequalities() const;

  /*! \brief
    Applies Gaussian elimination and back-substitution so as
    to provide a partial simplification of the system of constraints.

    It is assumed that the system has no pending constraints.
  */
  void simplify();

  /*! \brief
    Adjusts \p *this so that it matches \p new_topology and
    \p new_space_dim (adding or removing columns if needed).
    Returns <CODE>false</CODE> if and only if \p topol is
    equal to <CODE>NECESSARILY_CLOSED</CODE> and \p *this
    contains strict inequalities.
  */
  bool adjust_topology_and_space_dimension(Topology new_topology,
                                           dimension_type new_space_dim);

  //! Returns a constant reference to the \p k- th constraint of the system.
  const Constraint& operator[](dimension_type k) const;

  //! Returns <CODE>true</CODE> if \p g satisfies all the constraints.
  bool satisfies_all_constraints(const Generator& g) const;

  //! Substitutes a given column of coefficients by a given affine expression.
  /*!
    \param v
    The variable to which the affine transformation is substituted.

    \param expr
    The numerator of the affine transformation:
    \f$\sum_{i = 0}^{n - 1} a_i x_i + b\f$;

    \param denominator
    The denominator of the affine transformation.

    We want to allow affine transformations
    (see Section \ref Images_and_Preimages_of_Affine_Transfer_Relations)
    having any rational coefficients. Since the coefficients of the
    constraints are integers we must also provide an integer \p
    denominator that will be used as denominator of the affine
    transformation.
    The denominator is required to be a positive integer.

    The affine transformation substitutes the matrix of constraints
    by a new matrix whose elements \f${a'}_{ij}\f$ are built from
    the old one \f$a_{ij}\f$ as follows:
    \f[
      {a'}_{ij} =
        \begin{cases}
          a_{ij} * \mathrm{denominator} + a_{iv} * \mathrm{expr}[j]
            \quad \text{for } j \neq v; \\
          \mathrm{expr}[v] * a_{iv}
            \quad \text{for } j = v.
        \end{cases}
    \f]

    \p expr is a constant parameter and unaltered by this computation.
  */
  void affine_preimage(Variable v,
                       const Linear_Expression& expr,
                       Coefficient_traits::const_reference denominator);

  /*! \brief
    Inserts in \p *this a copy of the constraint \p c,
    increasing the number of space dimensions if needed.
    It is a pending constraint.
  */
  void insert_pending(const Constraint& c);

  //! Adds low-level constraints to the constraint system.
  void add_low_level_constraints();

  //! Returns the system topology.
  Topology topology() const;

  dimension_type num_rows() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    the system topology is <CODE>NECESSARILY_CLOSED</CODE>.
  */
  bool is_necessarily_closed() const;

  //! Returns the number of rows that are in the pending part of the system.
  dimension_type num_pending_rows() const;

  //! Returns the index of the first pending row.
  dimension_type first_pending_row() const;

  //! Returns the value of the sortedness flag.
  bool is_sorted() const;

  //! Sets the index to indicate that the system has no pending rows.
  void unset_pending_rows();

  //! Sets the index of the first pending row to \p i.
  void set_index_first_pending_row(dimension_type i);

  //! Sets the sortedness flag of the system to \p b.
  void set_sorted(bool b);

  //! Makes the system shrink by removing its i-th row.
  /*!
    When \p keep_sorted is \p true and the system is sorted, sortedness will
    be preserved, but this method costs O(n).

    Otherwise, this method just swaps the i-th row with the last and then
    removes it, so it costs O(1).
  */
  void remove_row(dimension_type i, bool keep_sorted = false);

  //! Removes the specified rows. The row ordering of remaining rows is
  //! preserved.
  /*!
    \param indexes specifies a list of row indexes.
                   It must be sorted.
  */
  void remove_rows(const std::vector<dimension_type>& indexes);

  //! Makes the system shrink by removing the rows in [first,last).
  /*!
    When \p keep_sorted is \p true and the system is sorted, sortedness will
    be preserved, but this method costs O(num_rows()).

    Otherwise, this method just swaps the rows with the last ones and then
    removes them, so it costs O(last - first).
  */
  void remove_rows(dimension_type first, dimension_type last,
                   bool keep_sorted = false);

  //! Makes the system shrink by removing its \p n trailing rows.
  void remove_trailing_rows(dimension_type n);

  //! Removes all the specified dimensions from the constraint system.
  /*!
    The space dimension of the variable with the highest space
    dimension in \p vars must be at most the space dimension
    of \p this.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  //! Shift by \p n positions the coefficients of variables, starting from
  //! the coefficient of \p v. This increases the space dimension by \p n.
  void shift_space_dimensions(Variable v, dimension_type n);

  //! Permutes the space dimensions of the matrix.
  /*
    \param cycle
    A vector representing a cycle of the permutation according to which the
    columns must be rearranged.

    The \p cycle vector represents a cycle of a permutation of space
    dimensions.
    For example, the permutation
    \f$ \{ x_1 \mapsto x_2, x_2 \mapsto x_3, x_3 \mapsto x_1 \}\f$ can be
    represented by the vector containing \f$ x_1, x_2, x_3 \f$.
  */
  void permute_space_dimensions(const std::vector<Variable>& cycle);

  //! Swaps the coefficients of the variables \p v1 and \p v2 .
  void swap_space_dimensions(Variable v1, Variable v2);

  bool has_no_rows() const;

  //! Strongly normalizes the system.
  void strong_normalize();

  /*! \brief
    Sorts the non-pending rows (in growing order) and eliminates
    duplicated ones.
  */
  void sort_rows();

  /*! \brief
    Adds the given row to the pending part of the system, stealing its
    contents and automatically resizing the system or the row, if needed.
  */
  void insert_pending(Constraint& r, Recycle_Input);

  //! Adds the rows of `y' to the pending part of `*this', stealing them from
  //! `y'.
  void insert_pending(Constraint_System& r, Recycle_Input);

  /*! \brief
    Adds \p r to the system, stealing its contents and
    automatically resizing the system or the row, if needed.
  */
  void insert(Constraint& r, Recycle_Input);

  //! Adds to \p *this a the rows of `y', stealing them from `y'.
  /*!
    It is assumed that \p *this has no pending rows.
  */
  void insert(Constraint_System& r, Recycle_Input);

  //! Adds a copy of the rows of `y' to the pending part of `*this'.
  void insert_pending(const Constraint_System& r);

  /*! \brief
    Assigns to \p *this the result of merging its rows with
    those of \p y, obtaining a sorted system.

    Duplicated rows will occur only once in the result.
    On entry, both systems are assumed to be sorted and have
    no pending rows.
  */
  void merge_rows_assign(const Constraint_System& y);

  //! Adds to \p *this a copy of  the rows of \p y.
  /*!
    It is assumed that \p *this has no pending rows.
  */
  void insert(const Constraint_System& y);

  //! Marks the epsilon dimension as a standard dimension.
  /*!
    The system topology is changed to <CODE>NOT_NECESSARILY_CLOSED</CODE>, and
    the number of space dimensions is increased by 1.
  */
  void mark_as_necessarily_closed();

  //! Marks the last dimension as the epsilon dimension.
  /*!
    The system topology is changed to <CODE>NECESSARILY_CLOSED</CODE>, and
    the number of space dimensions is decreased by 1.
  */
  void mark_as_not_necessarily_closed();

  //! Minimizes the subsystem of equations contained in \p *this.
  /*!
    This method works only on the equalities of the system:
    the system is required to be partially sorted, so that
    all the equalities are grouped at its top; it is assumed that
    the number of equalities is exactly \p n_lines_or_equalities.
    The method finds a minimal system for the equalities and
    returns its rank, i.e., the number of linearly independent equalities.
    The result is an upper triangular subsystem of equalities:
    for each equality, the pivot is chosen starting from
    the right-most columns.
  */
  dimension_type gauss(dimension_type n_lines_or_equalities);

  /*! \brief
    Back-substitutes the coefficients to reduce
    the complexity of the system.

    Takes an upper triangular system having \p n_lines_or_equalities rows.
    For each row, starting from the one having the minimum number of
    coefficients different from zero, computes the expression of an element
    as a function of the remaining ones and then substitutes this expression
    in all the other rows.
  */
  void back_substitute(dimension_type n_lines_or_equalities);

  //! Full assignment operator: pending rows are copied as pending.
  void assign_with_pending(const Constraint_System& y);

  /*! \brief
    Sorts the pending rows and eliminates those that also occur
    in the non-pending part of the system.
  */
  void sort_pending_and_remove_duplicates();

  /*! \brief
    Sorts the system, removing duplicates, keeping the saturation
    matrix consistent.

    \param sat
    Bit matrix with rows corresponding to the rows of \p *this.
  */
  void sort_and_remove_with_sat(Bit_Matrix& sat);

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is sorted,
    without checking for duplicates.
  */
  bool check_sorted() const;

  /*! \brief
    Returns the number of rows in the system
    that represent either lines or equalities.
  */
  dimension_type num_lines_or_equalities() const;

  //! Adds \p n rows and space dimensions to the system.
  /*!
    \param n
    The number of rows and space dimensions to be added: must be strictly
    positive.

    Turns the system \f$M \in \Rset^r \times \Rset^c\f$ into
    the system \f$N \in \Rset^{r+n} \times \Rset^{c+n}\f$
    such that
    \f$N = \bigl(\genfrac{}{}{0pt}{}{0}{M}\genfrac{}{}{0pt}{}{J}{o}\bigr)\f$,
    where \f$J\f$ is the specular image
    of the \f$n \times n\f$ identity matrix.
  */
  void add_universe_rows_and_space_dimensions(dimension_type n);

  friend class Polyhedron;
  friend class Termination_Helpers;
};

//! An iterator over a system of constraints.
/*! \ingroup PPL_CXX_interface
  A const_iterator is used to provide read-only access
  to each constraint contained in a Constraint_System object.

  \par Example
  The following code prints the system of constraints
  defining the polyhedron <CODE>ph</CODE>:
  \code
const Constraint_System& cs = ph.constraints();
for (Constraint_System::const_iterator i = cs.begin(),
        cs_end = cs.end(); i != cs_end; ++i)
  cout << *i << endl;
  \endcode
*/
// NOTE: This is not an inner class of Constraint_System, so Constraint can
// declare that this class is his friend without including this file
// (the .types.hh file suffices).
class Parma_Polyhedra_Library::Constraint_System_const_iterator
  : public std::iterator<std::forward_iterator_tag,
                         Constraint,
                         std::ptrdiff_t,
                         const Constraint*,
                         const Constraint&> {
public:
  //! Default constructor.
  Constraint_System_const_iterator();

  //! Ordinary copy constructor.
  Constraint_System_const_iterator(const Constraint_System_const_iterator& y);

  //! Destructor.
  ~Constraint_System_const_iterator();

  //! Assignment operator.
  Constraint_System_const_iterator&
  operator=(const Constraint_System_const_iterator& y);

  //! Dereference operator.
  const Constraint& operator*() const;

  //! Indirect member selector.
  const Constraint* operator->() const;

  //! Prefix increment operator.
  Constraint_System_const_iterator& operator++();

  //! Postfix increment operator.
  Constraint_System_const_iterator operator++(int);

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this and \p y are identical.
  */
  bool operator==(const Constraint_System_const_iterator& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this and \p y are different.
  */
  bool operator!=(const Constraint_System_const_iterator& y) const;

private:
  friend class Constraint_System;

  //! The const iterator over the matrix of constraints.
  Linear_System<Constraint>::const_iterator i;

  //! A const pointer to the matrix of constraints.
  const Linear_System<Constraint>* csp;

  //! Constructor.
  Constraint_System_const_iterator(const Linear_System<Constraint>
                                   ::const_iterator& iter,
                                   const Constraint_System& cs);

  //! \p *this skips to the next non-trivial constraint.
  void skip_forward();
};

namespace Parma_Polyhedra_Library {

namespace Implementation {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Helper returning number of constraints in system.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
dimension_type
num_constraints(const Constraint_System& cs);

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

// Constraint_System_inlines.hh is not included here on purpose.

/* Automatically generated from PPL source file ../src/Constraint_System_inlines.hh line 1. */
/* Constraint_System class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Constraint_System_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

inline
Constraint_System::Constraint_System(Representation r)
  : sys(NECESSARILY_CLOSED, r) {
}

inline
Constraint_System::Constraint_System(const Constraint& c, Representation r)
  : sys(c.topology(), r) {
  sys.insert(c);
}

inline
Constraint_System::Constraint_System(const Constraint_System& cs)
  : sys(cs.sys) {
}

inline
Constraint_System::Constraint_System(const Constraint_System& cs,
                                     Representation r)
  : sys(cs.sys, r) {
}

inline
Constraint_System::Constraint_System(const Topology topol, Representation r)
  : sys(topol, r) {
}

inline
Constraint_System::Constraint_System(const Topology topol,
                                     const dimension_type space_dim,
                                     Representation r)
  : sys(topol, space_dim, r) {
}

inline
Constraint_System::~Constraint_System() {
}

inline Constraint_System&
Constraint_System::operator=(const Constraint_System& y) {
  Constraint_System tmp = y;
  swap(*this, tmp);
  return *this;
}

inline const Constraint&
Constraint_System::operator[](const dimension_type k) const {
  return sys[k];
}

inline Representation
Constraint_System::representation() const {
  return sys.representation();
}

inline void
Constraint_System::set_representation(Representation r) {
  sys.set_representation(r);
}

inline dimension_type
Constraint_System::max_space_dimension() {
  return Linear_System<Constraint>::max_space_dimension();
}

inline dimension_type
Constraint_System::space_dimension() const {
  return sys.space_dimension();
}

inline void
Constraint_System::set_space_dimension(dimension_type space_dim) {
  return sys.set_space_dimension(space_dim);
}

inline void
Constraint_System::clear() {
  sys.clear();
}

inline const Constraint_System&
Constraint_System::zero_dim_empty() {
  PPL_ASSERT(zero_dim_empty_p != 0);
  return *zero_dim_empty_p;
}

inline
Constraint_System_const_iterator::Constraint_System_const_iterator()
  : i(), csp(0) {
}

inline
Constraint_System_const_iterator::Constraint_System_const_iterator(const Constraint_System_const_iterator& y)
  : i(y.i), csp(y.csp) {
}

inline
Constraint_System_const_iterator::~Constraint_System_const_iterator() {
}

inline Constraint_System_const_iterator&
Constraint_System_const_iterator::operator=(const Constraint_System_const_iterator& y) {
  i = y.i;
  csp = y.csp;
  return *this;
}

inline const Constraint&
Constraint_System_const_iterator::operator*() const {
  return *i;
}

inline const Constraint*
Constraint_System_const_iterator::operator->() const {
  return i.operator->();
}

inline Constraint_System_const_iterator&
Constraint_System_const_iterator::operator++() {
  ++i;
  skip_forward();
  return *this;
}

inline Constraint_System_const_iterator
Constraint_System_const_iterator::operator++(int) {
  const Constraint_System_const_iterator tmp = *this;
  operator++();
  return tmp;
}

inline bool
Constraint_System_const_iterator::operator==(const Constraint_System_const_iterator& y) const {
  return i == y.i;
}

inline bool
Constraint_System_const_iterator::operator!=(const Constraint_System_const_iterator& y) const {
  return i != y.i;
}

inline
Constraint_System_const_iterator::
Constraint_System_const_iterator(const Linear_System<Constraint>::const_iterator& iter,
               const Constraint_System& cs)
  : i(iter), csp(&cs.sys) {
}

inline Constraint_System_const_iterator
Constraint_System::begin() const {
  const_iterator i(sys.begin(), *this);
  i.skip_forward();
  return i;
}

inline Constraint_System_const_iterator
Constraint_System::end() const {
  const Constraint_System_const_iterator i(sys.end(), *this);
  return i;
}

inline bool
Constraint_System::empty() const {
  return begin() == end();
}

inline void
Constraint_System::add_low_level_constraints() {
  if (sys.is_necessarily_closed()) {
    // The positivity constraint.
    insert(Constraint::zero_dim_positivity());
  }
  else {
    // Add the epsilon constraints.
    insert(Constraint::epsilon_leq_one());
    insert(Constraint::epsilon_geq_zero());
  }
}

inline void
Constraint_System::m_swap(Constraint_System& y) {
  swap(sys, y.sys);
}

inline memory_size_type
Constraint_System::external_memory_in_bytes() const {
  return sys.external_memory_in_bytes();
}

inline memory_size_type
Constraint_System::total_memory_in_bytes() const {
  return external_memory_in_bytes() + sizeof(*this);
}

inline void
Constraint_System::simplify() {
  sys.simplify();
}

inline Topology
Constraint_System::topology() const {
  return sys.topology();
}

inline dimension_type
Constraint_System::num_rows() const {
  return sys.num_rows();
}

inline bool
Constraint_System::is_necessarily_closed() const {
  return sys.is_necessarily_closed();
}

inline dimension_type
Constraint_System::num_pending_rows() const {
  return sys.num_pending_rows();
}

inline dimension_type
Constraint_System::first_pending_row() const {
  return sys.first_pending_row();
}

inline bool
Constraint_System::is_sorted() const {
  return sys.is_sorted();
}

inline void
Constraint_System::unset_pending_rows() {
  sys.unset_pending_rows();
}

inline void
Constraint_System::set_index_first_pending_row(dimension_type i) {
  sys.set_index_first_pending_row(i);
}

inline void
Constraint_System::set_sorted(bool b) {
  sys.set_sorted(b);
}

inline void
Constraint_System::remove_row(dimension_type i, bool keep_sorted) {
  sys.remove_row(i, keep_sorted);
}

inline void
Constraint_System::remove_rows(dimension_type first, dimension_type last,
                               bool keep_sorted) {
  sys.remove_rows(first, last, keep_sorted);
}

inline void
Constraint_System::remove_rows(const std::vector<dimension_type>& indexes) {
  sys.remove_rows(indexes);
}

inline void
Constraint_System::remove_trailing_rows(dimension_type n) {
  sys.remove_trailing_rows(n);
}

inline void
Constraint_System
::remove_space_dimensions(const Variables_Set& vars) {
  sys.remove_space_dimensions(vars);
}

inline void
Constraint_System
::shift_space_dimensions(Variable v, dimension_type n) {
  sys.shift_space_dimensions(v, n);
}

inline void
Constraint_System
::permute_space_dimensions(const std::vector<Variable>& cycle) {
  sys.permute_space_dimensions(cycle);
}

inline void
Constraint_System
::swap_space_dimensions(Variable v1, Variable v2) {
  sys.swap_space_dimensions(v1, v2);
}

inline bool
Constraint_System::has_no_rows() const {
  return sys.has_no_rows();
}

inline void
Constraint_System::strong_normalize() {
  sys.strong_normalize();
}

inline void
Constraint_System::sort_rows() {
  sys.sort_rows();
}

inline void
Constraint_System::insert_pending(Constraint_System& r, Recycle_Input) {
  sys.insert_pending(r.sys, Recycle_Input());
}

inline void
Constraint_System::insert(Constraint_System& r, Recycle_Input) {
  sys.insert(r.sys, Recycle_Input());
}

inline void
Constraint_System::insert_pending(const Constraint_System& r) {
  sys.insert_pending(r.sys);
}

inline void
Constraint_System::merge_rows_assign(const Constraint_System& y) {
  sys.merge_rows_assign(y.sys);
}

inline void
Constraint_System::insert(const Constraint_System& y) {
  sys.insert(y.sys);
}

inline void
Constraint_System::mark_as_necessarily_closed() {
  sys.mark_as_necessarily_closed();
}

inline void
Constraint_System::mark_as_not_necessarily_closed() {
  sys.mark_as_not_necessarily_closed();
}

inline dimension_type
Constraint_System::gauss(dimension_type n_lines_or_equalities) {
  return sys.gauss(n_lines_or_equalities);
}

inline void
Constraint_System::back_substitute(dimension_type n_lines_or_equalities) {
  sys.back_substitute(n_lines_or_equalities);
}

inline void
Constraint_System::assign_with_pending(const Constraint_System& y) {
  sys.assign_with_pending(y.sys);
}

inline void
Constraint_System::sort_pending_and_remove_duplicates() {
  sys.sort_pending_and_remove_duplicates();
}

inline void
Constraint_System::sort_and_remove_with_sat(Bit_Matrix& sat) {
  sys.sort_and_remove_with_sat(sat);
}

inline bool
Constraint_System::check_sorted() const {
  return sys.check_sorted();
}

inline dimension_type
Constraint_System::num_lines_or_equalities() const {
  return sys.num_lines_or_equalities();
}

inline void
Constraint_System::add_universe_rows_and_space_dimensions(dimension_type n) {
  sys.add_universe_rows_and_space_dimensions(n);
}

inline bool
operator==(const Constraint_System& x, const Constraint_System& y) {
  return x.sys == y.sys;
}

inline bool
operator!=(const Constraint_System& x, const Constraint_System& y) {
  return !(x == y);
}

/*! \relates Constraint_System */
inline void
swap(Constraint_System& x, Constraint_System& y) {
  x.m_swap(y);
}

namespace Implementation {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Parma_Polyhedra_Library::Constraint_System */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
inline dimension_type
num_constraints(const Constraint_System& cs) {
  return static_cast<dimension_type>(std::distance(cs.begin(), cs.end()));
}

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Congruence_System_defs.hh line 1. */
/* Congruence_System class declaration.
*/


/* Automatically generated from PPL source file ../src/Congruence_System_defs.hh line 35. */
#include <iosfwd>
#include <cstddef>

namespace Parma_Polyhedra_Library {

/*! \relates Congruence_System */
bool
operator==(const Congruence_System& x, const Congruence_System& y);

}

//! A system of congruences.
/*! \ingroup PPL_CXX_interface
    An object of the class Congruence_System is a system of congruences,
    i.e., a multiset of objects of the class Congruence.
    When inserting congruences in a system, space dimensions are
    automatically adjusted so that all the congruences in the system
    are defined on the same vector space.

    \par
    In all the examples it is assumed that variables
    <CODE>x</CODE> and <CODE>y</CODE> are defined as follows:
    \code
  Variable x(0);
  Variable y(1);
    \endcode

    \par Example 1
    The following code builds a system of congruences corresponding to
    an integer grid in \f$\Rset^2\f$:
    \code
  Congruence_System cgs;
  cgs.insert(x %= 0);
  cgs.insert(y %= 0);
    \endcode
    Note that:
    the congruence system is created with space dimension zero;
    the first and second congruence insertions increase the space
    dimension to \f$1\f$ and \f$2\f$, respectively.

    \par Example 2
    By adding to the congruence system of the previous example,
    the congruence \f$x + y = 1 \pmod{2}\f$:
    \code
  cgs.insert((x + y %= 1) / 2);
    \endcode
    we obtain the grid containing just those integral
    points where the sum of the \p x and \p y values is odd.

    \par Example 3
    The following code builds a system of congruences corresponding to
    the grid in \f$\Zset^2\f$ containing just the integral points on
    the \p x axis:
    \code
  Congruence_System cgs;
  cgs.insert(x %= 0);
  cgs.insert((y %= 0) / 0);
    \endcode

    \note
    After inserting a multiset of congruences in a congruence system,
    there are no guarantees that an <EM>exact</EM> copy of them
    can be retrieved:
    in general, only an <EM>equivalent</EM> congruence system
    will be available, where original congruences may have been
    reordered, removed (if they are trivial, duplicate or
    implied by other congruences), linearly combined, etc.
*/
class Parma_Polyhedra_Library::Congruence_System {
public:

  typedef Congruence row_type;

  static const Representation default_representation = SPARSE;

  //! Default constructor: builds an empty system of congruences.
  explicit Congruence_System(Representation r = default_representation);

  //! Builds an empty (i.e. zero rows) system of dimension \p d.
  explicit Congruence_System(dimension_type d,
                             Representation r = default_representation);

  //! Builds the singleton system containing only congruence \p cg.
  explicit Congruence_System(const Congruence& cg,
                             Representation r = default_representation);

  /*! \brief
    If \p c represents the constraint \f$ e_1 = e_2 \f$, builds the
    singleton system containing only constraint \f$ e_1 = e_2
    \pmod{0}\f$.

    \exception std::invalid_argument
    Thrown if \p c is not an equality constraint.
  */
  explicit Congruence_System(const Constraint& c,
                             Representation r = default_representation);

  //! Builds a system containing copies of any equalities in \p cs.
  explicit Congruence_System(const Constraint_System& cs,
                             Representation r = default_representation);

  //! Ordinary copy constructor.
  /*!
    \note
    The new Congruence_System will have the same Representation as `cgs'
    so that it's indistinguishable from `cgs'.
  */
  Congruence_System(const Congruence_System& cgs);

  //! Copy constructor with specified representation.
  Congruence_System(const Congruence_System& cgs, Representation r);

  //! Destructor.
  ~Congruence_System();

  //! Assignment operator.
  Congruence_System& operator=(const Congruence_System& y);

  //! Returns the current representation of *this.
  Representation representation() const;

  //! Converts *this to the specified representation.
  void set_representation(Representation r);

  //! Returns the maximum space dimension a Congruence_System can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is exactly equal
    to \p y.
  */
  bool is_equal_to(const Congruence_System& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this contains one or
    more linear equalities.
  */
  bool has_linear_equalities() const;

  //! Removes all the congruences and sets the space dimension to 0.
  void clear();

  /*! \brief
    Inserts in \p *this a copy of the congruence \p cg, increasing the
    number of space dimensions if needed.

    The copy of \p cg will be strongly normalized after being
    inserted.
  */
  void insert(const Congruence& cg);

  /*! \brief
    Inserts in \p *this the congruence \p cg, stealing its contents and
    increasing the number of space dimensions if needed.

    \p cg will be strongly normalized.
  */
  void insert(Congruence& cg, Recycle_Input);

  /*! \brief
    Inserts in \p *this a copy of the equality constraint \p c, seen
    as a modulo 0 congruence, increasing the number of space
    dimensions if needed.

    The modulo 0 congruence will be strongly normalized after being
    inserted.

    \exception std::invalid_argument
    Thrown if \p c is a relational constraint.
  */
  void insert(const Constraint& c);

  // TODO: Consider adding a insert(cg, Recycle_Input).

  /*! \brief
    Inserts in \p *this a copy of the congruences in \p y,
    increasing the number of space dimensions if needed.

    The inserted copies will be strongly normalized.
  */
  void insert(const Congruence_System& y);

  /*! \brief
    Inserts into \p *this the congruences in \p cgs, increasing the
    number of space dimensions if needed.
  */
  void insert(Congruence_System& cgs, Recycle_Input);

  //! Initializes the class.
  static void initialize();

  //! Finalizes the class.
  static void finalize();

  //! Returns the system containing only Congruence::zero_dim_false().
  static const Congruence_System& zero_dim_empty();

  //! An iterator over a system of congruences.
  /*! \ingroup PPL_CXX_interface
    A const_iterator is used to provide read-only access
    to each congruence contained in an object of Congruence_System.

    \par Example
    The following code prints the system of congruences
    defining the grid <CODE>gr</CODE>:
    \code
  const Congruence_System& cgs = gr.congruences();
  for (Congruence_System::const_iterator i = cgs.begin(),
         cgs_end = cgs.end(); i != cgs_end; ++i)
    cout << *i << endl;
    \endcode
  */
  class const_iterator
    : public std::iterator<std::forward_iterator_tag,
                           Congruence,
                           std::ptrdiff_t,
                           const Congruence*,
                           const Congruence&> {
  public:
    //! Default constructor.
    const_iterator();

    //! Ordinary copy constructor.
    const_iterator(const const_iterator& y);

    //! Destructor.
    ~const_iterator();

    //! Assignment operator.
    const_iterator& operator=(const const_iterator& y);

    //! Dereference operator.
    const Congruence& operator*() const;

    //! Indirect member selector.
    const Congruence* operator->() const;

    //! Prefix increment operator.
    const_iterator& operator++();

    //! Postfix increment operator.
    const_iterator operator++(int);

    /*! \brief
      Returns <CODE>true</CODE> if and only if \p *this and \p y are
      identical.
    */
    bool operator==(const const_iterator& y) const;

    /*! \brief
      Returns <CODE>true</CODE> if and only if \p *this and \p y are
      different.
    */
    bool operator!=(const const_iterator& y) const;

  private:
    friend class Congruence_System;

    //! The const iterator over the vector of congruences.
    Swapping_Vector<Congruence>::const_iterator i;

    //! A const pointer to the vector of congruences.
    const Swapping_Vector<Congruence>* csp;

    //! Constructor.
    const_iterator(const Swapping_Vector<Congruence>::const_iterator& iter,
                   const Congruence_System& cgs);

    //! \p *this skips to the next non-trivial congruence.
    void skip_forward();
  };

  //! Returns <CODE>true</CODE> if and only if \p *this has no congruences.
  bool empty() const;

  /*! \brief
    Returns the const_iterator pointing to the first congruence, if \p
    *this is not empty; otherwise, returns the past-the-end
    const_iterator.
  */
  const_iterator begin() const;

  //! Returns the past-the-end const_iterator.
  const_iterator end() const;

  //! Checks if all the invariants are satisfied.
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*!
    Returns <CODE>true</CODE> if and only if all rows have space dimension
    space_dimension_, each row in the system is a valid Congruence and the
    space dimension is consistent with the number of congruences.
  */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Returns the number of equalities.
  dimension_type num_equalities() const;

  //! Returns the number of proper congruences.
  dimension_type num_proper_congruences() const;

  //! Swaps \p *this with \p y.
  void m_swap(Congruence_System& y);

  /*! \brief
    Adds \p dims rows and \p dims space dimensions to the matrix,
    initializing the added rows as in the unit congruence system.

    \param dims
    The number of rows and space dimensions to be added: must be strictly
    positive.

    Turns the \f$r \times c\f$ matrix \f$A\f$ into the \f$(r+dims) \times
    (c+dims)\f$ matrix
    \f$\bigl(\genfrac{}{}{0pt}{}{0}{A} \genfrac{}{}{0pt}{}{B}{A}\bigr)\f$
    where \f$B\f$ is the \f$dims \times dims\f$ unit matrix of the form
    \f$\bigl(\genfrac{}{}{0pt}{}{0}{1} \genfrac{}{}{0pt}{}{1}{0}\bigr)\f$.
    The matrix is expanded avoiding reallocation whenever possible.
  */
  void add_unit_rows_and_space_dimensions(dimension_type dims);

  //! Permutes the space dimensions of the system.
  /*!
    \param cycle
    A vector representing a cycle of the permutation according to which the
    columns must be rearranged.

    The \p cycle vector represents a cycle of a permutation of space
    dimensions.
    For example, the permutation
    \f$ \{ x_1 \mapsto x_2, x_2 \mapsto x_3, x_3 \mapsto x_1 \}\f$ can be
    represented by the vector containing \f$ x_1, x_2, x_3 \f$.
  */
  void permute_space_dimensions(const std::vector<Variable>& cycle);

  //! Swaps the columns having indexes \p i and \p j.
  void swap_space_dimensions(Variable v1, Variable v2);

  //! Sets the number of space dimensions to \p new_space_dim.
  /*!
    If \p new_space_dim is lower than the current space dimension, the
    coefficients referring to the removed space dimensions are lost.
  */
  bool set_space_dimension(dimension_type new_space_dim);

  // Note: the following method is protected to allow tests/Grid/congruences2
  // to call it using a derived class.
protected:
  //! Returns <CODE>true</CODE> if \p g satisfies all the congruences.
  bool satisfies_all_congruences(const Grid_Generator& g) const;

private:
  //! Returns the number of rows in the system.
  dimension_type num_rows() const;

  //! Returns \c true if num_rows()==0.
  bool has_no_rows() const;

  //! Returns a constant reference to the \p k- th congruence of the system.
  const Congruence& operator[](dimension_type k) const;

  //! Adjusts all expressions to have the same moduli.
  void normalize_moduli();

  /*! \brief
    Substitutes a given column of coefficients by a given affine
    expression.

    \param v
    Index of the column to which the affine transformation is
    substituted;

    \param expr
    The numerator of the affine transformation:
    \f$\sum_{i = 0}^{n - 1} a_i x_i + b\f$;

    \param denominator
    The denominator of the affine transformation.

    We allow affine transformations (see the Section \ref
    rational_grid_operations) to have rational
    coefficients. Since the coefficients of linear expressions are
    integers we also provide an integer \p denominator that will
    be used as denominator of the affine transformation.  The
    denominator is required to be a positive integer and its default value
    is 1.

    The affine transformation substitutes the matrix of congruences
    by a new matrix whose elements \f${a'}_{ij}\f$ are built from
    the old one \f$a_{ij}\f$ as follows:
    \f[
      {a'}_{ij} =
        \begin{cases}
          a_{ij} * \mathrm{denominator} + a_{iv} * \mathrm{expr}[j]
            \quad \text{for } j \neq v; \\
          \mathrm{expr}[v] * a_{iv}
            \quad \text{for } j = v.
        \end{cases}
    \f]

    \p expr is a constant parameter and unaltered by this computation.
  */
  void affine_preimage(Variable v,
                       const Linear_Expression& expr,
                       Coefficient_traits::const_reference denominator);

  // TODO: Consider making this private.
  /*! \brief
    Concatenates copies of the congruences from \p y onto \p *this.

    \param y
    The congruence system to append to \p this.  The number of rows in
    \p y must be strictly positive.

    The matrix for the new system of congruences is obtained by
    leaving the old system in the upper left-hand side and placing the
    congruences of \p y in the lower right-hand side, and padding
    with zeroes.
  */
  void concatenate(const Congruence_System& y);

  /*! \brief
    Inserts in \p *this the congruence \p cg, stealing its contents and
    increasing the number of space dimensions if needed.

    This method inserts \p cg in the given form, instead of first strong
    normalizing \p cg as \ref insert would do.
  */
  void insert_verbatim(Congruence& cg, Recycle_Input);

  //! Makes the system shrink by removing the rows in [first,last).
  /*!
    If \p keep_sorted is <CODE>true</CODE>, the ordering of the remaining rows
    will be preserved.
  */
  void remove_rows(dimension_type first, dimension_type last,
                   bool keep_sorted);

  void remove_trailing_rows(dimension_type n);

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the singleton system containing only Congruence::zero_dim_false().
  */
  static const Congruence_System* zero_dim_empty_p;

  Swapping_Vector<Congruence> rows;

  dimension_type space_dimension_;

  Representation representation_;

  /*! \brief
    Returns <CODE>true</CODE> if and only if any of the dimensions in
    \p *this is free of constraint.

    Any equality or proper congruence affecting a dimension constrains
    that dimension.

    This method assumes the system is in minimal form.
  */
  bool has_a_free_dimension() const;

  friend class Grid;

  friend bool
  operator==(const Congruence_System& x, const Congruence_System& y);
};

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*!
  \relates Parma_Polyhedra_Library::Congruence_System
  Writes <CODE>true</CODE> if \p cgs is empty.  Otherwise, writes on
  \p s the congruences of \p cgs, all in one row and separated by ", ".
*/
std::ostream&
operator<<(std::ostream& s, const Congruence_System& cgs);

} // namespace IO_Operators

/*! \relates Congruence_System */
void
swap(Congruence_System& x, Congruence_System& y);

} // namespace Parma_Polyhedra_Library

// Congruence_System_inlines.hh is not included here on purpose.

/* Automatically generated from PPL source file ../src/Congruence_System_inlines.hh line 1. */
/* Congruence_System class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Congruence_System_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

inline const Congruence&
Congruence_System::operator[](const dimension_type k) const {
  return rows[k];
}

inline dimension_type
Congruence_System::num_rows() const {
  return rows.size();
}

inline bool
Congruence_System::has_no_rows() const {
  return num_rows() == 0;
}

inline void
Congruence_System::remove_trailing_rows(dimension_type n) {
  PPL_ASSERT(num_rows() >= n);
  rows.resize(num_rows() - n);
}

inline void
Congruence_System::insert(const Congruence& cg) {
  Congruence tmp = cg;
  insert(tmp, Recycle_Input());
}

inline void
Congruence_System::insert(Congruence& cg, Recycle_Input) {
  PPL_ASSERT(cg.OK());
  cg.strong_normalize();
  PPL_ASSERT(cg.OK());
  insert_verbatim(cg, Recycle_Input());
  PPL_ASSERT(OK());
}

inline
Congruence_System::Congruence_System(Representation r)
  : rows(),
    space_dimension_(0),
    representation_(r) {
}

inline
Congruence_System::Congruence_System(const Congruence& cg, Representation r)
  : rows(),
    space_dimension_(0),
    representation_(r) {
  insert(cg);
}

inline
Congruence_System::Congruence_System(const Constraint& c, Representation r)
  : rows(),
    space_dimension_(0),
    representation_(r) {
  insert(c);
}

inline
Congruence_System::Congruence_System(const Congruence_System& cgs)
  : rows(cgs.rows),
    space_dimension_(cgs.space_dimension_),
    representation_(cgs.representation_) {
}

inline
Congruence_System::Congruence_System(const Congruence_System& cgs,
                                     Representation r)
  : rows(cgs.rows),
    space_dimension_(cgs.space_dimension_),
    representation_(r) {
  if (cgs.representation() != r) {
    for (dimension_type i = 0; i < num_rows(); ++i) {
      rows[i].set_representation(representation());
    }
  }
}

inline
Congruence_System::Congruence_System(const dimension_type d, Representation r)
  : rows(),
    space_dimension_(d),
    representation_(r) {
}

inline
Congruence_System::~Congruence_System() {
}

inline Congruence_System&
Congruence_System::operator=(const Congruence_System& y) {
  Congruence_System tmp = y;
  swap(*this, tmp);
  return *this;
}

inline Representation
Congruence_System::representation() const {
  return representation_;
}

inline void
Congruence_System::set_representation(Representation r) {
  if (representation_ == r) {
    return;
  }
  representation_ = r;
  for (dimension_type i = 0; i < num_rows(); ++i) {
    rows[i].set_representation(r);
  }
  PPL_ASSERT(OK());
}

inline dimension_type
Congruence_System::max_space_dimension() {
  return Congruence::max_space_dimension();
}

inline dimension_type
Congruence_System::space_dimension() const {
  return space_dimension_;
}

inline void
Congruence_System::clear() {
  rows.clear();
  space_dimension_ = 0;
}

inline const Congruence_System&
Congruence_System::zero_dim_empty() {
  PPL_ASSERT(zero_dim_empty_p != 0);
  return *zero_dim_empty_p;
}

inline
Congruence_System::const_iterator::const_iterator()
  : i(), csp(0) {
}

inline
Congruence_System::const_iterator::const_iterator(const const_iterator& y)
  : i(y.i), csp(y.csp) {
}

inline
Congruence_System::const_iterator::~const_iterator() {
}

inline Congruence_System::const_iterator&
Congruence_System::const_iterator::operator=(const const_iterator& y) {
  i = y.i;
  csp = y.csp;
  return *this;
}

inline const Congruence&
Congruence_System::const_iterator::operator*() const {
  return *i;
}

inline const Congruence*
Congruence_System::const_iterator::operator->() const {
  return i.operator->();
}

inline Congruence_System::const_iterator&
Congruence_System::const_iterator::operator++() {
  ++i;
  skip_forward();
  return *this;
}

inline Congruence_System::const_iterator
Congruence_System::const_iterator::operator++(int) {
  const const_iterator tmp = *this;
  operator++();
  return tmp;
}

inline bool
Congruence_System::const_iterator::operator==(const const_iterator& y) const {
  return i == y.i;
}

inline bool
Congruence_System::const_iterator::operator!=(const const_iterator& y) const {
  return i != y.i;
}

inline
Congruence_System::const_iterator::
const_iterator(const Swapping_Vector<Congruence>::const_iterator& iter,
               const Congruence_System& cgs)
  : i(iter), csp(&cgs.rows) {
}

inline Congruence_System::const_iterator
Congruence_System::begin() const {
  const_iterator i(rows.begin(), *this);
  i.skip_forward();
  return i;
}

inline Congruence_System::const_iterator
Congruence_System::end() const {
  const const_iterator i(rows.end(), *this);
  return i;
}

inline bool
Congruence_System::empty() const {
  return begin() == end();
}

inline void
Congruence_System::m_swap(Congruence_System& y) {
  using std::swap;
  swap(rows, y.rows);
  swap(space_dimension_, y.space_dimension_);
  swap(representation_, y.representation_);
  PPL_ASSERT(OK());
  PPL_ASSERT(y.OK());
}

inline memory_size_type
Congruence_System::external_memory_in_bytes() const {
  return rows.external_memory_in_bytes();
}

inline memory_size_type
Congruence_System::total_memory_in_bytes() const {
  return rows.external_memory_in_bytes() + sizeof(*this);
}

/*! \relates Congruence_System */
inline void
swap(Congruence_System& x, Congruence_System& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Box_inlines.hh line 33. */

namespace Parma_Polyhedra_Library {

template <typename ITV>
inline bool
Box<ITV>::marked_empty() const {
  return status.test_empty_up_to_date() && status.test_empty();
}

template <typename ITV>
inline void
Box<ITV>::set_empty() {
  status.set_empty();
  status.set_empty_up_to_date();
}

template <typename ITV>
inline void
Box<ITV>::set_nonempty() {
  status.reset_empty();
  status.set_empty_up_to_date();
}

template <typename ITV>
inline void
Box<ITV>::set_empty_up_to_date() {
  status.set_empty_up_to_date();
}

template <typename ITV>
inline void
Box<ITV>::reset_empty_up_to_date() {
  return status.reset_empty_up_to_date();
}

template <typename ITV>
inline
Box<ITV>::Box(const Box& y, Complexity_Class)
  : seq(y.seq), status(y.status) {
}

template <typename ITV>
inline Box<ITV>&
Box<ITV>::operator=(const Box& y) {
  seq = y.seq;
  status = y.status;
  return *this;
}

template <typename ITV>
inline void
Box<ITV>::m_swap(Box& y) {
  Box& x = *this;
  using std::swap;
  swap(x.seq, y.seq);
  swap(x.status, y.status);
}

template <typename ITV>
inline
Box<ITV>::Box(const Constraint_System& cs, Recycle_Input) {
  // Recycling is useless: just delegate.
  Box<ITV> tmp(cs);
  this->m_swap(tmp);
}

template <typename ITV>
inline
Box<ITV>::Box(const Generator_System& gs, Recycle_Input) {
  // Recycling is useless: just delegate.
  Box<ITV> tmp(gs);
  this->m_swap(tmp);
}

template <typename ITV>
inline
Box<ITV>::Box(const Congruence_System& cgs, Recycle_Input) {
  // Recycling is useless: just delegate.
  Box<ITV> tmp(cgs);
  this->m_swap(tmp);
}

template <typename ITV>
inline memory_size_type
Box<ITV>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename ITV>
inline dimension_type
Box<ITV>::space_dimension() const {
  return seq.size();
}

template <typename ITV>
inline dimension_type
Box<ITV>::max_space_dimension() {
  // One dimension is reserved to have a value of type dimension_type
  // that does not represent a legal dimension.
  return Sequence().max_size() - 1;
}

template <typename ITV>
inline int32_t
Box<ITV>::hash_code() const {
  return hash_code_from_dimension(space_dimension());
}

template <typename ITV>
inline const ITV&
Box<ITV>::operator[](const dimension_type k) const {
  PPL_ASSERT(k < seq.size());
  return seq[k];
}

template <typename ITV>
inline const ITV&
Box<ITV>::get_interval(const Variable var) const {
  if (space_dimension() < var.space_dimension()) {
    throw_dimension_incompatible("get_interval(v)", "v", var);
  }
  if (is_empty()) {
    static ITV empty_interval(EMPTY);
    return empty_interval;
  }

  return seq[var.id()];
}

template <typename ITV>
inline void
Box<ITV>::set_interval(const Variable var, const ITV& i) {
  const dimension_type space_dim = space_dimension();
  if (space_dim < var.space_dimension()) {
    throw_dimension_incompatible("set_interval(v, i)", "v", var);
  }

  if (is_empty() && space_dim >= 2) {
    // If the box is empty, and has dimension >= 2, setting only one
    // interval will not make it non-empty.
    return;
  }
  seq[var.id()] = i;
  reset_empty_up_to_date();

  PPL_ASSERT(OK());
}

template <typename ITV>
inline bool
Box<ITV>::is_empty() const {
  return marked_empty() || check_empty();
}

template <typename ITV>
inline bool
Box<ITV>::bounds_from_above(const Linear_Expression& expr) const {
  return bounds(expr, true);
}

template <typename ITV>
inline bool
Box<ITV>::bounds_from_below(const Linear_Expression& expr) const {
  return bounds(expr, false);
}

template <typename ITV>
inline bool
Box<ITV>::maximize(const Linear_Expression& expr,
                   Coefficient& sup_n, Coefficient& sup_d,
                   bool& maximum) const {
  return max_min(expr, true, sup_n, sup_d, maximum);
}

template <typename ITV>
inline bool
Box<ITV>::maximize(const Linear_Expression& expr,
                   Coefficient& sup_n, Coefficient& sup_d, bool& maximum,
                   Generator& g) const {
  return max_min(expr, true, sup_n, sup_d, maximum, g);
}

template <typename ITV>
inline bool
Box<ITV>::minimize(const Linear_Expression& expr,
                   Coefficient& inf_n, Coefficient& inf_d,
                   bool& minimum) const {
  return max_min(expr, false, inf_n, inf_d, minimum);
}

template <typename ITV>
inline bool
Box<ITV>::minimize(const Linear_Expression& expr,
                   Coefficient& inf_n, Coefficient& inf_d, bool& minimum,
                   Generator& g) const {
  return max_min(expr, false, inf_n, inf_d, minimum, g);
}

template <typename ITV>
inline bool
Box<ITV>::strictly_contains(const Box& y) const {
  const Box& x = *this;
  return x.contains(y) && !y.contains(x);
}

template <typename ITV>
inline void
Box<ITV>::expand_space_dimension(const Variable var,
                                 const dimension_type m) {
  const dimension_type space_dim = space_dimension();
  // `var' should be one of the dimensions of the vector space.
  if (var.space_dimension() > space_dim) {
    throw_dimension_incompatible("expand_space_dimension(v, m)", "v", var);
  }

  // The space dimension of the resulting Box should not
  // overflow the maximum allowed space dimension.
  if (m > max_space_dimension() - space_dim) {
    throw_invalid_argument("expand_dimension(v, m)",
                           "adding m new space dimensions exceeds "
                           "the maximum allowed space dimension");
  }

  // To expand the space dimension corresponding to variable `var',
  // we append to the box `m' copies of the corresponding interval.
  seq.insert(seq.end(), m, seq[var.id()]);
  PPL_ASSERT(OK());
}

template <typename ITV>
inline bool
operator!=(const Box<ITV>& x, const Box<ITV>& y) {
  return !(x == y);
}

template <typename ITV>
inline bool
Box<ITV>::has_lower_bound(const Variable var,
                          Coefficient& n, Coefficient& d, bool& closed) const {
  // NOTE: assertion !is_empty() would be wrong;
  // see the calls in method Box<ITV>::constraints().
  PPL_ASSERT(!marked_empty());
  const dimension_type k = var.id();
  PPL_ASSERT(k < seq.size());
  const ITV& seq_k = seq[k];

  if (seq_k.lower_is_boundary_infinity()) {
    return false;
  }
  closed = !seq_k.lower_is_open();

  PPL_DIRTY_TEMP(mpq_class, lr);
  assign_r(lr, seq_k.lower(), ROUND_NOT_NEEDED);
  n = lr.get_num();
  d = lr.get_den();

  return true;
}

template <typename ITV>
inline bool
Box<ITV>::has_upper_bound(const Variable var,
                          Coefficient& n, Coefficient& d, bool& closed) const {
  // NOTE: assertion !is_empty() would be wrong;
  // see the calls in method Box<ITV>::constraints().
  PPL_ASSERT(!marked_empty());
  const dimension_type k = var.id();
  PPL_ASSERT(k < seq.size());
  const ITV& seq_k = seq[k];

  if (seq_k.upper_is_boundary_infinity()) {
    return false;
  }

  closed = !seq_k.upper_is_open();

  PPL_DIRTY_TEMP(mpq_class, ur);
  assign_r(ur, seq_k.upper(), ROUND_NOT_NEEDED);
  n = ur.get_num();
  d = ur.get_den();

  return true;
}

template <typename ITV>
inline void
Box<ITV>::add_constraint(const Constraint& c) {
  const dimension_type c_space_dim = c.space_dimension();
  // Dimension-compatibility check.
  if (c_space_dim > space_dimension()) {
    throw_dimension_incompatible("add_constraint(c)", c);
  }

  add_constraint_no_check(c);
}

template <typename ITV>
inline void
Box<ITV>::add_constraints(const Constraint_System& cs) {
  // Dimension-compatibility check.
  if (cs.space_dimension() > space_dimension()) {
    throw_dimension_incompatible("add_constraints(cs)", cs);
  }

  add_constraints_no_check(cs);
}

template <typename T>
inline void
Box<T>::add_recycled_constraints(Constraint_System& cs) {
  add_constraints(cs);
}

template <typename ITV>
inline void
Box<ITV>::add_congruence(const Congruence& cg) {
  const dimension_type cg_space_dim = cg.space_dimension();
  // Dimension-compatibility check.
  if (cg_space_dim > space_dimension()) {
    throw_dimension_incompatible("add_congruence(cg)", cg);
  }

  add_congruence_no_check(cg);
}

template <typename ITV>
inline void
Box<ITV>::add_congruences(const Congruence_System& cgs) {
  if (cgs.space_dimension() > space_dimension()) {
    throw_dimension_incompatible("add_congruences(cgs)", cgs);
  }
  add_congruences_no_check(cgs);
}

template <typename T>
inline void
Box<T>::add_recycled_congruences(Congruence_System& cgs) {
  add_congruences(cgs);
}

template <typename T>
inline bool
Box<T>::can_recycle_constraint_systems() {
  return false;
}

template <typename T>
inline bool
Box<T>::can_recycle_congruence_systems() {
  return false;
}

template <typename T>
inline void
Box<T>::widening_assign(const Box& y, unsigned* tp) {
  CC76_widening_assign(y, tp);
}

template <typename ITV>
inline Congruence_System
Box<ITV>::minimized_congruences() const {
  // Only equalities can be congruences and these are already minimized.
  return congruences();
}

template <typename ITV>
inline I_Result
Box<ITV>
::refine_interval_no_check(ITV& itv,
                           const Constraint::Type type,
                           Coefficient_traits::const_reference numer,
                           Coefficient_traits::const_reference denom) {
  PPL_ASSERT(denom != 0);
  // The interval constraint is of the form
  // `var + numer / denom rel 0',
  // where `rel' is either the relation `==', `>=', or `>'.
  // For the purpose of refining the interval, this is
  // (morally) turned into `var rel -numer/denom'.
  PPL_DIRTY_TEMP(mpq_class, q);
  assign_r(q.get_num(), numer, ROUND_NOT_NEEDED);
  assign_r(q.get_den(), denom, ROUND_NOT_NEEDED);
  q.canonicalize();
  // Turn `numer/denom' into `-numer/denom'.
  q = -q;

  Relation_Symbol rel_sym;
  switch (type) {
  case Constraint::EQUALITY:
    rel_sym = EQUAL;
    break;
  case Constraint::NONSTRICT_INEQUALITY:
    rel_sym = (denom > 0) ? GREATER_OR_EQUAL : LESS_OR_EQUAL;
    break;
  case Constraint::STRICT_INEQUALITY:
    rel_sym = (denom > 0) ? GREATER_THAN : LESS_THAN;
    break;
  default:
    // Silence compiler warning.
    PPL_UNREACHABLE;
    return I_ANY;
  }
  I_Result res = itv.add_constraint(i_constraint(rel_sym, q));
  PPL_ASSERT(itv.OK());
  return res;
}

template <typename ITV>
inline void
Box<ITV>
::add_interval_constraint_no_check(const dimension_type var_id,
                                   const Constraint::Type type,
                                   Coefficient_traits::const_reference numer,
                                   Coefficient_traits::const_reference denom) {
  PPL_ASSERT(!marked_empty());
  PPL_ASSERT(var_id < space_dimension());
  PPL_ASSERT(denom != 0);
  refine_interval_no_check(seq[var_id], type, numer, denom);
  // FIXME: do check the value returned and set `empty' and
  // `empty_up_to_date' as appropriate.
  // This has to be done after reimplementation of intervals.
  reset_empty_up_to_date();
  PPL_ASSERT(OK());
}

template <typename ITV>
inline void
Box<ITV>::refine_with_constraint(const Constraint& c) {
  const dimension_type c_space_dim = c.space_dimension();
  // Dimension-compatibility check.
  if (c_space_dim > space_dimension()) {
    throw_dimension_incompatible("refine_with_constraint(c)", c);
  }

  // If the box is already empty, there is nothing left to do.
  if (marked_empty()) {
    return;
  }

  refine_no_check(c);
}

template <typename ITV>
inline void
Box<ITV>::refine_with_constraints(const Constraint_System& cs) {
  // Dimension-compatibility check.
  if (cs.space_dimension() > space_dimension()) {
    throw_dimension_incompatible("refine_with_constraints(cs)", cs);
  }

  // If the box is already empty, there is nothing left to do.
  if (marked_empty()) {
    return;
  }
  refine_no_check(cs);
}

template <typename ITV>
inline void
Box<ITV>::refine_with_congruence(const Congruence& cg) {
  const dimension_type cg_space_dim = cg.space_dimension();
  // Dimension-compatibility check.
  if (cg_space_dim > space_dimension()) {
    throw_dimension_incompatible("refine_with_congruence(cg)", cg);
  }
  // If the box is already empty, there is nothing left to do.
  if (marked_empty()) {
    return;
  }
  refine_no_check(cg);
}

template <typename ITV>
inline void
Box<ITV>::refine_with_congruences(const Congruence_System& cgs) {
  // Dimension-compatibility check.
  if (cgs.space_dimension() > space_dimension()) {
    throw_dimension_incompatible("refine_with_congruences(cgs)", cgs);
  }

  // If the box is already empty, there is nothing left to do.
  if (marked_empty()) {
    return;
  }

  refine_no_check(cgs);
}

template <typename ITV>
inline void
Box<ITV>::propagate_constraint(const Constraint& c) {
  const dimension_type c_space_dim = c.space_dimension();
  // Dimension-compatibility check.
  if (c_space_dim > space_dimension()) {
    throw_dimension_incompatible("propagate_constraint(c)", c);
  }

  // If the box is already empty, there is nothing left to do.
  if (marked_empty()) {
    return;
  }
  propagate_constraint_no_check(c);
}

template <typename ITV>
inline void
Box<ITV>::propagate_constraints(const Constraint_System& cs,
                                const dimension_type max_iterations) {
  // Dimension-compatibility check.
  if (cs.space_dimension() > space_dimension()) {
    throw_dimension_incompatible("propagate_constraints(cs)", cs);
  }

  // If the box is already empty, there is nothing left to do.
  if (marked_empty()) {
    return;
  }

  propagate_constraints_no_check(cs, max_iterations);
}

template <typename ITV>
inline void
Box<ITV>::unconstrain(const Variable var) {
  const dimension_type var_id = var.id();
  // Dimension-compatibility check.
  if (space_dimension() < var_id + 1) {
    throw_dimension_incompatible("unconstrain(var)", var_id + 1);
  }

  // If the box is already empty, there is nothing left to do.
  if (marked_empty()) {
    return;
  }

  // Here the box might still be empty (but we haven't detected it yet):
  // check emptiness of the interval for `var' before cylindrification.
  ITV& seq_var = seq[var_id];
  if (seq_var.is_empty()) {
    set_empty();
  }
  else {
    seq_var.assign(UNIVERSE);
  }

  PPL_ASSERT(OK());
}

/*! \relates Box */
template <typename Temp, typename To, typename ITV>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Box<ITV>& x,
                            const Box<ITV>& y,
                            const Rounding_Dir dir,
                            Temp& tmp0,
                            Temp& tmp1,
                            Temp& tmp2) {
  return l_m_distance_assign<Rectilinear_Distance_Specialization<Temp> >
    (r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Box */
template <typename Temp, typename To, typename ITV>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Box<ITV>& x,
                            const Box<ITV>& y,
                            const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return rectilinear_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Box */
template <typename To, typename ITV>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Box<ITV>& x,
                            const Box<ITV>& y,
                            const Rounding_Dir dir) {
  // FIXME: the following qualification is only to work around a bug
  // in the Intel C/C++ compiler version 10.1.x.
  return Parma_Polyhedra_Library
    ::rectilinear_distance_assign<To, To, ITV>(r, x, y, dir);
}

/*! \relates Box */
template <typename Temp, typename To, typename ITV>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Box<ITV>& x,
                          const Box<ITV>& y,
                          const Rounding_Dir dir,
                          Temp& tmp0,
                          Temp& tmp1,
                          Temp& tmp2) {
  return l_m_distance_assign<Euclidean_Distance_Specialization<Temp> >
    (r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Box */
template <typename Temp, typename To, typename ITV>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Box<ITV>& x,
                          const Box<ITV>& y,
                          const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return euclidean_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Box */
template <typename To, typename ITV>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Box<ITV>& x,
                          const Box<ITV>& y,
                          const Rounding_Dir dir) {
  // FIXME: the following qualification is only to work around a bug
  // in the Intel C/C++ compiler version 10.1.x.
  return Parma_Polyhedra_Library
    ::euclidean_distance_assign<To, To, ITV>(r, x, y, dir);
}

/*! \relates Box */
template <typename Temp, typename To, typename ITV>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Box<ITV>& x,
                           const Box<ITV>& y,
                           const Rounding_Dir dir,
                           Temp& tmp0,
                           Temp& tmp1,
                           Temp& tmp2) {
  return l_m_distance_assign<L_Infinity_Distance_Specialization<Temp> >
    (r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Box */
template <typename Temp, typename To, typename ITV>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Box<ITV>& x,
                           const Box<ITV>& y,
                           const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return l_infinity_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Box */
template <typename To, typename ITV>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Box<ITV>& x,
                           const Box<ITV>& y,
                           const Rounding_Dir dir) {
  // FIXME: the following qualification is only to work around a bug
  // in the Intel C/C++ compiler version 10.1.x.
  return Parma_Polyhedra_Library
    ::l_infinity_distance_assign<To, To, ITV>(r, x, y, dir);
}

/*! \relates Box */
template <typename ITV>
inline void
swap(Box<ITV>& x, Box<ITV>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Box_templates.hh line 1. */
/* Box class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/Generator_System_defs.hh line 1. */
/* Generator_System class declaration.
*/


/* Automatically generated from PPL source file ../src/Generator_System_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/Poly_Con_Relation_defs.hh line 1. */
/* Poly_Con_Relation class declaration.
*/


/* Automatically generated from PPL source file ../src/Poly_Con_Relation_defs.hh line 29. */
#include <iosfwd>

namespace Parma_Polyhedra_Library {

// Put them in the namespace here to declare them friend later.

//! True if and only if \p x and \p y are logically equivalent.
/*! \relates Poly_Con_Relation */
bool operator==(const Poly_Con_Relation& x, const Poly_Con_Relation& y);

//! True if and only if \p x and \p y are not logically equivalent.
/*! \relates Poly_Con_Relation */
bool operator!=(const Poly_Con_Relation& x, const Poly_Con_Relation& y);

//! Yields the logical conjunction of \p x and \p y.
/*! \relates Poly_Con_Relation */
Poly_Con_Relation operator&&(const Poly_Con_Relation& x,
                             const Poly_Con_Relation& y);

/*! \brief
  Yields the assertion with all the conjuncts of \p x
  that are not in \p y.

  \relates Poly_Con_Relation
*/
Poly_Con_Relation operator-(const Poly_Con_Relation& x,
                            const Poly_Con_Relation& y);

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Poly_Con_Relation */
std::ostream& operator<<(std::ostream& s, const Poly_Con_Relation& r);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library


//! The relation between a polyhedron and a constraint.
/*! \ingroup PPL_CXX_interface
  This class implements conjunctions of assertions on the relation
  between a polyhedron and a constraint.
*/
class Parma_Polyhedra_Library::Poly_Con_Relation {
private:
  //! Poly_Con_Relation is implemented by means of a finite bitset.
  typedef unsigned int flags_t;

  //! \name Bit-masks for the individual assertions
  //@{
  static const flags_t NOTHING             = 0U;
  static const flags_t IS_DISJOINT         = 1U << 0;
  static const flags_t STRICTLY_INTERSECTS = 1U << 1;
  static const flags_t IS_INCLUDED         = 1U << 2;
  static const flags_t SATURATES           = 1U << 3;
  //@} // Bit-masks for the individual assertions

  //! All assertions together.
  static const flags_t EVERYTHING
  = IS_DISJOINT
  | STRICTLY_INTERSECTS
  | IS_INCLUDED
  | SATURATES;

  //! This holds the current bitset.
  flags_t flags;

  //! True if and only if the conjunction \p x implies the conjunction \p y.
  static bool implies(flags_t x, flags_t y);

  //! Construct from a bit-mask.
  Poly_Con_Relation(flags_t mask);

  friend bool
  operator==(const Poly_Con_Relation& x, const Poly_Con_Relation& y);
  friend bool
  operator!=(const Poly_Con_Relation& x, const Poly_Con_Relation& y);

  friend Poly_Con_Relation
  operator&&(const Poly_Con_Relation& x, const Poly_Con_Relation& y);

  friend Poly_Con_Relation
  operator-(const Poly_Con_Relation& x, const Poly_Con_Relation& y);

  friend std::ostream&
  Parma_Polyhedra_Library::
  IO_Operators::operator<<(std::ostream& s, const Poly_Con_Relation& r);

public:
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Access the internal flags: this is needed for some language
    interfaces.
  */
#endif
  flags_t get_flags() const;

public:
  //! The assertion that says nothing.
  static Poly_Con_Relation nothing();

  /*! \brief
    The polyhedron and the set of points satisfying
    the constraint are disjoint.
  */
  static Poly_Con_Relation is_disjoint();

  /*! \brief
    The polyhedron intersects the set of points satisfying
    the constraint, but it is not included in it.
  */
  static Poly_Con_Relation strictly_intersects();

  /*! \brief
    The polyhedron is included in the set of points satisfying
    the constraint.
  */
  static Poly_Con_Relation is_included();

  /*! \brief
    The polyhedron is included in the set of points saturating
    the constraint.
  */
  static Poly_Con_Relation saturates();

  PPL_OUTPUT_DECLARATIONS

  //! True if and only if \p *this implies \p y.
  bool implies(const Poly_Con_Relation& y) const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;
};

/* Automatically generated from PPL source file ../src/Poly_Con_Relation_inlines.hh line 1. */
/* Poly_Con_Relation class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline
Poly_Con_Relation::Poly_Con_Relation(flags_t mask)
  : flags(mask) {
}

inline Poly_Con_Relation::flags_t
Poly_Con_Relation::get_flags() const {
  return flags;
}

inline Poly_Con_Relation
Poly_Con_Relation::nothing() {
  return Poly_Con_Relation(NOTHING);
}

inline Poly_Con_Relation
Poly_Con_Relation::is_disjoint() {
  return Poly_Con_Relation(IS_DISJOINT);
}

inline Poly_Con_Relation
Poly_Con_Relation::strictly_intersects() {
  return Poly_Con_Relation(STRICTLY_INTERSECTS);
}

inline Poly_Con_Relation
Poly_Con_Relation::is_included() {
  return Poly_Con_Relation(IS_INCLUDED);
}

inline Poly_Con_Relation
Poly_Con_Relation::saturates() {
  return Poly_Con_Relation(SATURATES);
}

inline bool
Poly_Con_Relation::implies(flags_t x, flags_t y) {
  return (x & y) == y;
}

inline bool
Poly_Con_Relation::implies(const Poly_Con_Relation& y) const {
  return implies(flags, y.flags);
}

/*! \relates Poly_Con_Relation */
inline bool
operator==(const Poly_Con_Relation& x, const Poly_Con_Relation& y) {
  return x.flags == y.flags;
}

/*! \relates Poly_Con_Relation */
inline bool
operator!=(const Poly_Con_Relation& x, const Poly_Con_Relation& y) {
  return x.flags != y.flags;
}

/*! \relates Poly_Con_Relation */
inline Poly_Con_Relation
operator&&(const Poly_Con_Relation& x, const Poly_Con_Relation& y) {
  return Poly_Con_Relation(x.flags | y.flags);
}

/*! \relates Poly_Con_Relation */
inline Poly_Con_Relation
operator-(const Poly_Con_Relation& x, const Poly_Con_Relation& y) {
  return Poly_Con_Relation(x.flags & ~y.flags);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Poly_Con_Relation_defs.hh line 165. */

/* Automatically generated from PPL source file ../src/Generator_System_defs.hh line 35. */
#include <iosfwd>
#include <cstddef>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*!
  \relates Parma_Polyhedra_Library::Generator_System
  Writes <CODE>false</CODE> if \p gs is empty.  Otherwise, writes on
  \p s the generators of \p gs, all in one row and separated by ", ".
*/
std::ostream& operator<<(std::ostream& s, const Generator_System& gs);

} // namespace IO_Operators

// TODO: Consider removing this.
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are identical.
/*! \relates Generator_System */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator==(const Generator_System& x, const Generator_System& y);

// TODO: Consider removing this.
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are different.
/*! \relates Generator_System */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool operator!=(const Generator_System& x, const Generator_System& y);

/*! \relates Generator_System */
void
swap(Generator_System& x, Generator_System& y);

} // namespace Parma_Polyhedra_Library

//! A system of generators.
/*! \ingroup PPL_CXX_interface
    An object of the class Generator_System is a system of generators,
    i.e., a multiset of objects of the class Generator
    (lines, rays, points and closure points).
    When inserting generators in a system, space dimensions are automatically
    adjusted so that all the generators in the system are defined
    on the same vector space.
    A system of generators which is meant to define a non-empty
    polyhedron must include at least one point: the reason is that
    lines, rays and closure points need a supporting point
    (lines and rays only specify directions while closure points only
    specify points in the topological closure of the NNC polyhedron).

    \par
     In all the examples it is assumed that variables
    <CODE>x</CODE> and <CODE>y</CODE> are defined as follows:
    \code
  Variable x(0);
  Variable y(1);
    \endcode

    \par Example 1
    The following code defines the line having the same direction
    as the \f$x\f$ axis (i.e., the first Cartesian axis)
    in \f$\Rset^2\f$:
    \code
  Generator_System gs;
  gs.insert(line(x + 0*y));
    \endcode
    As said above, this system of generators corresponds to
    an empty polyhedron, because the line has no supporting point.
    To define a system of generators that does correspond to
    the \f$x\f$ axis, we can add the following code which
    inserts the origin of the space as a point:
    \code
  gs.insert(point(0*x + 0*y));
    \endcode
    Since space dimensions are automatically adjusted, the following
    code obtains the same effect:
    \code
  gs.insert(point(0*x));
    \endcode
    In contrast, if we had added the following code, we would have
    defined a line parallel to the \f$x\f$ axis through
    the point \f$(0, 1)^\transpose \in \Rset^2\f$.
    \code
  gs.insert(point(0*x + 1*y));
    \endcode

    \par Example 2
    The following code builds a ray having the same direction as
    the positive part of the \f$x\f$ axis in \f$\Rset^2\f$:
    \code
  Generator_System gs;
  gs.insert(ray(x + 0*y));
    \endcode
    To define a system of generators indeed corresponding to the set
    \f[
      \bigl\{\,
        (x, 0)^\transpose \in \Rset^2
      \bigm|
        x \geq 0
      \,\bigr\},
    \f]
    one just has to add the origin:
    \code
  gs.insert(point(0*x + 0*y));
    \endcode

    \par Example 3
    The following code builds a system of generators having four points
    and corresponding to a square in \f$\Rset^2\f$
    (the same as Example 1 for the system of constraints):
    \code
  Generator_System gs;
  gs.insert(point(0*x + 0*y));
  gs.insert(point(0*x + 3*y));
  gs.insert(point(3*x + 0*y));
  gs.insert(point(3*x + 3*y));
    \endcode

    \par Example 4
    By using closure points, we can define the \e kernel
    (i.e., the largest open set included in a given set)
    of the square defined in the previous example.
    Note that a supporting point is needed and, for that purpose,
    any inner point could be considered.
    \code
  Generator_System gs;
  gs.insert(point(x + y));
  gs.insert(closure_point(0*x + 0*y));
  gs.insert(closure_point(0*x + 3*y));
  gs.insert(closure_point(3*x + 0*y));
  gs.insert(closure_point(3*x + 3*y));
    \endcode

    \par Example 5
    The following code builds a system of generators having two points
    and a ray, corresponding to a half-strip in \f$\Rset^2\f$
    (the same as Example 2 for the system of constraints):
    \code
  Generator_System gs;
  gs.insert(point(0*x + 0*y));
  gs.insert(point(0*x + 1*y));
  gs.insert(ray(x - y));
    \endcode

    \note
    After inserting a multiset of generators in a generator system,
    there are no guarantees that an <EM>exact</EM> copy of them
    can be retrieved:
    in general, only an <EM>equivalent</EM> generator system
    will be available, where original generators may have been
    reordered, removed (if they are duplicate or redundant), etc.
*/
class Parma_Polyhedra_Library::Generator_System {
public:
  typedef Generator row_type;

  static const Representation default_representation = SPARSE;

  //! Default constructor: builds an empty system of generators.
  Generator_System(Representation r = default_representation);

  //! Builds the singleton system containing only generator \p g.
  explicit Generator_System(const Generator& g,
                            Representation r = default_representation);

  //! Ordinary copy constructor.
  //! The new Generator_System will have the same representation as `gs'.
  Generator_System(const Generator_System& gs);

  //! Copy constructor with specified representation.
  Generator_System(const Generator_System& gs, Representation r);

  //! Destructor.
  ~Generator_System();

  //! Assignment operator.
  Generator_System& operator=(const Generator_System& y);

  //! Returns the current representation of *this.
  Representation representation() const;

  //! Converts *this to the specified representation.
  void set_representation(Representation r);

  //! Returns the maximum space dimension a Generator_System can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  //! Sets the space dimension of the rows in the system to \p space_dim .
  void set_space_dimension(dimension_type space_dim);

  /*! \brief
    Removes all the generators from the generator system
    and sets its space dimension to 0.
  */
  void clear();

  /*! \brief
    Inserts in \p *this a copy of the generator \p g,
    increasing the number of space dimensions if needed.
  */
  void insert(const Generator& g);

  /*! \brief
    Inserts in \p *this the generator \p g, stealing its contents and
    increasing the number of space dimensions if needed.
  */
  void insert(Generator& g, Recycle_Input);

  //! Initializes the class.
  static void initialize();

  //! Finalizes the class.
  static void finalize();

  /*! \brief
    Returns the singleton system containing only Generator::zero_dim_point().
  */
  static const Generator_System& zero_dim_univ();

  typedef Generator_System_const_iterator const_iterator;

  //! Returns <CODE>true</CODE> if and only if \p *this has no generators.
  bool empty() const;

  /*! \brief
    Returns the const_iterator pointing to the first generator,
    if \p *this is not empty;
    otherwise, returns the past-the-end const_iterator.
  */
  const_iterator begin() const;

  //! Returns the past-the-end const_iterator.
  const_iterator end() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.

    Resizes the matrix of generators using the numbers of rows and columns
    read from \p s, then initializes the coordinates of each generator
    and its type reading the contents from \p s.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Swaps \p *this with \p y.
  void m_swap(Generator_System& y);

private:

  bool has_no_rows() const;

  //! Removes all the specified dimensions from the generator system.
  /*!
    The space dimension of the variable with the highest space
    dimension in \p vars must be at most the space dimension
    of \p this.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  //! Shift by \p n positions the coefficients of variables, starting from
  //! the coefficient of \p v. This increases the space dimension by \p n.
  void shift_space_dimensions(Variable v, dimension_type n);

  //! Permutes the space dimensions of the matrix.
  /*
    \param cycle
    A vector representing a cycle of the permutation according to which the
    columns must be rearranged.

    The \p cycle vector represents a cycle of a permutation of space
    dimensions.
    For example, the permutation
    \f$ \{ x_1 \mapsto x_2, x_2 \mapsto x_3, x_3 \mapsto x_1 \}\f$ can be
    represented by the vector containing \f$ x_1, x_2, x_3 \f$.
  */
  void permute_space_dimensions(const std::vector<Variable>& cycle);

  //! Swaps the coefficients of the variables \p v1 and \p v2 .
  void swap_space_dimensions(Variable v1, Variable v2);

  dimension_type num_rows() const;

  //! Adds \p n rows and space dimensions to the system.
  /*!
    \param n
    The number of rows and space dimensions to be added: must be strictly
    positive.

    Turns the system \f$M \in \Rset^r \times \Rset^c\f$ into
    the system \f$N \in \Rset^{r+n} \times \Rset^{c+n}\f$
    such that
    \f$N = \bigl(\genfrac{}{}{0pt}{}{0}{M}\genfrac{}{}{0pt}{}{J}{o}\bigr)\f$,
    where \f$J\f$ is the specular image
    of the \f$n \times n\f$ identity matrix.
  */
  void add_universe_rows_and_space_dimensions(dimension_type n);

  Topology topology() const;

  //! Returns the index of the first pending row.
  dimension_type first_pending_row() const;

  //! Sets the index to indicate that the system has no pending rows.
  void unset_pending_rows();

  //! Sets the sortedness flag of the system to \p b.
  void set_sorted(bool b);

  //! Returns the value of the sortedness flag.
  bool is_sorted() const;

  //! Sets the index of the first pending row to \p i.
  void set_index_first_pending_row(dimension_type i);

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    the system topology is <CODE>NECESSARILY_CLOSED</CODE>.
  */
  bool is_necessarily_closed() const;

  //! Full assignment operator: pending rows are copied as pending.
  void assign_with_pending(const Generator_System& y);

  //! Returns the number of rows that are in the pending part of the system.
  dimension_type num_pending_rows() const;

  /*! \brief
    Sorts the pending rows and eliminates those that also occur
    in the non-pending part of the system.
  */
  void sort_pending_and_remove_duplicates();

  /*! \brief
    Sorts the system, removing duplicates, keeping the saturation
    matrix consistent.

    \param sat
    Bit matrix with rows corresponding to the rows of \p *this.
  */
  void sort_and_remove_with_sat(Bit_Matrix& sat);

  /*! \brief
    Sorts the non-pending rows (in growing order) and eliminates
    duplicated ones.
  */
  void sort_rows();

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is sorted,
    without checking for duplicates.
  */
  bool check_sorted() const;

  /*! \brief
    Returns the number of rows in the system
    that represent either lines or equalities.
  */
  dimension_type num_lines_or_equalities() const;

  //! Makes the system shrink by removing its i-th row.
  /*!
    When \p keep_sorted is \p true and the system is sorted, sortedness will
    be preserved, but this method costs O(n).

    Otherwise, this method just swaps the i-th row with the last and then
    removes it, so it costs O(1).
  */
  void remove_row(dimension_type i, bool keep_sorted = false);

  //! Makes the system shrink by removing the rows in [first,last).
  /*!
    When \p keep_sorted is \p true and the system is sorted, sortedness will
    be preserved, but this method costs O(num_rows()).

    Otherwise, this method just swaps the rows with the last ones and then
    removes them, so it costs O(last - first).
  */
  void remove_rows(dimension_type first, dimension_type last,
                   bool keep_sorted = false);

  //! Removes the specified rows. The row ordering of remaining rows is
  //! preserved.
  /*!
    \param indexes specifies a list of row indexes.
                   It must be sorted.
  */
  void remove_rows(const std::vector<dimension_type>& indexes);

  //! Makes the system shrink by removing its \p n trailing rows.
  void remove_trailing_rows(dimension_type n);

  //! Minimizes the subsystem of equations contained in \p *this.
  /*!
    This method works only on the equalities of the system:
    the system is required to be partially sorted, so that
    all the equalities are grouped at its top; it is assumed that
    the number of equalities is exactly \p n_lines_or_equalities.
    The method finds a minimal system for the equalities and
    returns its rank, i.e., the number of linearly independent equalities.
    The result is an upper triangular subsystem of equalities:
    for each equality, the pivot is chosen starting from
    the right-most columns.
  */
  dimension_type gauss(dimension_type n_lines_or_equalities);

  /*! \brief
    Back-substitutes the coefficients to reduce
    the complexity of the system.

    Takes an upper triangular system having \p n_lines_or_equalities rows.
    For each row, starting from the one having the minimum number of
    coefficients different from zero, computes the expression of an element
    as a function of the remaining ones and then substitutes this expression
    in all the other rows.
  */
  void back_substitute(dimension_type n_lines_or_equalities);

  //! Strongly normalizes the system.
  void strong_normalize();

  /*! \brief
    Assigns to \p *this the result of merging its rows with
    those of \p y, obtaining a sorted system.

    Duplicated rows will occur only once in the result.
    On entry, both systems are assumed to be sorted and have
    no pending rows.
  */
  void merge_rows_assign(const Generator_System& y);

  //! Adds to \p *this a copy of  the rows of \p y.
  /*!
    It is assumed that \p *this has no pending rows.
  */
  void insert(const Generator_System& y);

  //! Adds a copy of the rows of `y' to the pending part of `*this'.
  void insert_pending(const Generator_System& r);

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the singleton system containing only Generator::zero_dim_point().
  */
  static const Generator_System* zero_dim_univ_p;

  friend class Generator_System_const_iterator;

  //! Builds an empty system of generators having the specified topology.
  explicit Generator_System(Topology topol,
                            Representation r = default_representation);

  /*! \brief
    Builds a system of rays/points on a \p space_dim dimensional space. If
    \p topol is <CODE>NOT_NECESSARILY_CLOSED</CODE> the \f$\epsilon\f$
    dimension is added.
  */
  Generator_System(Topology topol, dimension_type space_dim,
                   Representation r = default_representation);

  /*! \brief
    Adjusts \p *this so that it matches the \p new_topology and
    \p new_space_dim (adding or removing columns if needed).
    Returns <CODE>false</CODE> if and only if \p topol is
    equal to <CODE>NECESSARILY_CLOSED</CODE> and \p *this
    contains closure points.
  */
  bool adjust_topology_and_space_dimension(Topology new_topology,
                                           dimension_type new_space_dim);

  /*! \brief
    For each unmatched closure point in \p *this, adds the
    corresponding point.

    It is assumed that the topology of \p *this
    is <CODE>NOT_NECESSARILY_CLOSED</CODE>.
  */
  void add_corresponding_points();

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    contains one or more points.
  */
  bool has_points() const;

  /*! \brief
    For each unmatched point in \p *this, adds the corresponding
    closure point.

    It is assumed that the topology of \p *this
    is <CODE>NOT_NECESSARILY_CLOSED</CODE>.
  */
  void add_corresponding_closure_points();

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    contains one or more closure points.

    Note: the check for the presence of closure points is
    done under the point of view of the user. Namely, we scan
    the generator system using high-level iterators, so that
    closure points that are matching the corresponding points
    will be disregarded.
  */
  bool has_closure_points() const;

  //! Converts this generator system into a non-necessarily closed generator
  //! system.
  void convert_into_non_necessarily_closed();

  //! Returns a constant reference to the \p k- th generator of the system.
  const Generator& operator[](dimension_type k) const;

  /*! \brief
    Returns the relations holding between the generator system
    and the constraint \p c.
  */
  Parma_Polyhedra_Library::Poly_Con_Relation
  relation_with(const Constraint& c) const;

  //! Returns <CODE>true</CODE> if all the generators satisfy \p c.
  bool satisfied_by_all_generators(const Constraint& c) const;

  //! Returns <CODE>true</CODE> if all the generators satisfy \p c.
  /*!
    It is assumed that <CODE>c.is_necessarily_closed()</CODE> holds.
  */
  bool satisfied_by_all_generators_C(const Constraint& c) const;

  //! Returns <CODE>true</CODE> if all the generators satisfy \p c.
  /*!
    It is assumed that <CODE>c.is_necessarily_closed()</CODE> does not hold.
  */
  bool satisfied_by_all_generators_NNC(const Constraint& c) const;

  //! Assigns to a given variable an affine expression.
  /*!
    \param v
    The variable to which the affine transformation is assigned;

    \param expr
    The numerator of the affine transformation:
    \f$\sum_{i = 0}^{n - 1} a_i x_i + b\f$;

    \param denominator
    The denominator of the affine transformation.

    We want to allow affine transformations (see the Introduction) having
    any rational coefficients. Since the coefficients of the
    constraints are integers we must also provide an integer \p denominator
    that will be used as denominator of the affine transformation.
    The denominator is required to be a positive integer.

    The affine transformation assigns to each element of the column containing
    the coefficients of v the follow expression:
    \f[
      \frac{\sum_{i = 0}^{n - 1} a_i x_i + b}
           {\mathrm{denominator}}.
    \f]

    \p expr is a constant parameter and unaltered by this computation.
  */
  void affine_image(Variable v,
                    const Linear_Expression& expr,
                    Coefficient_traits::const_reference denominator);

  //! Returns the number of lines of the system.
  dimension_type num_lines() const;

  //! Returns the number of rays of the system.
  dimension_type num_rays() const;

  //! Removes all the invalid lines and rays.
  /*!
    The invalid lines and rays are those with all
    the homogeneous terms set to zero.
  */
  void remove_invalid_lines_and_rays();

  /*! \brief
    Applies Gaussian elimination and back-substitution so as
    to provide a partial simplification of the system of generators.

    It is assumed that the system has no pending generators.
  */
  void simplify();

  /*! \brief
    Inserts in \p *this a copy of the generator \p g,
    increasing the number of space dimensions if needed.
    It is a pending generator.
  */
  void insert_pending(const Generator& g);

  /*! \brief
    Inserts in \p *this the generator \p g, stealing its contents and
    increasing the number of space dimensions if needed.
    It is a pending generator.
  */
  void insert_pending(Generator& g, Recycle_Input);

  Linear_System<Generator> sys;

  friend bool
  operator==(const Generator_System& x, const Generator_System& y);

  friend class Polyhedron;
};

//! An iterator over a system of generators
/*! \ingroup PPL_CXX_interface
    A const_iterator is used to provide read-only access
    to each generator contained in an object of Generator_System.

    \par Example
    The following code prints the system of generators
    of the polyhedron <CODE>ph</CODE>:
    \code
const Generator_System& gs = ph.generators();
for (Generator_System::const_iterator i = gs.begin(),
        gs_end = gs.end(); i != gs_end; ++i)
  cout << *i << endl;
    \endcode
    The same effect can be obtained more concisely by using
    more features of the STL:
    \code
const Generator_System& gs = ph.generators();
copy(gs.begin(), gs.end(), ostream_iterator<Generator>(cout, "\n"));
    \endcode
*/
class Parma_Polyhedra_Library::Generator_System_const_iterator
  : public std::iterator<std::forward_iterator_tag,
        Generator,
        std::ptrdiff_t,
        const Generator*,
        const Generator&> {
public:
  //! Default constructor.
  Generator_System_const_iterator();

  //! Ordinary copy constructor.
  Generator_System_const_iterator(const Generator_System_const_iterator& y);

  //! Destructor.
  ~Generator_System_const_iterator();

  //! Assignment operator.
  Generator_System_const_iterator& operator=(const Generator_System_const_iterator& y);

  //! Dereference operator.
  const Generator& operator*() const;

  //! Indirect member selector.
  const Generator* operator->() const;

  //! Prefix increment operator.
  Generator_System_const_iterator& operator++();

  //! Postfix increment operator.
  Generator_System_const_iterator operator++(int);

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this and \p y are identical.
  */
  bool operator==(const Generator_System_const_iterator& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this and \p y are different.
  */
  bool operator!=(const Generator_System_const_iterator& y) const;

private:
  friend class Generator_System;

  //! The const iterator over the Linear_System.
  Linear_System<Generator>::const_iterator i;

  //! A const pointer to the Linear_System.
  const Linear_System<Generator>* gsp;

  //! Constructor.
  Generator_System_const_iterator(const Linear_System<Generator>::const_iterator& iter,
      const Generator_System& gsys);

  /*! \brief
    \p *this skips to the next generator, skipping those
    closure points that are immediately followed by a matching point.
  */
  void skip_forward();
};

// Generator_System_inlines.hh is not included here on purpose.

/* Automatically generated from PPL source file ../src/Generator_System_inlines.hh line 1. */
/* Generator_System class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Generator_System_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

inline
Generator_System::Generator_System(Representation r)
  : sys(NECESSARILY_CLOSED, r) {
}

inline
Generator_System::Generator_System(const Generator& g, Representation r)
  : sys(g.topology(), r) {
  sys.insert(g);
}

inline
Generator_System::Generator_System(const Generator_System& gs)
  : sys(gs.sys) {
}

inline
Generator_System::Generator_System(const Generator_System& gs,
                                   Representation r)
  : sys(gs.sys, r) {
}

inline
Generator_System::Generator_System(const Topology topol, Representation r)
  : sys(topol, r) {
}

inline
Generator_System::Generator_System(const Topology topol,
                                   const dimension_type space_dim,
                                   Representation r)
  : sys(topol, space_dim, r) {
}

inline
Generator_System::~Generator_System() {
}

inline Generator_System&
Generator_System::operator=(const Generator_System& y) {
  Generator_System tmp = y;
  swap(*this, tmp);
  return *this;
}

inline Representation
Generator_System::representation() const {
  return sys.representation();
}

inline void
Generator_System::set_representation(Representation r) {
  sys.set_representation(r);
}

inline dimension_type
Generator_System::max_space_dimension() {
  return Linear_System<Generator>::max_space_dimension();
}

inline dimension_type
Generator_System::space_dimension() const {
  return sys.space_dimension();
}

inline void
Generator_System::set_space_dimension(dimension_type space_dim) {
  const dimension_type old_space_dim = space_dimension();
  sys.set_space_dimension_no_ok(space_dim);

  if (space_dim < old_space_dim) {
    // We may have invalid lines and rays now.
    remove_invalid_lines_and_rays();
  }

#ifndef NDEBUG
  for (dimension_type i = 0; i < sys.num_rows(); ++i) {
    PPL_ASSERT(sys[i].OK());
  }
#endif
  PPL_ASSERT(sys.OK());
  PPL_ASSERT(OK());
}

inline void
Generator_System::clear() {
  sys.clear();
}

inline const Generator&
Generator_System::operator[](const dimension_type k) const {
  return sys[k];
}

inline void
Generator_System
::remove_space_dimensions(const Variables_Set& vars) {
  sys.remove_space_dimensions(vars);
}

inline void
Generator_System
::shift_space_dimensions(Variable v, dimension_type n) {
  sys.shift_space_dimensions(v, n);
}

inline void
Generator_System
::permute_space_dimensions(const std::vector<Variable>& cycle) {
  sys.permute_space_dimensions(cycle);
}

inline void
Generator_System
::swap_space_dimensions(Variable v1, Variable v2) {
  sys.swap_space_dimensions(v1, v2);
}

inline dimension_type
Generator_System::num_rows() const {
  return sys.num_rows();
}

inline void
Generator_System::add_universe_rows_and_space_dimensions(dimension_type n) {
  sys.add_universe_rows_and_space_dimensions(n);
}

inline Topology
Generator_System::topology() const {
  return sys.topology();
}

inline dimension_type
Generator_System::first_pending_row() const {
  return sys.first_pending_row();
}

inline void
Generator_System::unset_pending_rows() {
  sys.unset_pending_rows();
}

inline void
Generator_System::set_sorted(bool b) {
  sys.set_sorted(b);
}

inline bool
Generator_System::is_sorted() const {
  return sys.is_sorted();
}

inline void
Generator_System::set_index_first_pending_row(dimension_type i) {
  sys.set_index_first_pending_row(i);
}

inline bool
Generator_System::is_necessarily_closed() const {
  return sys.is_necessarily_closed();
}

inline void
Generator_System::assign_with_pending(const Generator_System& y) {
  sys.assign_with_pending(y.sys);
}

inline dimension_type
Generator_System::num_pending_rows() const {
  return sys.num_pending_rows();
}

inline void
Generator_System::sort_pending_and_remove_duplicates() {
  return sys.sort_pending_and_remove_duplicates();
}

inline void
Generator_System::sort_and_remove_with_sat(Bit_Matrix& sat) {
  sys.sort_and_remove_with_sat(sat);
}

inline void
Generator_System::sort_rows() {
  sys.sort_rows();
}

inline bool
Generator_System::check_sorted() const {
  return sys.check_sorted();
}

inline dimension_type
Generator_System::num_lines_or_equalities() const {
  return sys.num_lines_or_equalities();
}

inline void
Generator_System::remove_row(dimension_type i, bool keep_sorted) {
  sys.remove_row(i, keep_sorted);
}

inline void
Generator_System::remove_rows(dimension_type first, dimension_type last,
                              bool keep_sorted) {
  sys.remove_rows(first, last, keep_sorted);
}

inline void
Generator_System::remove_rows(const std::vector<dimension_type>& indexes) {
  sys.remove_rows(indexes);
}

inline void
Generator_System::remove_trailing_rows(dimension_type n) {
  sys.remove_trailing_rows(n);
}

inline dimension_type
Generator_System::gauss(dimension_type n_lines_or_equalities) {
  return sys.gauss(n_lines_or_equalities);
}

inline void
Generator_System::back_substitute(dimension_type n_lines_or_equalities) {
  sys.back_substitute(n_lines_or_equalities);
}

inline void
Generator_System::strong_normalize() {
  sys.strong_normalize();
}

inline void
Generator_System::merge_rows_assign(const Generator_System& y) {
  sys.merge_rows_assign(y.sys);
}

inline void
Generator_System::insert(const Generator_System& y) {
  sys.insert(y.sys);
}

inline void
Generator_System::insert_pending(const Generator_System& r) {
  sys.insert_pending(r.sys);
}

inline bool
operator==(const Generator_System& x, const Generator_System& y) {
  return x.sys == y.sys;
}

inline bool
operator!=(const Generator_System& x, const Generator_System& y) {
  return !(x == y);
}

inline
Generator_System_const_iterator::Generator_System_const_iterator()
  : i(), gsp(0) {
}

inline
Generator_System_const_iterator::Generator_System_const_iterator(const Generator_System_const_iterator& y)
  : i(y.i), gsp(y.gsp) {
}

inline
Generator_System_const_iterator::~Generator_System_const_iterator() {
}

inline
Generator_System_const_iterator&
Generator_System_const_iterator::operator=(const Generator_System_const_iterator& y) {
  i = y.i;
  gsp = y.gsp;
  return *this;
}

inline const Generator&
Generator_System_const_iterator::operator*() const {
  return *i;
}

inline const Generator*
Generator_System_const_iterator::operator->() const {
  return i.operator->();
}

inline Generator_System_const_iterator&
Generator_System_const_iterator::operator++() {
  ++i;
  if (!gsp->is_necessarily_closed()) {
    skip_forward();
  }
  return *this;
}

inline Generator_System_const_iterator
Generator_System_const_iterator::operator++(int) {
  const Generator_System_const_iterator tmp = *this;
  operator++();
  return tmp;
}

inline bool
Generator_System_const_iterator::operator==(const Generator_System_const_iterator& y) const {
  return i == y.i;
}

inline bool
Generator_System_const_iterator::operator!=(const Generator_System_const_iterator& y) const {
  return i != y.i;
}

inline
Generator_System_const_iterator::
Generator_System_const_iterator(const Linear_System<Generator>::const_iterator& iter,
                                const Generator_System& gs)
  : i(iter), gsp(&gs.sys) {
}

inline bool
Generator_System::empty() const {
  return sys.has_no_rows();
}

inline bool
Generator_System::has_no_rows() const {
  return sys.has_no_rows();
}

inline Generator_System::const_iterator
Generator_System::begin() const {
  const_iterator i(sys.begin(), *this);
  if (!sys.is_necessarily_closed()) {
    i.skip_forward();
  }
  return i;
}

inline Generator_System::const_iterator
Generator_System::end() const {
  const const_iterator i(sys.end(), *this);
  return i;
}

inline const Generator_System&
Generator_System::zero_dim_univ() {
  PPL_ASSERT(zero_dim_univ_p != 0);
  return *zero_dim_univ_p;
}

inline void
Generator_System::m_swap(Generator_System& y) {
  swap(sys, y.sys);
}

inline memory_size_type
Generator_System::external_memory_in_bytes() const {
  return sys.external_memory_in_bytes();
}

inline memory_size_type
Generator_System::total_memory_in_bytes() const {
  return external_memory_in_bytes() + sizeof(*this);
}

inline void
Generator_System::simplify() {
  sys.simplify();
  remove_invalid_lines_and_rays();
}

/*! \relates Generator_System */
inline void
swap(Generator_System& x, Generator_System& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Poly_Gen_Relation_defs.hh line 1. */
/* Poly_Gen_Relation class declaration.
*/


/* Automatically generated from PPL source file ../src/Poly_Gen_Relation_defs.hh line 29. */
#include <iosfwd>

namespace Parma_Polyhedra_Library {

// Put them in the namespace here to declare them friend later.

//! True if and only if \p x and \p y are logically equivalent.
/*! \relates Poly_Gen_Relation */
bool operator==(const Poly_Gen_Relation& x, const Poly_Gen_Relation& y);

//! True if and only if \p x and \p y are not logically equivalent.
/*! \relates Poly_Gen_Relation */
bool operator!=(const Poly_Gen_Relation& x, const Poly_Gen_Relation& y);

//! Yields the logical conjunction of \p x and \p y.
/*! \relates Poly_Gen_Relation */
Poly_Gen_Relation operator&&(const Poly_Gen_Relation& x,
                             const Poly_Gen_Relation& y);

/*! \brief
  Yields the assertion with all the conjuncts of \p x
  that are not in \p y.

  \relates Poly_Gen_Relation
*/
Poly_Gen_Relation operator-(const Poly_Gen_Relation& x,
                            const Poly_Gen_Relation& y);

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Poly_Gen_Relation */
std::ostream& operator<<(std::ostream& s, const Poly_Gen_Relation& r);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library


//! The relation between a polyhedron and a generator
/*! \ingroup PPL_CXX_interface
  This class implements conjunctions of assertions on the relation
  between a polyhedron and a generator.
*/
class Parma_Polyhedra_Library::Poly_Gen_Relation {
private:
  //! Poly_Gen_Relation is implemented by means of a finite bitset.
  typedef unsigned int flags_t;

  //! \name Bit-masks for the individual assertions
  //@{
  static const flags_t NOTHING  = 0U;
  static const flags_t SUBSUMES = 1U << 0;
  //@} // Bit-masks for the individual assertions

  //! All assertions together.
  static const flags_t EVERYTHING
  = SUBSUMES;

  //! This holds the current bitset.
  flags_t flags;

  //! True if and only if the conjunction \p x implies the conjunction \p y.
  static bool implies(flags_t x, flags_t y);

  //! Construct from a bit-mask.
  Poly_Gen_Relation(flags_t mask);

  friend bool
  operator==(const Poly_Gen_Relation& x, const Poly_Gen_Relation& y);
  friend bool
  operator!=(const Poly_Gen_Relation& x, const Poly_Gen_Relation& y);

  friend Poly_Gen_Relation
  operator&&(const Poly_Gen_Relation& x, const Poly_Gen_Relation& y);

  friend Poly_Gen_Relation
  operator-(const Poly_Gen_Relation& x, const Poly_Gen_Relation& y);

  friend std::ostream&
  Parma_Polyhedra_Library::
  IO_Operators::operator<<(std::ostream& s, const Poly_Gen_Relation& r);

public:
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Access the internal flags: this is needed for some language
    interfaces.
  */
#endif
  flags_t get_flags() const;

public:
  //! The assertion that says nothing.
  static Poly_Gen_Relation nothing();

  //! Adding the generator would not change the polyhedron.
  static Poly_Gen_Relation subsumes();

  PPL_OUTPUT_DECLARATIONS

  //! True if and only if \p *this implies \p y.
  bool implies(const Poly_Gen_Relation& y) const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;
};

/* Automatically generated from PPL source file ../src/Poly_Gen_Relation_inlines.hh line 1. */
/* Poly_Gen_Relation class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline
Poly_Gen_Relation::Poly_Gen_Relation(flags_t mask)
  : flags(mask) {
}

inline Poly_Gen_Relation::flags_t
Poly_Gen_Relation::get_flags() const {
  return flags;
}

inline Poly_Gen_Relation
Poly_Gen_Relation::nothing() {
  return Poly_Gen_Relation(NOTHING);
}

inline Poly_Gen_Relation
Poly_Gen_Relation::subsumes() {
  return Poly_Gen_Relation(SUBSUMES);
}

inline bool
Poly_Gen_Relation::implies(flags_t x, flags_t y) {
  return (x & y) == y;
}

inline bool
Poly_Gen_Relation::implies(const Poly_Gen_Relation& y) const {
  return implies(flags, y.flags);
}

/*! \relates Poly_Gen_Relation */
inline bool
operator==(const Poly_Gen_Relation& x, const Poly_Gen_Relation& y) {
  return x.flags == y.flags;
}

/*! \relates Poly_Gen_Relation */
inline bool
operator!=(const Poly_Gen_Relation& x, const Poly_Gen_Relation& y) {
  return x.flags != y.flags;
}

/*! \relates Poly_Gen_Relation */
inline Poly_Gen_Relation
operator&&(const Poly_Gen_Relation& x, const Poly_Gen_Relation& y) {
  return Poly_Gen_Relation(x.flags | y.flags);
}

/*! \relates Poly_Gen_Relation */
inline Poly_Gen_Relation
operator-(const Poly_Gen_Relation& x, const Poly_Gen_Relation& y) {
  return Poly_Gen_Relation(x.flags & ~y.flags);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Poly_Gen_Relation_defs.hh line 138. */

/* Automatically generated from PPL source file ../src/Polyhedron_defs.hh line 1. */
/* Polyhedron class declaration.
*/


/* Automatically generated from PPL source file ../src/H79_Certificate_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class H79_Certificate;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Polyhedron_defs.hh line 51. */
#include <vector>
#include <iosfwd>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*!
  \relates Parma_Polyhedra_Library::Polyhedron
  Writes a textual representation of \p ph on \p s:
  <CODE>false</CODE> is written if \p ph is an empty polyhedron;
  <CODE>true</CODE> is written if \p ph is a universe polyhedron;
  a minimized system of constraints defining \p ph is written otherwise,
  all constraints in one row separated by ", ".
*/
std::ostream&
operator<<(std::ostream& s, const Polyhedron& ph);

} // namespace IO_Operators

//! Swaps \p x with \p y.
/*! \relates Polyhedron */
void swap(Polyhedron& x, Polyhedron& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if
  \p x and \p y are the same polyhedron.

  \relates Polyhedron
  Note that \p x and \p y may be topology- and/or dimension-incompatible
  polyhedra: in those cases, the value <CODE>false</CODE> is returned.
*/
bool operator==(const Polyhedron& x, const Polyhedron& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if
  \p x and \p y are different polyhedra.

  \relates Polyhedron
  Note that \p x and \p y may be topology- and/or dimension-incompatible
  polyhedra: in those cases, the value <CODE>true</CODE> is returned.
*/
bool operator!=(const Polyhedron& x, const Polyhedron& y);

namespace Interfaces {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Returns \c true if and only if
  <code>ph.topology() == NECESSARILY_CLOSED</code>.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
bool is_necessarily_closed_for_interfaces(const Polyhedron& ph);

} // namespace Interfaces

} // namespace Parma_Polyhedra_Library


//! The base class for convex polyhedra.
/*! \ingroup PPL_CXX_interface
  An object of the class Polyhedron represents a convex polyhedron
  in the vector space \f$\Rset^n\f$.

  A polyhedron can be specified as either a finite system of constraints
  or a finite system of generators (see Section \ref representation)
  and it is always possible to obtain either representation.
  That is, if we know the system of constraints, we can obtain
  from this the system of generators that define the same polyhedron
  and vice versa.
  These systems can contain redundant members: in this case we say
  that they are not in the minimal form.

  Two key attributes of any polyhedron are its topological kind
  (recording whether it is a C_Polyhedron or an NNC_Polyhedron object)
  and its space dimension (the dimension \f$n \in \Nset\f$ of
  the enclosing vector space):

  - all polyhedra, the empty ones included, are endowed with
    a specific topology and space dimension;
  - most operations working on a polyhedron and another object
    (i.e., another polyhedron, a constraint or generator,
    a set of variables, etc.) will throw an exception if
    the polyhedron and the object are not both topology-compatible
    and dimension-compatible (see Section \ref representation);
  - the topology of a polyhedron cannot be changed;
    rather, there are constructors for each of the two derived classes
    that will build a new polyhedron with the topology of that class
    from another polyhedron from either class and any topology;
  - the only ways in which the space dimension of a polyhedron can
    be changed are:
    - <EM>explicit</EM> calls to operators provided for that purpose;
    - standard copy, assignment and swap operators.

  Note that four different polyhedra can be defined on
  the zero-dimension space:
  the empty polyhedron, either closed or NNC,
  and the universe polyhedron \f$R^0\f$, again either closed or NNC.

  \par
  In all the examples it is assumed that variables
  <CODE>x</CODE> and <CODE>y</CODE> are defined (where they are
  used) as follows:
  \code
  Variable x(0);
  Variable y(1);
  \endcode

  \par Example 1
  The following code builds a polyhedron corresponding to
  a square in \f$\Rset^2\f$, given as a system of constraints:
  \code
  Constraint_System cs;
  cs.insert(x >= 0);
  cs.insert(x <= 3);
  cs.insert(y >= 0);
  cs.insert(y <= 3);
  C_Polyhedron ph(cs);
  \endcode
  The following code builds the same polyhedron as above,
  but starting from a system of generators specifying
  the four vertices of the square:
  \code
  Generator_System gs;
  gs.insert(point(0*x + 0*y));
  gs.insert(point(0*x + 3*y));
  gs.insert(point(3*x + 0*y));
  gs.insert(point(3*x + 3*y));
  C_Polyhedron ph(gs);
  \endcode

  \par Example 2
  The following code builds an unbounded polyhedron
  corresponding to a half-strip in \f$\Rset^2\f$,
  given as a system of constraints:
  \code
  Constraint_System cs;
  cs.insert(x >= 0);
  cs.insert(x - y <= 0);
  cs.insert(x - y + 1 >= 0);
  C_Polyhedron ph(cs);
  \endcode
  The following code builds the same polyhedron as above,
  but starting from the system of generators specifying
  the two vertices of the polyhedron and one ray:
  \code
  Generator_System gs;
  gs.insert(point(0*x + 0*y));
  gs.insert(point(0*x + y));
  gs.insert(ray(x - y));
  C_Polyhedron ph(gs);
  \endcode

  \par Example 3
  The following code builds the polyhedron corresponding to
  a half-plane by adding a single constraint
  to the universe polyhedron in \f$\Rset^2\f$:
  \code
  C_Polyhedron ph(2);
  ph.add_constraint(y >= 0);
  \endcode
  The following code builds the same polyhedron as above,
  but starting from the empty polyhedron in the space \f$\Rset^2\f$
  and inserting the appropriate generators
  (a point, a ray and a line).
  \code
  C_Polyhedron ph(2, EMPTY);
  ph.add_generator(point(0*x + 0*y));
  ph.add_generator(ray(y));
  ph.add_generator(line(x));
  \endcode
  Note that, although the above polyhedron has no vertices, we must add
  one point, because otherwise the result of the Minkowski's sum
  would be an empty polyhedron.
  To avoid subtle errors related to the minimization process,
  it is required that the first generator inserted in an empty
  polyhedron is a point (otherwise, an exception is thrown).

  \par Example 4
  The following code shows the use of the function
  <CODE>add_space_dimensions_and_embed</CODE>:
  \code
  C_Polyhedron ph(1);
  ph.add_constraint(x == 2);
  ph.add_space_dimensions_and_embed(1);
  \endcode
  We build the universe polyhedron in the 1-dimension space \f$\Rset\f$.
  Then we add a single equality constraint,
  thus obtaining the polyhedron corresponding to the singleton set
  \f$\{ 2 \} \sseq \Rset\f$.
  After the last line of code, the resulting polyhedron is
  \f[
    \bigl\{\,
      (2, y)^\transpose \in \Rset^2
    \bigm|
      y \in \Rset
    \,\bigr\}.
  \f]

  \par Example 5
  The following code shows the use of the function
  <CODE>add_space_dimensions_and_project</CODE>:
  \code
  C_Polyhedron ph(1);
  ph.add_constraint(x == 2);
  ph.add_space_dimensions_and_project(1);
  \endcode
  The first two lines of code are the same as in Example 4 for
  <CODE>add_space_dimensions_and_embed</CODE>.
  After the last line of code, the resulting polyhedron is
  the singleton set
  \f$\bigl\{ (2, 0)^\transpose \bigr\} \sseq \Rset^2\f$.

  \par Example 6
  The following code shows the use of the function
  <CODE>affine_image</CODE>:
  \code
  C_Polyhedron ph(2, EMPTY);
  ph.add_generator(point(0*x + 0*y));
  ph.add_generator(point(0*x + 3*y));
  ph.add_generator(point(3*x + 0*y));
  ph.add_generator(point(3*x + 3*y));
  Linear_Expression expr = x + 4;
  ph.affine_image(x, expr);
  \endcode
  In this example the starting polyhedron is a square in
  \f$\Rset^2\f$, the considered variable is \f$x\f$ and the affine
  expression is \f$x+4\f$.  The resulting polyhedron is the same
  square translated to the right.  Moreover, if the affine
  transformation for the same variable \p x is \f$x+y\f$:
  \code
  Linear_Expression expr = x + y;
  \endcode
  the resulting polyhedron is a parallelogram with the height equal to
  the side of the square and the oblique sides parallel to the line
  \f$x-y\f$.
  Instead, if we do not use an invertible transformation for the same
  variable; for example, the affine expression \f$y\f$:
  \code
  Linear_Expression expr = y;
  \endcode
  the resulting polyhedron is a diagonal of the square.

  \par Example 7
  The following code shows the use of the function
  <CODE>affine_preimage</CODE>:
  \code
  C_Polyhedron ph(2);
  ph.add_constraint(x >= 0);
  ph.add_constraint(x <= 3);
  ph.add_constraint(y >= 0);
  ph.add_constraint(y <= 3);
  Linear_Expression expr = x + 4;
  ph.affine_preimage(x, expr);
  \endcode
  In this example the starting polyhedron, \p var and the affine
  expression and the denominator are the same as in Example 6,
  while the resulting polyhedron is again the same square,
  but translated to the left.
  Moreover, if the affine transformation for \p x is \f$x+y\f$
  \code
  Linear_Expression expr = x + y;
  \endcode
  the resulting polyhedron is a parallelogram with the height equal to
  the side of the square and the oblique sides parallel to the line
  \f$x+y\f$.
  Instead, if we do not use an invertible transformation for the same
  variable \p x, for example, the affine expression \f$y\f$:
  \code
  Linear_Expression expr = y;
  \endcode
  the resulting polyhedron is a line that corresponds to the \f$y\f$ axis.

  \par Example 8
  For this example we use also the variables:
  \code
  Variable z(2);
  Variable w(3);
  \endcode
  The following code shows the use of the function
  <CODE>remove_space_dimensions</CODE>:
  \code
  Generator_System gs;
  gs.insert(point(3*x + y + 0*z + 2*w));
  C_Polyhedron ph(gs);
  Variables_Set vars;
  vars.insert(y);
  vars.insert(z);
  ph.remove_space_dimensions(vars);
  \endcode
  The starting polyhedron is the singleton set
  \f$\bigl\{ (3, 1, 0, 2)^\transpose \bigr\} \sseq \Rset^4\f$, while
  the resulting polyhedron is
  \f$\bigl\{ (3, 2)^\transpose \bigr\} \sseq \Rset^2\f$.
  Be careful when removing space dimensions <EM>incrementally</EM>:
  since dimensions are automatically renamed after each application
  of the <CODE>remove_space_dimensions</CODE> operator, unexpected
  results can be obtained.
  For instance, by using the following code we would obtain
  a different result:
  \code
  set<Variable> vars1;
  vars1.insert(y);
  ph.remove_space_dimensions(vars1);
  set<Variable> vars2;
  vars2.insert(z);
  ph.remove_space_dimensions(vars2);
  \endcode
  In this case, the result is the polyhedron
  \f$\bigl\{(3, 0)^\transpose \bigr\} \sseq \Rset^2\f$:
  when removing the set of dimensions \p vars2
  we are actually removing variable \f$w\f$ of the original polyhedron.
  For the same reason, the operator \p remove_space_dimensions
  is not idempotent: removing twice the same non-empty set of dimensions
  is never the same as removing them just once.
*/

class Parma_Polyhedra_Library::Polyhedron {
public:
  //! The numeric type of coefficients.
  typedef Coefficient coefficient_type;

  //! Returns the maximum space dimension all kinds of Polyhedron can handle.
  static dimension_type max_space_dimension();

  /*! \brief
    Returns \c true indicating that this domain has methods that
    can recycle constraints.
  */
  static bool can_recycle_constraint_systems();

  //! Initializes the class.
  static void initialize();

  //! Finalizes the class.
  static void finalize();

  /*! \brief
    Returns \c false indicating that this domain cannot recycle congruences.
  */
  static bool can_recycle_congruence_systems();

protected:
  //! Builds a polyhedron having the specified properties.
  /*!
    \param topol
    The topology of the polyhedron;

    \param num_dimensions
    The number of dimensions of the vector space enclosing the polyhedron;

    \param kind
    Specifies whether the universe or the empty polyhedron has to be built.
  */
  Polyhedron(Topology topol,
             dimension_type num_dimensions,
             Degenerate_Element kind);

  //! Ordinary copy constructor.
  /*!
    The complexity argument is ignored.
  */
  Polyhedron(const Polyhedron& y,
             Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a polyhedron from a system of constraints.
  /*!
    The polyhedron inherits the space dimension of the constraint system.

    \param topol
    The topology of the polyhedron;

    \param cs
    The system of constraints defining the polyhedron.

    \exception std::invalid_argument
    Thrown if the topology of \p cs is incompatible with \p topol.
  */
  Polyhedron(Topology topol, const Constraint_System& cs);

  //! Builds a polyhedron recycling a system of constraints.
  /*!
    The polyhedron inherits the space dimension of the constraint system.

    \param topol
    The topology of the polyhedron;

    \param cs
    The system of constraints defining the polyhedron.  It is not
    declared <CODE>const</CODE> because its data-structures may be
    recycled to build the polyhedron.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.

    \exception std::invalid_argument
    Thrown if the topology of \p cs is incompatible with \p topol.
  */
  Polyhedron(Topology topol, Constraint_System& cs, Recycle_Input dummy);

  //! Builds a polyhedron from a system of generators.
  /*!
    The polyhedron inherits the space dimension of the generator system.

    \param topol
    The topology of the polyhedron;

    \param gs
    The system of generators defining the polyhedron.

    \exception std::invalid_argument
    Thrown if the topology of \p gs is incompatible with \p topol,
    or if the system of generators is not empty but has no points.
  */
  Polyhedron(Topology topol, const Generator_System& gs);

  //! Builds a polyhedron recycling a system of generators.
  /*!
    The polyhedron inherits the space dimension of the generator system.

    \param topol
    The topology of the polyhedron;

    \param gs
    The system of generators defining the polyhedron.  It is not
    declared <CODE>const</CODE> because its data-structures may be
    recycled to build the polyhedron.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.

    \exception std::invalid_argument
    Thrown if the topology of \p gs is incompatible with \p topol,
    or if the system of generators is not empty but has no points.
  */
  Polyhedron(Topology topol, Generator_System& gs, Recycle_Input dummy);

  //! Builds a polyhedron from a box.
  /*!
    This will use an algorithm whose complexity is polynomial and build
    the smallest polyhedron with topology \p topol containing \p box.

    \param topol
    The topology of the polyhedron;

    \param box
    The box representing the polyhedron to be built;

    \param complexity
    This argument is ignored.
  */
  template <typename Interval>
  Polyhedron(Topology topol, const Box<Interval>& box,
             Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    The assignment operator.
    (\p *this and \p y can be dimension-incompatible.)
  */
  Polyhedron& operator=(const Polyhedron& y);

public:
  //! \name Member Functions that Do Not Modify the Polyhedron
  //@{

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  /*! \brief
    Returns \f$0\f$, if \p *this is empty; otherwise, returns the
    \ref Affine_Independence_and_Affine_Dimension "affine dimension"
    of \p *this.
  */
  dimension_type affine_dimension() const;

  //! Returns the system of constraints.
  const Constraint_System& constraints() const;

  //! Returns the system of constraints, with no redundant constraint.
  const Constraint_System& minimized_constraints() const;

  //! Returns the system of generators.
  const Generator_System& generators() const;

  //! Returns the system of generators, with no redundant generator.
  const Generator_System& minimized_generators() const;

  //! Returns a system of (equality) congruences satisfied by \p *this.
  Congruence_System congruences() const;

  /*! \brief
    Returns a system of (equality) congruences satisfied by \p *this,
    with no redundant congruences and having the same affine dimension
    as \p *this.
  */
  Congruence_System minimized_congruences() const;

  /*! \brief
    Returns the relations holding between the polyhedron \p *this
    and the constraint \p c.

    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible.
  */
  Poly_Con_Relation relation_with(const Constraint& c) const;

  /*! \brief
    Returns the relations holding between the polyhedron \p *this
    and the generator \p g.

    \exception std::invalid_argument
    Thrown if \p *this and generator \p g are dimension-incompatible.
  */
  Poly_Gen_Relation relation_with(const Generator& g) const;

  /*! \brief
    Returns the relations holding between the polyhedron \p *this
    and the congruence \p c.

    \exception std::invalid_argument
    Thrown if \p *this and congruence \p c are dimension-incompatible.
  */
  Poly_Con_Relation relation_with(const Congruence& cg) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is
    an empty polyhedron.
  */
  bool is_empty() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    is a universe polyhedron.
  */
  bool is_universe() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    is a topologically closed subset of the vector space.
  */
  bool is_topologically_closed() const;

  //! Returns <CODE>true</CODE> if and only if \p *this and \p y are disjoint.
  /*!
    \exception std::invalid_argument
    Thrown if \p x and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool is_disjoint_from(const Polyhedron& y) const;

  //! Returns <CODE>true</CODE> if and only if \p *this is discrete.
  bool is_discrete() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    is a bounded polyhedron.
  */
  bool is_bounded() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    contains at least one integer point.
  */
  bool contains_integer_point() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p var is constrained in
    \p *this.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  bool constrains(Variable var) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p expr is
    bounded from above in \p *this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_above(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p expr is
    bounded from below in \p *this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_below(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from above in \p *this, in which case
    the supremum value is computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if and only if the supremum is also the maximum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from above,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d
    and \p maximum are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from above in \p *this, in which case
    the supremum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if and only if the supremum is also the maximum value;

    \param g
    When maximization succeeds, will be assigned the point or
    closure point where \p expr reaches its supremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from above,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d, \p maximum
    and \p g are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from below in \p *this, in which case
    the infimum value is computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if and only if the infimum is also the minimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d
    and \p minimum are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from below in \p *this, in which case
    the infimum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if and only if the infimum is also the minimum value;

    \param g
    When minimization succeeds, will be assigned a point or
    closure point where \p expr reaches its infimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d, \p minimum
    and \p g are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if there exist a
    unique value \p val such that \p *this
    saturates the equality <CODE>expr = val</CODE>.

    \param expr
    The linear expression for which the frequency is needed;

    \param freq_n
    If <CODE>true</CODE> is returned, the value is set to \f$0\f$;
    Present for interface compatibility with class Grid, where
    the \ref Grid_Frequency "frequency" can have a non-zero value;

    \param freq_d
    If <CODE>true</CODE> is returned, the value is set to \f$1\f$;

    \param val_n
    The numerator of \p val;

    \param val_d
    The denominator of \p val;

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If <CODE>false</CODE> is returned, then \p freq_n, \p freq_d,
    \p val_n and \p val_d are left untouched.
  */
  bool frequency(const Linear_Expression& expr,
                 Coefficient& freq_n, Coefficient& freq_d,
                 Coefficient& val_n, Coefficient& val_d) const;

  //! Returns <CODE>true</CODE> if and only if \p *this contains \p y.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool contains(const Polyhedron& y) const;

  //! Returns <CODE>true</CODE> if and only if \p *this strictly contains \p y.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool strictly_contains(const Polyhedron& y) const;

  //! Checks if all the invariants are satisfied.
  /*!
    \return
    <CODE>true</CODE> if and only if \p *this satisfies all the
    invariants and either \p check_not_empty is <CODE>false</CODE> or
    \p *this is not empty.

    \param check_not_empty
    <CODE>true</CODE> if and only if, in addition to checking the
    invariants, \p *this must be checked to be not empty.

    The check is performed so as to intrude as little as possible.  If
    the library has been compiled with run-time assertions enabled,
    error messages are written on <CODE>std::cerr</CODE> in case
    invariants are violated. This is useful for the purpose of
    debugging the library.
  */
  bool OK(bool check_not_empty = false) const;

  //@} // Member Functions that Do Not Modify the Polyhedron

  //! \name Space Dimension Preserving Member Functions that May Modify the Polyhedron
  //@{

  /*! \brief
    Adds a copy of constraint \p c to the system of constraints
    of \p *this (without minimizing the result).

    \param c
    The constraint that will be added to the system of
    constraints of \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are topology-incompatible
    or dimension-incompatible.
  */
  void add_constraint(const Constraint& c);

  /*! \brief
    Adds a copy of generator \p g to the system of generators
    of \p *this (without minimizing the result).

    \exception std::invalid_argument
    Thrown if \p *this and generator \p g are topology-incompatible or
    dimension-incompatible, or if \p *this is an empty polyhedron and
    \p g is not a point.
  */
  void add_generator(const Generator& g);

  /*! \brief
    Adds a copy of congruence \p cg to \p *this,
    if \p cg can be exactly represented by a polyhedron.

    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible,
    of if \p cg is a proper congruence which is neither a tautology,
    nor a contradiction.
  */
  void add_congruence(const Congruence& cg);

  /*! \brief
    Adds a copy of the constraints in \p cs to the system
    of constraints of \p *this (without minimizing the result).

    \param cs
    Contains the constraints that will be added to the system of
    constraints of \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are topology-incompatible or
    dimension-incompatible.
  */
  void add_constraints(const Constraint_System& cs);

  /*! \brief
    Adds the constraints in \p cs to the system of constraints
    of \p *this (without minimizing the result).

    \param cs
    The constraint system to be added to \p *this.  The constraints in
    \p cs may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are topology-incompatible or
    dimension-incompatible.

    \warning
    The only assumption that can be made on \p cs upon successful or
    exceptional return is that it can be safely destroyed.
  */
  void add_recycled_constraints(Constraint_System& cs);

  /*! \brief
    Adds a copy of the generators in \p gs to the system
    of generators of \p *this (without minimizing the result).

    \param gs
    Contains the generators that will be added to the system of
    generators of \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p gs are topology-incompatible or
    dimension-incompatible, or if \p *this is empty and the system of
    generators \p gs is not empty, but has no points.
  */
  void add_generators(const Generator_System& gs);

  /*! \brief
    Adds the generators in \p gs to the system of generators
    of \p *this (without minimizing the result).

    \param gs
    The generator system to be added to \p *this.  The generators in
    \p gs may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p gs are topology-incompatible or
    dimension-incompatible, or if \p *this is empty and the system of
    generators \p gs is not empty, but has no points.

    \warning
    The only assumption that can be made on \p gs upon successful or
    exceptional return is that it can be safely destroyed.
  */
  void add_recycled_generators(Generator_System& gs);

  /*! \brief
    Adds a copy of the congruences in \p cgs to \p *this,
    if all the congruences can be exactly represented by a polyhedron.

    \param cgs
    The congruences to be added.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible,
    of if there exists in \p cgs a proper congruence which is
    neither a tautology, nor a contradiction.
  */
  void add_congruences(const Congruence_System& cgs);

  /*! \brief
    Adds the congruences in \p cgs to \p *this,
    if all the congruences can be exactly represented by a polyhedron.

    \param cgs
    The congruences to be added. Its elements may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible,
    of if there exists in \p cgs a proper congruence which is
    neither a tautology, nor a contradiction

    \warning
    The only assumption that can be made on \p cgs upon successful or
    exceptional return is that it can be safely destroyed.
  */
  void add_recycled_congruences(Congruence_System& cgs);

  /*! \brief
    Uses a copy of constraint \p c to refine \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible.
  */
  void refine_with_constraint(const Constraint& c);

  /*! \brief
    Uses a copy of congruence \p cg to refine \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible.
  */
  void refine_with_congruence(const Congruence& cg);

  /*! \brief
    Uses a copy of the constraints in \p cs to refine \p *this.

    \param cs
    Contains the constraints used to refine the system of
    constraints of \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible.
  */
  void refine_with_constraints(const Constraint_System& cs);

  /*! \brief
    Uses a copy of the congruences in \p cgs to refine \p *this.

    \param cgs
    Contains the congruences used to refine the system of
    constraints of \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible.
  */
  void refine_with_congruences(const Congruence_System& cgs);

  /*! \brief
    Refines \p *this with the constraint expressed by \p left \f$<\f$
    \p right if \p is_strict is set, with the constraint \p left \f$\leq\f$
    \p right otherwise.

    \param left
    The linear form on intervals with floating point boundaries that
    is on the left of the comparison operator. All of its coefficients
    MUST be bounded.

    \param right
    The linear form on intervals with floating point boundaries that
    is on the right of the comparison operator. All of its coefficients
    MUST be bounded.

    \param is_strict
    True if the comparison is strict.

    \exception std::invalid_argument
    Thrown if \p left (or \p right) is dimension-incompatible with \p *this.

    This function is used in abstract interpretation to model a filter
    that is generated by a comparison of two expressions that are correctly
    approximated by \p left and \p right respectively.
  */
  template <typename FP_Format, typename Interval_Info>
  void refine_with_linear_form_inequality(
  const Linear_Form< Interval<FP_Format, Interval_Info> >& left,
  const Linear_Form< Interval<FP_Format, Interval_Info> >& right,
  bool is_strict = false);

  /*! \brief
    Refines \p *this with the constraint expressed by \p left \f$\relsym\f$
    \p right, where \f$\relsym\f$ is the relation symbol specified by
    \p relsym..

    \param left
    The linear form on intervals with floating point boundaries that
    is on the left of the comparison operator. All of its coefficients
    MUST be bounded.

    \param right
    The linear form on intervals with floating point boundaries that
    is on the right of the comparison operator. All of its coefficients
    MUST be bounded.

    \param relsym
    The relation symbol.

    \exception std::invalid_argument
    Thrown if \p left (or \p right) is dimension-incompatible with \p *this.

    \exception std::runtime_error
    Thrown if \p relsym is not a valid relation symbol.

    This function is used in abstract interpretation to model a filter
    that is generated by a comparison of two expressions that are correctly
    approximated by \p left and \p right respectively.
  */
  template <typename FP_Format, typename Interval_Info>
  void generalized_refine_with_linear_form_inequality(
  const Linear_Form< Interval<FP_Format, Interval_Info> >& left,
  const Linear_Form< Interval<FP_Format, Interval_Info> >& right,
  Relation_Symbol relsym);

  //! Refines \p store with the constraints defining \p *this.
  /*!
    \param store
    The interval floating point abstract store to refine.
  */
  template <typename FP_Format, typename Interval_Info>
  void refine_fp_interval_abstract_store(
       Box< Interval<FP_Format, Interval_Info> >& store)
       const;

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to space dimension \p var, assigning the result to \p *this.

    \param var
    The space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  void unconstrain(Variable var);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to the set of space dimensions \p vars,
    assigning the result to \p *this.

    \param vars
    The set of space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars.
  */
  void unconstrain(const Variables_Set& vars);

  /*! \brief
    Assigns to \p *this the intersection of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  void intersection_assign(const Polyhedron& y);

  /*! \brief
    Assigns to \p *this the poly-hull of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  void poly_hull_assign(const Polyhedron& y);

  //! Same as poly_hull_assign(y).
  void upper_bound_assign(const Polyhedron& y);

  /*! \brief
    Assigns to \p *this
    the \ref Convex_Polyhedral_Difference "poly-difference"
    of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  void poly_difference_assign(const Polyhedron& y);

  //! Same as poly_difference_assign(y).
  void difference_assign(const Polyhedron& y);

  /*! \brief
    Assigns to \p *this a \ref Meet_Preserving_Simplification
    "meet-preserving simplification" of \p *this with respect to \p y.
    If \c false is returned, then the intersection is empty.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool simplify_using_context_assign(const Polyhedron& y);

  /*! \brief
    Assigns to \p *this the
    \ref Single_Update_Affine_Functions "affine image"
    of \p *this under the function mapping variable \p var to the
    affine expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is assigned;

    \param expr
    The numerator of the affine expression;

    \param denominator
    The denominator of the affine expression (optional argument with
    default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of
    \p *this.

    \if Include_Implementation_Details

    When considering the generators of a polyhedron, the
    affine transformation
    \f[
      \frac{\sum_{i=0}^{n-1} a_i x_i + b}{\mathrm{denominator}}
    \f]
    is assigned to \p var where \p expr is
    \f$\sum_{i=0}^{n-1} a_i x_i + b\f$
    (\f$b\f$ is the inhomogeneous term).

    If constraints are up-to-date, it uses the specialized function
    affine_preimage() (for the system of constraints)
    and inverse transformation to reach the same result.
    To obtain the inverse transformation we use the following observation.

    Observation:
    -# The affine transformation is invertible if the coefficient
       of \p var in this transformation (i.e., \f$a_\mathrm{var}\f$)
       is different from zero.
    -# If the transformation is invertible, then we can write
       \f[
         \mathrm{denominator} * {x'}_\mathrm{var}
           = \sum_{i = 0}^{n - 1} a_i x_i + b
           = a_\mathrm{var} x_\mathrm{var}
             + \sum_{i \neq var} a_i x_i + b,
       \f]
       so that the inverse transformation is
       \f[
         a_\mathrm{var} x_\mathrm{var}
           = \mathrm{denominator} * {x'}_\mathrm{var}
             - \sum_{i \neq j} a_i x_i - b.
       \f]

    Then, if the transformation is invertible, all the entities that
    were up-to-date remain up-to-date. Otherwise only generators remain
    up-to-date.

    In other words, if \f$R\f$ is a \f$m_1 \times n\f$ matrix representing
    the rays of the polyhedron, \f$V\f$ is a \f$m_2 \times n\f$
    matrix representing the points of the polyhedron and
    \f[
      P = \bigl\{\,
            \vect{x} = (x_0, \ldots, x_{n-1})^\mathrm{T}
          \bigm|
            \vect{x} = \vect{\lambda} R + \vect{\mu} V,
            \vect{\lambda} \in \Rset^{m_1}_+,
            \vect{\mu} \in \Rset^{m_2}_+,
            \sum_{i = 0}^{m_2 - 1} \mu_i = 1
          \,\bigr\}
    \f]
    and \f$T\f$ is the affine transformation to apply to \f$P\f$, then
    the resulting polyhedron is
    \f[
      P' = \bigl\{\,
             (x_0, \ldots, T(x_0, \ldots, x_{n-1}),
                     \ldots, x_{n-1})^\mathrm{T}
           \bigm|
             (x_0, \ldots, x_{n-1})^\mathrm{T} \in P
           \,\bigr\}.
    \f]

    Affine transformations are, for example:
    - translations
    - rotations
    - symmetries.
    \endif
  */
  void affine_image(Variable var,
                    const Linear_Expression& expr,
                    Coefficient_traits::const_reference denominator
                      = Coefficient_one());

  // FIXME: To be completed.
  /*!
    Assigns to \p *this the
    \ref affine_form_relation "affine form image"
    of \p *this under the function mapping variable \p var into the
    affine expression(s) specified by \p lf.

    \param var
    The variable to which the affine expression is assigned.

    \param lf
    The linear form on intervals with floating point boundaries that
    defines the affine expression(s). ALL of its coefficients MUST be bounded.

    \exception std::invalid_argument
    Thrown if \p lf and \p *this are dimension-incompatible or if \p var is
    not a space dimension of \p *this.

    This function is used in abstract interpretation to model an assignment
    of a value that is correctly overapproximated by \p lf to the
    floating point variable represented by \p var.
  */
  template <typename FP_Format, typename Interval_Info>
  void affine_form_image(Variable var,
  const Linear_Form<Interval <FP_Format, Interval_Info> >& lf);

  /*! \brief
    Assigns to \p *this the
    \ref Single_Update_Affine_Functions "affine preimage"
    of \p *this under the function mapping variable \p var to the
    affine expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is substituted;

    \param expr
    The numerator of the affine expression;

    \param denominator
    The denominator of the affine expression (optional argument with
    default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this.

    \if Include_Implementation_Details

    When considering constraints of a polyhedron, the affine transformation
    \f[
      \frac{\sum_{i=0}^{n-1} a_i x_i + b}{denominator},
    \f]
    is assigned to \p var where \p expr is
    \f$\sum_{i=0}^{n-1} a_i x_i + b\f$
    (\f$b\f$ is the inhomogeneous term).

    If generators are up-to-date, then the specialized function
    affine_image() is used (for the system of generators)
    and inverse transformation to reach the same result.
    To obtain the inverse transformation, we use the following observation.

    Observation:
    -# The affine transformation is invertible if the coefficient
       of \p var in this transformation (i.e. \f$a_\mathrm{var}\f$)
       is different from zero.
    -# If the transformation is invertible, then we can write
       \f[
         \mathrm{denominator} * {x'}_\mathrm{var}
           = \sum_{i = 0}^{n - 1} a_i x_i + b
           = a_\mathrm{var} x_\mathrm{var}
               + \sum_{i \neq \mathrm{var}} a_i x_i + b,
       \f],
       the inverse transformation is
       \f[
         a_\mathrm{var} x_\mathrm{var}
           = \mathrm{denominator} * {x'}_\mathrm{var}
               - \sum_{i \neq j} a_i x_i - b.
       \f].

    Then, if the transformation is invertible, all the entities that
    were up-to-date remain up-to-date. Otherwise only constraints remain
    up-to-date.

    In other words, if \f$A\f$ is a \f$m \times n\f$ matrix representing
    the constraints of the polyhedron, \f$T\f$ is the affine transformation
    to apply to \f$P\f$ and
    \f[
      P = \bigl\{\,
            \vect{x} = (x_0, \ldots, x_{n-1})^\mathrm{T}
          \bigm|
            A\vect{x} \geq \vect{0}
          \,\bigr\}.
    \f]
    The resulting polyhedron is
    \f[
      P' = \bigl\{\,
             \vect{x} = (x_0, \ldots, x_{n-1}))^\mathrm{T}
           \bigm|
             A'\vect{x} \geq \vect{0}
           \,\bigr\},
    \f]
    where \f$A'\f$ is defined as follows:
    \f[
      {a'}_{ij}
        = \begin{cases}
            a_{ij} * \mathrm{denominator} + a_{i\mathrm{var}}*\mathrm{expr}[j]
              \quad \mathrm{for } j \neq \mathrm{var}; \\
            \mathrm{expr}[\mathrm{var}] * a_{i\mathrm{var}},
              \quad \text{for } j = \mathrm{var}.
          \end{cases}
    \f]
    \endif
  */
  void affine_preimage(Variable var,
                       const Linear_Expression& expr,
                       Coefficient_traits::const_reference denominator
                         = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym.

    \param var
    The left hand side variable of the generalized affine relation;

    \param relsym
    The relation symbol;

    \param expr
    The numerator of the right hand side affine expression;

    \param denominator
    The denominator of the right hand side affine expression (optional
    argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void generalized_affine_image(Variable var,
                                Relation_Symbol relsym,
                                const Linear_Expression& expr,
                                Coefficient_traits::const_reference denominator
                                = Coefficient_one());

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym.

    \param var
    The left hand side variable of the generalized affine relation;

    \param relsym
    The relation symbol;

    \param expr
    The numerator of the right hand side affine expression;

    \param denominator
    The denominator of the right hand side affine expression (optional
    argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void
  generalized_affine_preimage(Variable var,
                              Relation_Symbol relsym,
                              const Linear_Expression& expr,
                              Coefficient_traits::const_reference denominator
                              = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.

    \param lhs
    The left hand side affine expression;

    \param relsym
    The relation symbol;

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void generalized_affine_image(const Linear_Expression& lhs,
                                Relation_Symbol relsym,
                                const Linear_Expression& rhs);

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.

    \param lhs
    The left hand side affine expression;

    \param relsym
    The relation symbol;

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void generalized_affine_preimage(const Linear_Expression& lhs,
                                   Relation_Symbol relsym,
                                   const Linear_Expression& rhs);

  /*!
    \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_image(Variable var,
                            const Linear_Expression& lb_expr,
                            const Linear_Expression& ub_expr,
                            Coefficient_traits::const_reference denominator
                            = Coefficient_one());

  /*!
    \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_preimage(Variable var,
                               const Linear_Expression& lb_expr,
                               const Linear_Expression& ub_expr,
                               Coefficient_traits::const_reference denominator
                               = Coefficient_one());

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref Time_Elapse_Operator "time-elapse" between \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  void time_elapse_assign(const Polyhedron& y);

  /*! \brief
    Assigns to \p *this (the best approximation of) the result of
    computing the
    \ref Positive_Time_Elapse_Operator "positive time-elapse"
    between \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void positive_time_elapse_assign(const Polyhedron& y);

  /*! \brief
    \ref Wrapping_Operator "Wraps" the specified dimensions of the
    vector space.

    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be wrapped.

    \param w
    The width of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param r
    The representation of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param o
    The overflow behavior of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param cs_p
    Possibly null pointer to a constraint system whose variables
    are contained in \p vars.  If <CODE>*cs_p</CODE> depends on
    variables not in \p vars, the behavior is undefined.
    When non-null, the pointed-to constraint system is assumed to
    represent the conditional or looping construct guard with respect
    to which wrapping is performed.  Since wrapping requires the
    computation of upper bounds and due to non-distributivity of
    constraint refinement over upper bounds, passing a constraint
    system in this way can be more precise than refining the result of
    the wrapping operation with the constraints in <CODE>*cs_p</CODE>.

    \param complexity_threshold
    A precision parameter of the \ref Wrapping_Operator "wrapping operator":
    higher values result in possibly improved precision.

    \param wrap_individually
    <CODE>true</CODE> if the dimensions should be wrapped individually
    (something that results in much greater efficiency to the detriment of
    precision).

    \exception std::invalid_argument
    Thrown if <CODE>*cs_p</CODE> is dimension-incompatible with
    \p vars, or if \p *this is dimension-incompatible \p vars or with
    <CODE>*cs_p</CODE>.
  */
  void wrap_assign(const Variables_Set& vars,
                   Bounded_Integer_Type_Width w,
                   Bounded_Integer_Type_Representation r,
                   Bounded_Integer_Type_Overflow o,
                   const Constraint_System* cs_p = 0,
                   unsigned complexity_threshold = 16,
                   bool wrap_individually = true);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates for the space dimensions corresponding to \p vars.

    \param vars
    Points with non-integer coordinates for these variables/space-dimensions
    can be discarded.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(const Variables_Set& vars,
                                    Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  //! Assigns to \p *this its topological closure.
  void topological_closure_assign();

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref BHRZ03_widening "BHRZ03-widening" between \p *this and \p y.

    \param y
    A polyhedron that <EM>must</EM> be contained in \p *this;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  void BHRZ03_widening_assign(const Polyhedron& y, unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref limited_extrapolation "limited extrapolation"
    between \p *this and \p y using the \ref BHRZ03_widening
    "BHRZ03-widening" operator.

    \param y
    A polyhedron that <EM>must</EM> be contained in \p *this;

    \param cs
    The system of constraints used to improve the widened polyhedron;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cs are topology-incompatible or
    dimension-incompatible.
  */
  void limited_BHRZ03_extrapolation_assign(const Polyhedron& y,
                                           const Constraint_System& cs,
                                           unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref bounded_extrapolation "bounded extrapolation"
    between \p *this and \p y using the \ref BHRZ03_widening
    "BHRZ03-widening" operator.

    \param y
    A polyhedron that <EM>must</EM> be contained in \p *this;

    \param cs
    The system of constraints used to improve the widened polyhedron;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cs are topology-incompatible or
    dimension-incompatible.
  */
  void bounded_BHRZ03_extrapolation_assign(const Polyhedron& y,
                                           const Constraint_System& cs,
                                           unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref H79_widening "H79_widening" between \p *this and \p y.

    \param y
    A polyhedron that <EM>must</EM> be contained in \p *this;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  void H79_widening_assign(const Polyhedron& y, unsigned* tp = 0);

  //! Same as H79_widening_assign(y, tp).
  void widening_assign(const Polyhedron& y, unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref limited_extrapolation "limited extrapolation"
    between \p *this and \p y using the \ref H79_widening
    "H79-widening" operator.

    \param y
    A polyhedron that <EM>must</EM> be contained in \p *this;

    \param cs
    The system of constraints used to improve the widened polyhedron;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cs are topology-incompatible or
    dimension-incompatible.
  */
  void limited_H79_extrapolation_assign(const Polyhedron& y,
                                        const Constraint_System& cs,
                                        unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref bounded_extrapolation "bounded extrapolation"
    between \p *this and \p y using the \ref H79_widening
    "H79-widening" operator.

    \param y
    A polyhedron that <EM>must</EM> be contained in \p *this;

    \param cs
    The system of constraints used to improve the widened polyhedron;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cs are topology-incompatible or
    dimension-incompatible.
  */
  void bounded_H79_extrapolation_assign(const Polyhedron& y,
                                        const Constraint_System& cs,
                                        unsigned* tp = 0);

  //@} // Space Dimension Preserving Member Functions that May Modify [...]

  //! \name Member Functions that May Modify the Dimension of the Vector Space
  //@{

  /*! \brief
    Adds \p m new space dimensions and embeds the old polyhedron
    in the new vector space.

    \param m
    The number of dimensions to add.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the
    vector space to exceed dimension <CODE>max_space_dimension()</CODE>.

    The new space dimensions will be those having the highest indexes
    in the new polyhedron, which is characterized by a system
    of constraints in which the variables running through
    the new dimensions are not constrained.
    For instance, when starting from the polyhedron \f$\cP \sseq \Rset^2\f$
    and adding a third space dimension, the result will be the polyhedron
    \f[
      \bigl\{\,
        (x, y, z)^\transpose \in \Rset^3
      \bigm|
        (x, y)^\transpose \in \cP
      \,\bigr\}.
    \f]
  */
  void add_space_dimensions_and_embed(dimension_type m);

  /*! \brief
    Adds \p m new space dimensions to the polyhedron
    and does not embed it in the new vector space.

    \param m
    The number of space dimensions to add.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the
    vector space to exceed dimension <CODE>max_space_dimension()</CODE>.

    The new space dimensions will be those having the highest indexes
    in the new polyhedron, which is characterized by a system
    of constraints in which the variables running through
    the new dimensions are all constrained to be equal to 0.
    For instance, when starting from the polyhedron \f$\cP \sseq \Rset^2\f$
    and adding a third space dimension, the result will be the polyhedron
    \f[
      \bigl\{\,
        (x, y, 0)^\transpose \in \Rset^3
      \bigm|
        (x, y)^\transpose \in \cP
      \,\bigr\}.
    \f]
  */
  void add_space_dimensions_and_project(dimension_type m);

  /*! \brief
    Assigns to \p *this the \ref Concatenating_Polyhedra "concatenation"
    of \p *this and \p y, taken in this order.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible.

    \exception std::length_error
    Thrown if the concatenation would cause the vector space
    to exceed dimension <CODE>max_space_dimension()</CODE>.
  */
  void concatenate_assign(const Polyhedron& y);

  //! Removes all the specified dimensions from the vector space.
  /*!
    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be removed.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  /*! \brief
    Removes the higher dimensions of the vector space so that
    the resulting space will have dimension \p new_dimension.

    \exception std::invalid_argument
    Thrown if \p new_dimensions is greater than the space dimension of
    \p *this.
  */
  void remove_higher_space_dimensions(dimension_type new_dimension);

  /*! \brief
    Remaps the dimensions of the vector space according to
    a \ref Mapping_the_Dimensions_of_the_Vector_Space "partial function".

    \param pfunc
    The partial function specifying the destiny of each space dimension.

    The template type parameter Partial_Function must provide
    the following methods.
    \code
      bool has_empty_codomain() const
    \endcode
    returns <CODE>true</CODE> if and only if the represented partial
    function has an empty codomain (i.e., it is always undefined).
    The <CODE>has_empty_codomain()</CODE> method will always be called
    before the methods below.  However, if
    <CODE>has_empty_codomain()</CODE> returns <CODE>true</CODE>, none
    of the functions below will be called.
    \code
      dimension_type max_in_codomain() const
    \endcode
    returns the maximum value that belongs to the codomain
    of the partial function.
    The <CODE>max_in_codomain()</CODE> method is called at most once.
    \code
      bool maps(dimension_type i, dimension_type& j) const
    \endcode
    Let \f$f\f$ be the represented function and \f$k\f$ be the value
    of \p i.  If \f$f\f$ is defined in \f$k\f$, then \f$f(k)\f$ is
    assigned to \p j and <CODE>true</CODE> is returned.
    If \f$f\f$ is undefined in \f$k\f$, then <CODE>false</CODE> is
    returned.
    This method is called at most \f$n\f$ times, where \f$n\f$ is the
    dimension of the vector space enclosing the polyhedron.

    The result is undefined if \p pfunc does not encode a partial
    function with the properties described in the
    \ref Mapping_the_Dimensions_of_the_Vector_Space
    "specification of the mapping operator".
  */
  template <typename Partial_Function>
  void map_space_dimensions(const Partial_Function& pfunc);

  //! Creates \p m copies of the space dimension corresponding to \p var.
  /*!
    \param var
    The variable corresponding to the space dimension to be replicated;

    \param m
    The number of replicas to be created.

    \exception std::invalid_argument
    Thrown if \p var does not correspond to a dimension of the vector space.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the
    vector space to exceed dimension <CODE>max_space_dimension()</CODE>.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    and <CODE>var</CODE> has space dimension \f$k \leq n\f$,
    then the \f$k\f$-th space dimension is
    \ref expand_space_dimension "expanded" to \p m new space dimensions
    \f$n\f$, \f$n+1\f$, \f$\dots\f$, \f$n+m-1\f$.
  */
  void expand_space_dimension(Variable var, dimension_type m);

  //! Folds the space dimensions in \p vars into \p dest.
  /*!
    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be folded;

    \param dest
    The variable corresponding to the space dimension that is the
    destination of the folding operation.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p dest or with
    one of the Variable objects contained in \p vars.
    Also thrown if \p dest is contained in \p vars.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    <CODE>dest</CODE> has space dimension \f$k \leq n\f$,
    \p vars is a set of variables whose maximum space dimension
    is also less than or equal to \f$n\f$, and \p dest is not a member
    of \p vars, then the space dimensions corresponding to
    variables in \p vars are \ref fold_space_dimensions "folded"
    into the \f$k\f$-th space dimension.
  */
  void fold_space_dimensions(const Variables_Set& vars, Variable dest);

  //@} // Member Functions that May Modify the Dimension of the Vector Space

  friend bool operator==(const Polyhedron& x, const Polyhedron& y);

  //! \name Miscellaneous Member Functions
  //@{

  //! Destructor.
  ~Polyhedron();

  /*! \brief
    Swaps \p *this with polyhedron \p y.
    (\p *this and \p y can be dimension-incompatible.)

    \exception std::invalid_argument
    Thrown if \p x and \p y are topology-incompatible.
  */
  void m_swap(Polyhedron& y);

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns a 32-bit hash code for \p *this.

    If \p x and \p y are such that <CODE>x == y</CODE>,
    then <CODE>x.hash_code() == y.hash_code()</CODE>.
  */
  int32_t hash_code() const;

  //@} // Miscellaneous Member Functions

private:
  static const Representation default_con_sys_repr = DENSE;
  static const Representation default_gen_sys_repr = DENSE;

  //! The system of constraints.
  Constraint_System con_sys;

  //! The system of generators.
  Generator_System gen_sys;

  //! The saturation matrix having constraints on its columns.
  Bit_Matrix sat_c;

  //! The saturation matrix having generators on its columns.
  Bit_Matrix sat_g;

#define PPL_IN_Polyhedron_CLASS
/* Automatically generated from PPL source file ../src/Ph_Status_idefs.hh line 1. */
/* Polyhedron::Status class declaration.
*/


#ifndef PPL_IN_Polyhedron_CLASS
#error "Do not include Ph_Status_idefs.hh directly; use Polyhedron_defs.hh instead"
#endif

//! A conjunctive assertion about a polyhedron.
/*! \ingroup PPL_CXX_interface
  The assertions supported are:
  - <EM>zero-dim universe</EM>: the polyhedron is the zero-dimension
    vector space \f$\Rset^0 = \{\cdot\}\f$;
  - <EM>empty</EM>: the polyhedron is the empty set;
  - <EM>constraints pending</EM>: the polyhedron is correctly
    characterized by the attached system of constraints, which is
    split in two non-empty subsets: the already processed constraints,
    which are in minimal form, and the pending constraints, which
    still have to be processed and may thus be inconsistent or
    contain redundancies;
  - <EM>generators pending</EM>: the polyhedron is correctly
    characterized by the attached system of generators, which is
    split in two non-empty subsets: the already processed generators,
    which are in minimal form, and the pending generators, which still
    have to be processed and may thus contain redundancies;
  - <EM>constraints up-to-date</EM>: the polyhedron is correctly
    characterized by the attached system of constraints, modulo the
    processing of pending generators;
  - <EM>generators up-to-date</EM>: the polyhedron is correctly
    characterized by the attached system of generators, modulo the
    processing of pending constraints;
  - <EM>constraints minimized</EM>: the non-pending part of the system
    of constraints attached to the polyhedron is in minimal form;
  - <EM>generators minimized</EM>: the non-pending part of the system
    of generators attached to the polyhedron is in minimal form;
  - <EM>constraints' saturation matrix up-to-date</EM>: the attached
    saturation matrix having rows indexed by non-pending generators and
    columns indexed by non-pending constraints correctly expresses
    the saturation relation between the attached non-pending constraints
    and generators;
  - <EM>generators' saturation matrix up-to-date</EM>: the attached
    saturation matrix having rows indexed by non-pending constraints and
    columns indexed by non-pending generators correctly expresses
    the saturation relation between the attached non-pending constraints
    and generators;

  Not all the conjunctions of these elementary assertions constitute
  a legal Status.  In fact:
  - <EM>zero-dim universe</EM> excludes any other assertion;
  - <EM>empty</EM>: excludes any other assertion;
  - <EM>constraints pending</EM> and <EM>generators pending</EM>
    are mutually exclusive;
  - <EM>constraints pending</EM> implies both <EM>constraints minimized</EM>
    and <EM>generators minimized</EM>;
  - <EM>generators pending</EM> implies both <EM>constraints minimized</EM>
    and <EM>generators minimized</EM>;
  - <EM>constraints minimized</EM> implies <EM>constraints up-to-date</EM>;
  - <EM>generators minimized</EM> implies <EM>generators up-to-date</EM>;
  - <EM>constraints' saturation matrix up-to-date</EM> implies both
    <EM>constraints up-to-date</EM> and <EM>generators up-to-date</EM>;
  - <EM>generators' saturation matrix up-to-date</EM> implies both
    <EM>constraints up-to-date</EM> and <EM>generators up-to-date</EM>.
*/
class Status {
public:
  //! By default Status is the <EM>zero-dim universe</EM> assertion.
  Status();

  //! \name Test, remove or add an individual assertion from the conjunction
  //@{
  bool test_zero_dim_univ() const;
  void reset_zero_dim_univ();
  void set_zero_dim_univ();

  bool test_empty() const;
  void reset_empty();
  void set_empty();

  bool test_c_up_to_date() const;
  void reset_c_up_to_date();
  void set_c_up_to_date();

  bool test_g_up_to_date() const;
  void reset_g_up_to_date();
  void set_g_up_to_date();

  bool test_c_minimized() const;
  void reset_c_minimized();
  void set_c_minimized();

  bool test_g_minimized() const;
  void reset_g_minimized();
  void set_g_minimized();

  bool test_sat_c_up_to_date() const;
  void reset_sat_c_up_to_date();
  void set_sat_c_up_to_date();

  bool test_sat_g_up_to_date() const;
  void reset_sat_g_up_to_date();
  void set_sat_g_up_to_date();

  bool test_c_pending() const;
  void reset_c_pending();
  void set_c_pending();

  bool test_g_pending() const;
  void reset_g_pending();
  void set_g_pending();
  //@} // Test, remove or add an individual assertion from the conjunction

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

private:
  //! Status is implemented by means of a finite bitset.
  typedef unsigned int flags_t;

  //! \name Bit-masks for the individual assertions
  //@{
  static const flags_t ZERO_DIM_UNIV    = 0U;
  static const flags_t EMPTY            = 1U << 0;
  static const flags_t C_UP_TO_DATE     = 1U << 1;
  static const flags_t G_UP_TO_DATE     = 1U << 2;
  static const flags_t C_MINIMIZED      = 1U << 3;
  static const flags_t G_MINIMIZED      = 1U << 4;
  static const flags_t SAT_C_UP_TO_DATE = 1U << 5;
  static const flags_t SAT_G_UP_TO_DATE = 1U << 6;
  static const flags_t CS_PENDING       = 1U << 7;
  static const flags_t GS_PENDING       = 1U << 8;
  //@} // Bit-masks for the individual assertions

  //! This holds the current bitset.
  flags_t flags;

  //! Construct from a bit-mask.
  Status(flags_t mask);

  //! Check whether <EM>all</EM> bits in \p mask are set.
  bool test_all(flags_t mask) const;

  //! Check whether <EM>at least one</EM> bit in \p mask is set.
  bool test_any(flags_t mask) const;

  //! Set the bits in \p mask.
  void set(flags_t mask);

  //! Reset the bits in \p mask.
  void reset(flags_t mask);
};

/* Automatically generated from PPL source file ../src/Polyhedron_defs.hh line 2043. */
#undef PPL_IN_Polyhedron_CLASS

  //! The status flags to keep track of the polyhedron's internal state.
  Status status;

  //! The number of dimensions of the enclosing vector space.
  dimension_type space_dim;

  //! Returns the topological kind of the polyhedron.
  Topology topology() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if the polyhedron
    is necessarily closed.
  */
  bool is_necessarily_closed() const;

  friend bool
  Parma_Polyhedra_Library::Interfaces
  ::is_necessarily_closed_for_interfaces(const Polyhedron&);

  /*! \brief
    Uses a copy of constraint \p c to refine the system of constraints
    of \p *this.

    \param c The constraint to be added. If it is dimension-incompatible
    with \p *this, the behavior is undefined.
  */
  void refine_no_check(const Constraint& c);

  //! \name Private Verifiers: Verify if Individual Flags are Set
  //@{

  //! Returns <CODE>true</CODE> if the polyhedron is known to be empty.
  /*!
    The return value <CODE>false</CODE> does not necessarily
    implies that \p *this is non-empty.
  */
  bool marked_empty() const;

  //! Returns <CODE>true</CODE> if the system of constraints is up-to-date.
  bool constraints_are_up_to_date() const;

  //! Returns <CODE>true</CODE> if the system of generators is up-to-date.
  bool generators_are_up_to_date() const;

  //! Returns <CODE>true</CODE> if the system of constraints is minimized.
  /*!
    Note that only \em weak minimization is entailed, so that
    an NNC polyhedron may still have \f$\epsilon\f$-redundant constraints.
  */
  bool constraints_are_minimized() const;

  //! Returns <CODE>true</CODE> if the system of generators is minimized.
  /*!
    Note that only \em weak minimization is entailed, so that
    an NNC polyhedron may still have \f$\epsilon\f$-redundant generators.
  */
  bool generators_are_minimized() const;

  //! Returns <CODE>true</CODE> if there are pending constraints.
  bool has_pending_constraints() const;

  //! Returns <CODE>true</CODE> if there are pending generators.
  bool has_pending_generators() const;

  /*! \brief
    Returns <CODE>true</CODE> if there are
    either pending constraints or pending generators.
  */
  bool has_something_pending() const;

  //! Returns <CODE>true</CODE> if the polyhedron can have something pending.
  bool can_have_something_pending() const;

  /*! \brief
    Returns <CODE>true</CODE> if the saturation matrix \p sat_c
    is up-to-date.
  */
  bool sat_c_is_up_to_date() const;

  /*! \brief
    Returns <CODE>true</CODE> if the saturation matrix \p sat_g
    is up-to-date.
  */
  bool sat_g_is_up_to_date() const;

  //@} // Private Verifiers: Verify if Individual Flags are Set

  //! \name State Flag Setters: Set Only the Specified Flags
  //@{

  /*! \brief
    Sets \p status to express that the polyhedron is the universe
    0-dimension vector space, clearing all corresponding matrices.
  */
  void set_zero_dim_univ();

  /*! \brief
    Sets \p status to express that the polyhedron is empty,
    clearing all corresponding matrices.
  */
  void set_empty();

  //! Sets \p status to express that constraints are up-to-date.
  void set_constraints_up_to_date();

  //! Sets \p status to express that generators are up-to-date.
  void set_generators_up_to_date();

  //! Sets \p status to express that constraints are minimized.
  void set_constraints_minimized();

  //! Sets \p status to express that generators are minimized.
  void set_generators_minimized();

  //! Sets \p status to express that constraints are pending.
  void set_constraints_pending();

  //! Sets \p status to express that generators are pending.
  void set_generators_pending();

  //! Sets \p status to express that \p sat_c is up-to-date.
  void set_sat_c_up_to_date();

  //! Sets \p status to express that \p sat_g is up-to-date.
  void set_sat_g_up_to_date();

  //@} // State Flag Setters: Set Only the Specified Flags

  //! \name State Flag Cleaners: Clear Only the Specified Flag
  //@{

  //! Clears the \p status flag indicating that the polyhedron is empty.
  void clear_empty();

  //! Sets \p status to express that constraints are no longer up-to-date.
  /*!
    This also implies that they are neither minimized
    and both saturation matrices are no longer meaningful.
  */
  void clear_constraints_up_to_date();

  //! Sets \p status to express that generators are no longer up-to-date.
  /*!
    This also implies that they are neither minimized
    and both saturation matrices are no longer meaningful.
  */
  void clear_generators_up_to_date();

  //! Sets \p status to express that constraints are no longer minimized.
  void clear_constraints_minimized();

  //! Sets \p status to express that generators are no longer minimized.
  void clear_generators_minimized();

  //! Sets \p status to express that there are no longer pending constraints.
  void clear_pending_constraints();

  //! Sets \p status to express that there are no longer pending generators.
  void clear_pending_generators();

  //! Sets \p status to express that \p sat_c is no longer up-to-date.
  void clear_sat_c_up_to_date();

  //! Sets \p status to express that \p sat_g is no longer up-to-date.
  void clear_sat_g_up_to_date();

  //@} // State Flag Cleaners: Clear Only the Specified Flag

  //! \name The Handling of Pending Rows
  //@{

  /*! \brief
    Processes the pending rows of either description of the polyhedron
    and obtains a minimized polyhedron.

    \return
    <CODE>false</CODE> if and only if \p *this turns out to be an
    empty polyhedron.

    It is assumed that the polyhedron does have some constraints or
    generators pending.
  */
  bool process_pending() const;

  //! Processes the pending constraints and obtains a minimized polyhedron.
  /*!
    \return
    <CODE>false</CODE> if and only if \p *this turns out to be an
    empty polyhedron.

    It is assumed that the polyhedron does have some pending constraints.
  */
  bool process_pending_constraints() const;

  //! Processes the pending generators and obtains a minimized polyhedron.
  /*!
    It is assumed that the polyhedron does have some pending generators.
  */
  void process_pending_generators() const;

  /*! \brief
    Lazily integrates the pending descriptions of the polyhedron
    to obtain a constraint system without pending rows.

    It is assumed that the polyhedron does have some constraints or
    generators pending.
  */
  void remove_pending_to_obtain_constraints() const;

  /*! \brief
    Lazily integrates the pending descriptions of the polyhedron
    to obtain a generator system without pending rows.

    \return
    <CODE>false</CODE> if and only if \p *this turns out to be an
    empty polyhedron.

    It is assumed that the polyhedron does have some constraints or
    generators pending.
  */
  bool remove_pending_to_obtain_generators() const;

  //@} // The Handling of Pending Rows

  //! \name Updating and Sorting Matrices
  //@{

  //! Updates constraints starting from generators and minimizes them.
  /*!
    The resulting system of constraints is only partially sorted:
    the equalities are in the upper part of the matrix,
    while the inequalities in the lower part.
  */
  void update_constraints() const;

  //! Updates generators starting from constraints and minimizes them.
  /*!
    \return
    <CODE>false</CODE> if and only if \p *this turns out to be an
    empty polyhedron.

    The resulting system of generators is only partially sorted:
    the lines are in the upper part of the matrix,
    while rays and points are in the lower part.
    It is illegal to call this method when the Status field
    already declares the polyhedron to be empty.
  */
  bool update_generators() const;

  //! Updates \p sat_c using the updated constraints and generators.
  /*!
    It is assumed that constraints and generators are up-to-date
    and minimized and that the Status field does not already flag
    \p sat_c to be up-to-date.
    The values of the saturation matrix are computed as follows:
    \f[
      \begin{cases}
        sat\_c[i][j] = 0,
          \quad \text{if } G[i] \cdot C^\mathrm{T}[j] = 0; \\
        sat\_c[i][j] = 1,
          \quad \text{if } G[i] \cdot C^\mathrm{T}[j] > 0.
      \end{cases}
    \f]
  */
  void update_sat_c() const;

  //! Updates \p sat_g using the updated constraints and generators.
  /*!
    It is assumed that constraints and generators are up-to-date
    and minimized and that the Status field does not already flag
    \p sat_g to be up-to-date.
    The values of the saturation matrix are computed as follows:
    \f[
      \begin{cases}
        sat\_g[i][j] = 0,
          \quad \text{if } C[i] \cdot G^\mathrm{T}[j] = 0; \\
        sat\_g[i][j] = 1,
          \quad \text{if } C[i] \cdot G^\mathrm{T}[j] > 0.
      \end{cases}
    \f]
  */
  void update_sat_g() const;

  //! Sorts the matrix of constraints keeping status consistency.
  /*!
    It is assumed that constraints are up-to-date.
    If at least one of the saturation matrices is up-to-date,
    then \p sat_g is kept consistent with the sorted matrix
    of constraints.
    The method is declared \p const because reordering
    the constraints does not modify the polyhedron
    from a \e logical point of view.
  */
  void obtain_sorted_constraints() const;

  //! Sorts the matrix of generators keeping status consistency.
  /*!
    It is assumed that generators are up-to-date.
    If at least one of the saturation matrices is up-to-date,
    then \p sat_c is kept consistent with the sorted matrix
    of generators.
    The method is declared \p const because reordering
    the generators does not modify the polyhedron
    from a \e logical point of view.
  */
  void obtain_sorted_generators() const;

  //! Sorts the matrix of constraints and updates \p sat_c.
  /*!
    It is assumed that both constraints and generators
    are up-to-date and minimized.
    The method is declared \p const because reordering
    the constraints does not modify the polyhedron
    from a \e logical point of view.
  */
  void obtain_sorted_constraints_with_sat_c() const;

  //! Sorts the matrix of generators and updates \p sat_g.
  /*!
    It is assumed that both constraints and generators
    are up-to-date and minimized.
    The method is declared \p const because reordering
    the generators does not modify the polyhedron
    from a \e logical point of view.
  */
  void obtain_sorted_generators_with_sat_g() const;

  //@} // Updating and Sorting Matrices

  //! \name Weak and Strong Minimization of Descriptions
  //@{

  //! Applies (weak) minimization to both the constraints and generators.
  /*!
    \return
    <CODE>false</CODE> if and only if \p *this turns out to be an
    empty polyhedron.

    Minimization is not attempted if the Status field already declares
    both systems to be minimized.
  */
  bool minimize() const;

  //! Applies strong minimization to the constraints of an NNC polyhedron.
  /*!
    \return
    <CODE>false</CODE> if and only if \p *this turns out to be an
    empty polyhedron.
  */
  bool strongly_minimize_constraints() const;

  //! Applies strong minimization to the generators of an NNC polyhedron.
  /*!
    \return
    <CODE>false</CODE> if and only if \p *this turns out to be an
    empty polyhedron.
  */
  bool strongly_minimize_generators() const;

  //! If constraints are up-to-date, obtain a simplified copy of them.
  Constraint_System simplified_constraints() const;

  //@} // Weak and Strong Minimization of Descriptions

  enum Three_Valued_Boolean {
    TVB_TRUE,
    TVB_FALSE,
    TVB_DONT_KNOW
  };

  //! Polynomial but incomplete equivalence test between polyhedra.
  Three_Valued_Boolean quick_equivalence_test(const Polyhedron& y) const;

  //! Returns <CODE>true</CODE> if and only if \p *this is included in \p y.
  bool is_included_in(const Polyhedron& y) const;

  //! Checks if and how \p expr is bounded in \p *this.
  /*!
    Returns <CODE>true</CODE> if and only if \p from_above is
    <CODE>true</CODE> and \p expr is bounded from above in \p *this,
    or \p from_above is <CODE>false</CODE> and \p expr is bounded
    from below in \p *this.

    \param expr
    The linear expression to test;

    \param from_above
    <CODE>true</CODE> if and only if the boundedness of interest is
    "from above".

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds(const Linear_Expression& expr, bool from_above) const;

  //! Maximizes or minimizes \p expr subject to \p *this.
  /*!
    \param expr
    The linear expression to be maximized or minimized subject to \p
    *this;

    \param maximize
    <CODE>true</CODE> if maximization is what is wanted;

    \param ext_n
    The numerator of the extremum value;

    \param ext_d
    The denominator of the extremum value;

    \param included
    <CODE>true</CODE> if and only if the extremum of \p expr can
    actually be reached in \p * this;

    \param g
    When maximization or minimization succeeds, will be assigned
    a point or closure point where \p expr reaches the
    corresponding extremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded in the appropriate
    direction, <CODE>false</CODE> is returned and \p ext_n, \p ext_d,
    \p included and \p g are left untouched.
  */
  bool max_min(const Linear_Expression& expr,
               bool maximize,
               Coefficient& ext_n, Coefficient& ext_d, bool& included,
               Generator& g) const;

  //! \name Widening- and Extrapolation-Related Functions
  //@{

  /*! \brief
    Copies to \p cs_selection the constraints of \p y corresponding
    to the definition of the CH78-widening of \p *this and \p y.
  */
  void select_CH78_constraints(const Polyhedron& y,
                               Constraint_System& cs_selection) const;

  /*! \brief
    Splits the constraints of `x' into two subsets, depending on whether
    or not they are selected to compute the \ref H79_widening "H79-widening"
    of \p *this and \p y.
  */
  void select_H79_constraints(const Polyhedron& y,
                              Constraint_System& cs_selected,
                              Constraint_System& cs_not_selected) const;

  bool BHRZ03_combining_constraints(const Polyhedron& y,
                                    const BHRZ03_Certificate& y_cert,
                                    const Polyhedron& H79,
                                    const Constraint_System& x_minus_H79_cs);

  bool BHRZ03_evolving_points(const Polyhedron& y,
                              const BHRZ03_Certificate& y_cert,
                              const Polyhedron& H79);

  bool BHRZ03_evolving_rays(const Polyhedron& y,
                            const BHRZ03_Certificate& y_cert,
                            const Polyhedron& H79);

  static void modify_according_to_evolution(Linear_Expression& ray,
                                            const Linear_Expression& x,
                                            const Linear_Expression& y);

  //@} // Widening- and Extrapolation-Related Functions

  //! Adds new space dimensions to the given linear systems.
  /*!
    \param sys1
    The linear system to which columns are added;

    \param sys2
    The linear system to which rows and columns are added;

    \param sat1
    The saturation matrix whose columns are indexed by the rows of
    \p sys1. On entry it is up-to-date;

    \param sat2
    The saturation matrix whose columns are indexed by the rows of \p
    sys2;

    \param add_dim
    The number of space dimensions to add.

    Adds new space dimensions to the vector space modifying the linear
    systems and saturation matrices.
    This function is invoked only by
    <CODE>add_space_dimensions_and_embed()</CODE> and
    <CODE>add_space_dimensions_and_project()</CODE>, passing the
    linear system of constraints and that of generators (and the
    corresponding saturation matrices) in different order (see those
    methods for details).
  */
  template <typename Linear_System1, typename Linear_System2>
  static void add_space_dimensions(Linear_System1& sys1,
                                   Linear_System2& sys2,
                                   Bit_Matrix& sat1,
                                   Bit_Matrix& sat2,
                                   dimension_type add_dim);

  //! \name Minimization-Related Static Member Functions
  //@{

  //! Builds and simplifies constraints from generators (or vice versa).
  // Detailed Doxygen comment to be found in file minimize.cc.
  template <typename Source_Linear_System, typename Dest_Linear_System>
  static bool minimize(bool con_to_gen,
                       Source_Linear_System& source,
                       Dest_Linear_System& dest,
                       Bit_Matrix& sat);

  /*! \brief
    Adds given constraints and builds minimized corresponding generators
    or vice versa.
  */
  // Detailed Doxygen comment to be found in file minimize.cc.
  template <typename Source_Linear_System1, typename Source_Linear_System2,
            typename Dest_Linear_System>
  static bool add_and_minimize(bool con_to_gen,
                               Source_Linear_System1& source1,
                               Dest_Linear_System& dest,
                               Bit_Matrix& sat,
                               const Source_Linear_System2& source2);

  /*! \brief
    Adds given constraints and builds minimized corresponding generators
    or vice versa. The given constraints are in \p source.
  */
  // Detailed Doxygen comment to be found in file minimize.cc.
  template <typename Source_Linear_System, typename Dest_Linear_System>
  static bool add_and_minimize(bool con_to_gen,
                               Source_Linear_System& source,
                               Dest_Linear_System& dest,
                               Bit_Matrix& sat);

  //! Performs the conversion from constraints to generators and vice versa.
  // Detailed Doxygen comment to be found in file conversion.cc.
  template <typename Source_Linear_System, typename Dest_Linear_System>
  static dimension_type conversion(Source_Linear_System& source,
                                   dimension_type start,
                                   Dest_Linear_System& dest,
                                   Bit_Matrix& sat,
                                   dimension_type num_lines_or_equalities);

  /*! \brief
    Uses Gauss' elimination method to simplify the result of
    <CODE>conversion()</CODE>.
  */
  // Detailed Doxygen comment to be found in file simplify.cc.
  template <typename Linear_System1>
  static dimension_type simplify(Linear_System1& sys, Bit_Matrix& sat);

  //@} // Minimization-Related Static Member Functions

  /*! \brief
    Pointer to an array used by simplify().

    Holds (between class initialization and finalization) a pointer to
    an array, allocated with operator new[](), of
    simplify_num_saturators_size elements.
  */
  static dimension_type* simplify_num_saturators_p;

  /*! \brief
    Dimension of an array used by simplify().

    Holds (between class initialization and finalization) the size of the
    array pointed to by simplify_num_saturators_p.
  */
  static size_t simplify_num_saturators_size;

  template <typename Interval> friend class Parma_Polyhedra_Library::Box;
  template <typename T> friend class Parma_Polyhedra_Library::BD_Shape;
  template <typename T> friend class Parma_Polyhedra_Library::Octagonal_Shape;
  friend class Parma_Polyhedra_Library::Grid;
  friend class Parma_Polyhedra_Library::BHRZ03_Certificate;
  friend class Parma_Polyhedra_Library::H79_Certificate;

protected:
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    If the poly-hull of \p *this and \p y is exact it is assigned
    to \p *this and \c true is returned, otherwise \c false is returned.

    Current implementation is based on (a variant of) Algorithm 8.1 in
      A. Bemporad, K. Fukuda, and F. D. Torrisi
      <em>Convexity Recognition of the Union of Polyhedra</em>
      Technical Report AUT00-13, ETH Zurich, 2000

    \note
    It is assumed that \p *this and \p y are topologically closed
    and dimension-compatible;
    if the assumption does not hold, the behavior is undefined.
  */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  bool BFT00_poly_hull_assign_if_exact(const Polyhedron& y);

  bool BHZ09_poly_hull_assign_if_exact(const Polyhedron& y);
  bool BHZ09_C_poly_hull_assign_if_exact(const Polyhedron& y);
  bool BHZ09_NNC_poly_hull_assign_if_exact(const Polyhedron& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \name Exception Throwers
  //@{
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
protected:
  void throw_invalid_argument(const char* method, const char* reason) const;

  void throw_topology_incompatible(const char* method,
                                   const char* ph_name,
                                   const Polyhedron& ph) const;
  void throw_topology_incompatible(const char* method,
                                   const char* c_name,
                                   const Constraint& c) const;
  void throw_topology_incompatible(const char* method,
                                   const char* g_name,
                                   const Generator& g) const;
  void throw_topology_incompatible(const char* method,
                                   const char* cs_name,
                                   const Constraint_System& cs) const;
  void throw_topology_incompatible(const char* method,
                                   const char* gs_name,
                                   const Generator_System& gs) const;

  void throw_dimension_incompatible(const char* method,
                                    const char* other_name,
                                    dimension_type other_dim) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* ph_name,
                                    const Polyhedron& ph) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* le_name,
                                    const Linear_Expression& le) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* c_name,
                                    const Constraint& c) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* g_name,
                                    const Generator& g) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* cg_name,
                                    const Congruence& cg) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* cs_name,
                                    const Constraint_System& cs) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* gs_name,
                                    const Generator_System& gs) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* cgs_name,
                                    const Congruence_System& cgs) const;
  template <typename C>
  void throw_dimension_incompatible(const char* method,
                                    const char* lf_name,
                                    const Linear_Form<C>& lf) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* var_name,
                                    Variable var) const;
  void throw_dimension_incompatible(const char* method,
                                    dimension_type required_space_dim) const;

  // Note: the following three methods need to be static, because they
  // can be called inside constructors (before actually constructing the
  // polyhedron object).
  static dimension_type
  check_space_dimension_overflow(dimension_type dim, dimension_type max,
                                 const Topology topol,
                                 const char* method, const char* reason);

  static dimension_type
  check_space_dimension_overflow(dimension_type dim, const Topology topol,
                                 const char* method, const char* reason);

  template <typename Object>
  static Object&
  check_obj_space_dimension_overflow(Object& input, Topology topol,
                                     const char* method, const char* reason);

  void throw_invalid_generator(const char* method,
                               const char* g_name) const;

  void throw_invalid_generators(const char* method,
                                const char* gs_name) const;
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //@} // Exception Throwers
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates for the space dimensions corresponding to \p *vars_p.

    \param vars_p
    When nonzero, points with non-integer coordinates for the
    variables/space-dimensions contained in \p *vars_p can be discarded.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(const Variables_Set* vars_p,
                                    Complexity_Class complexity);

  //! Helper function that overapproximates an interval linear form.
  /*!
    \param lf
    The linear form on intervals with floating point boundaries to approximate.
    ALL of its coefficients MUST be bounded.

    \param lf_dimension
    Must be the space dimension of \p lf.

    \param result
    Used to store the result.

    This function makes \p result become a linear form that is a correct
    approximation of \p lf under the constraints specified by \p *this.
    The resulting linear form has the property that all of its variable
    coefficients have a non-significant upper bound and can thus be
    considered as singletons.
  */
  template <typename FP_Format, typename Interval_Info>
  void overapproximate_linear_form(
  const Linear_Form<Interval <FP_Format, Interval_Info> >& lf,
  const dimension_type lf_dimension,
  Linear_Form<Interval <FP_Format, Interval_Info> >& result);

  /*! \brief
    Helper function that makes \p result become a Linear_Expression obtained
    by normalizing the denominators in \p lf.

    \param lf
    The linear form on intervals with floating point boundaries to normalize.
    It should be the result of an application of static method
    <CODE>overapproximate_linear_form</CODE>.

    \param lf_dimension
    Must be the space dimension of \p lf.

    \param result
    Used to store the result.

    This function ignores the upper bound of intervals in \p lf,
    so that in fact \p result can be seen as \p lf multiplied by a proper
    normalization constant.
  */
  template <typename FP_Format, typename Interval_Info>
  static void convert_to_integer_expression(
              const Linear_Form<Interval <FP_Format, Interval_Info> >& lf,
              const dimension_type lf_dimension,
              Linear_Expression& result);

  //! Normalization helper function.
  /*!
    \param lf
    The linear form on intervals with floating point boundaries to normalize.
    It should be the result of an application of static method
    <CODE>overapproximate_linear_form</CODE>.

    \param lf_dimension
    Must be the space dimension of \p lf.

    \param res
    Stores the normalized linear form, except its inhomogeneous term.

    \param res_low_coeff
    Stores the lower boundary of the inhomogeneous term of the result.

    \param res_hi_coeff
    Stores the higher boundary of the inhomogeneous term of the result.

    \param denominator
    Becomes the common denominator of \p res_low_coeff, \p res_hi_coeff
    and all coefficients in \p res.

    Results are obtained by normalizing denominators in \p lf, ignoring
    the upper bounds of variable coefficients in \p lf.
  */
  template <typename FP_Format, typename Interval_Info>
  static void
  convert_to_integer_expressions(const Linear_Form<Interval<FP_Format,
                                                            Interval_Info> >&
                                 lf,
                                 const dimension_type lf_dimension,
                                 Linear_Expression& res,
                                 Coefficient& res_low_coeff,
                                 Coefficient& res_hi_coeff,
                                 Coefficient& denominator);

  template <typename Linear_System1, typename Row2>
  static bool
  add_to_system_and_check_independence(Linear_System1& eq_sys,
                                       const Row2& eq);

  /*! \brief
    Assuming \p *this is NNC, assigns to \p *this the result of the
    "positive time-elapse" between \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void positive_time_elapse_assign_impl(const Polyhedron& y);
};

/* Automatically generated from PPL source file ../src/Ph_Status_inlines.hh line 1. */
/* Polyhedron::Status class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline
Polyhedron::Status::Status(flags_t mask)
  : flags(mask) {
}

inline
Polyhedron::Status::Status()
  : flags(ZERO_DIM_UNIV) {
}

inline bool
Polyhedron::Status::test_all(flags_t mask) const {
  return (flags & mask) == mask;
}

inline bool
Polyhedron::Status::test_any(flags_t mask) const {
  return (flags & mask) != 0;
}

inline void
Polyhedron::Status::set(flags_t mask) {
  flags |= mask;
}

inline void
Polyhedron::Status::reset(flags_t mask) {
  flags &= ~mask;
}

inline bool
Polyhedron::Status::test_zero_dim_univ() const {
  return flags == ZERO_DIM_UNIV;
}

inline void
Polyhedron::Status::reset_zero_dim_univ() {
  // This is a no-op if the current status is not zero-dim.
  if (flags == ZERO_DIM_UNIV) {
    // In the zero-dim space, if it is not the universe it is empty.
    flags = EMPTY;
  }
}

inline void
Polyhedron::Status::set_zero_dim_univ() {
  // Zero-dim universe is incompatible with anything else.
  flags = ZERO_DIM_UNIV;
}

inline bool
Polyhedron::Status::test_empty() const {
  return test_any(EMPTY);
}

inline void
Polyhedron::Status::reset_empty() {
  reset(EMPTY);
}

inline void
Polyhedron::Status::set_empty() {
  flags = EMPTY;
}

inline bool
Polyhedron::Status::test_c_up_to_date() const {
  return test_any(C_UP_TO_DATE);
}

inline void
Polyhedron::Status::reset_c_up_to_date() {
  reset(C_UP_TO_DATE);
}

inline void
Polyhedron::Status::set_c_up_to_date() {
  set(C_UP_TO_DATE);
}

inline bool
Polyhedron::Status::test_g_up_to_date() const {
  return test_any(G_UP_TO_DATE);
}

inline void
Polyhedron::Status::reset_g_up_to_date() {
  reset(G_UP_TO_DATE);
}

inline void
Polyhedron::Status::set_g_up_to_date() {
  set(G_UP_TO_DATE);
}

inline bool
Polyhedron::Status::test_c_minimized() const {
  return test_any(C_MINIMIZED);
}

inline void
Polyhedron::Status::reset_c_minimized() {
  reset(C_MINIMIZED);
}

inline void
Polyhedron::Status::set_c_minimized() {
  set(C_MINIMIZED);
}

inline bool
Polyhedron::Status::test_g_minimized() const {
  return test_any(G_MINIMIZED);
}

inline void
Polyhedron::Status::reset_g_minimized() {
  reset(G_MINIMIZED);
}

inline void
Polyhedron::Status::set_g_minimized() {
  set(G_MINIMIZED);
}


inline bool
Polyhedron::Status::test_c_pending() const {
  return test_any(CS_PENDING);
}

inline void
Polyhedron::Status::reset_c_pending() {
  reset(CS_PENDING);
}

inline void
Polyhedron::Status::set_c_pending() {
  set(CS_PENDING);
}

inline bool
Polyhedron::Status::test_g_pending() const {
  return test_any(GS_PENDING);
}

inline void
Polyhedron::Status::reset_g_pending() {
  reset(GS_PENDING);
}

inline void
Polyhedron::Status::set_g_pending() {
  set(GS_PENDING);
}


inline bool
Polyhedron::Status::test_sat_c_up_to_date() const {
  return test_any(SAT_C_UP_TO_DATE);
}

inline void
Polyhedron::Status::reset_sat_c_up_to_date() {
  reset(SAT_C_UP_TO_DATE);
}

inline void
Polyhedron::Status::set_sat_c_up_to_date() {
  set(SAT_C_UP_TO_DATE);
}

inline bool
Polyhedron::Status::test_sat_g_up_to_date() const {
  return test_any(SAT_G_UP_TO_DATE);
}

inline void
Polyhedron::Status::reset_sat_g_up_to_date() {
  reset(SAT_G_UP_TO_DATE);
}

inline void
Polyhedron::Status::set_sat_g_up_to_date() {
  set(SAT_G_UP_TO_DATE);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Polyhedron_inlines.hh line 1. */
/* Polyhedron class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Polyhedron_inlines.hh line 29. */
#include <algorithm>
#include <deque>

namespace Parma_Polyhedra_Library {

inline memory_size_type
Polyhedron::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

inline dimension_type
Polyhedron::space_dimension() const {
  return space_dim;
}

inline int32_t
Polyhedron::hash_code() const {
  return hash_code_from_dimension(space_dimension());
}

inline dimension_type
Polyhedron::max_space_dimension() {
  using std::min;
  // One dimension is reserved to have a value of type dimension_type
  // that does not represent a legal dimension.
  return min(std::numeric_limits<dimension_type>::max() - 1,
             min(Constraint_System::max_space_dimension(),
                 Generator_System::max_space_dimension()
                 )
             );
}

inline Topology
Polyhedron::topology() const {
  // We can check either one of the two matrices.
  // (`con_sys' is slightly better, since it is placed at offset 0.)
  return con_sys.topology();
}

inline bool
Polyhedron::is_discrete() const {
  return affine_dimension() == 0;
}

inline bool
Polyhedron::is_necessarily_closed() const {
  // We can check either one of the two matrices.
  // (`con_sys' is slightly better, since it is placed at offset 0.)
  return con_sys.is_necessarily_closed();
}

inline void
Polyhedron::upper_bound_assign(const Polyhedron& y) {
  poly_hull_assign(y);
}

inline void
Polyhedron::difference_assign(const Polyhedron& y) {
  poly_difference_assign(y);
}

inline void
Polyhedron::widening_assign(const Polyhedron& y, unsigned* tp) {
  H79_widening_assign(y, tp);
}

inline
Polyhedron::~Polyhedron() {
}

inline void
Polyhedron::m_swap(Polyhedron& y) {
  if (topology() != y.topology()) {
    throw_topology_incompatible("swap(y)", "y", y);
  }
  using std::swap;
  swap(con_sys, y.con_sys);
  swap(gen_sys, y.gen_sys);
  swap(sat_c, y.sat_c);
  swap(sat_g, y.sat_g);
  swap(status, y.status);
  swap(space_dim, y.space_dim);
}

/*! \relates Polyhedron */
inline void
swap(Polyhedron& x, Polyhedron& y) {
  x.m_swap(y);
}

inline bool
Polyhedron::can_recycle_constraint_systems() {
  return true;
}

inline bool
Polyhedron::can_recycle_congruence_systems() {
  return false;
}

inline bool
Polyhedron::marked_empty() const {
  return status.test_empty();
}

inline bool
Polyhedron::constraints_are_up_to_date() const {
  return status.test_c_up_to_date();
}

inline bool
Polyhedron::generators_are_up_to_date() const {
  return status.test_g_up_to_date();
}

inline bool
Polyhedron::constraints_are_minimized() const {
  return status.test_c_minimized();
}

inline bool
Polyhedron::generators_are_minimized() const {
  return status.test_g_minimized();
}

inline bool
Polyhedron::sat_c_is_up_to_date() const {
  return status.test_sat_c_up_to_date();
}

inline bool
Polyhedron::sat_g_is_up_to_date() const {
  return status.test_sat_g_up_to_date();
}

inline bool
Polyhedron::has_pending_constraints() const {
  return status.test_c_pending();
}

inline bool
Polyhedron::has_pending_generators() const {
  return status.test_g_pending();
}

inline bool
Polyhedron::has_something_pending() const {
  return status.test_c_pending() || status.test_g_pending();
}

inline bool
Polyhedron::can_have_something_pending() const {
  return constraints_are_minimized()
    && generators_are_minimized()
    && (sat_c_is_up_to_date() || sat_g_is_up_to_date());
}

inline bool
Polyhedron::is_empty() const {
  if (marked_empty()) {
    return true;
  }
  // Try a fast-fail test: if generators are up-to-date and
  // there are no pending constraints, then the generator system
  // (since it is well formed) contains a point.
  if (generators_are_up_to_date() && !has_pending_constraints()) {
    return false;
  }
  return !minimize();
}

inline void
Polyhedron::set_constraints_up_to_date() {
  status.set_c_up_to_date();
}

inline void
Polyhedron::set_generators_up_to_date() {
  status.set_g_up_to_date();
}

inline void
Polyhedron::set_constraints_minimized() {
  set_constraints_up_to_date();
  status.set_c_minimized();
}

inline void
Polyhedron::set_generators_minimized() {
  set_generators_up_to_date();
  status.set_g_minimized();
}

inline void
Polyhedron::set_constraints_pending() {
  status.set_c_pending();
}

inline void
Polyhedron::set_generators_pending() {
  status.set_g_pending();
}

inline void
Polyhedron::set_sat_c_up_to_date() {
  status.set_sat_c_up_to_date();
}

inline void
Polyhedron::set_sat_g_up_to_date() {
  status.set_sat_g_up_to_date();
}

inline void
Polyhedron::clear_empty() {
  status.reset_empty();
}

inline void
Polyhedron::clear_constraints_minimized() {
  status.reset_c_minimized();
}

inline void
Polyhedron::clear_generators_minimized() {
  status.reset_g_minimized();
}

inline void
Polyhedron::clear_pending_constraints() {
  status.reset_c_pending();
}

inline void
Polyhedron::clear_pending_generators() {
  status.reset_g_pending();
}

inline void
Polyhedron::clear_sat_c_up_to_date() {
  status.reset_sat_c_up_to_date();
  // Can get rid of sat_c here.
}

inline void
Polyhedron::clear_sat_g_up_to_date() {
  status.reset_sat_g_up_to_date();
  // Can get rid of sat_g here.
}

inline void
Polyhedron::clear_constraints_up_to_date() {
  clear_pending_constraints();
  clear_constraints_minimized();
  clear_sat_c_up_to_date();
  clear_sat_g_up_to_date();
  status.reset_c_up_to_date();
  // Can get rid of con_sys here.
}

inline void
Polyhedron::clear_generators_up_to_date() {
  clear_pending_generators();
  clear_generators_minimized();
  clear_sat_c_up_to_date();
  clear_sat_g_up_to_date();
  status.reset_g_up_to_date();
  // Can get rid of gen_sys here.
}

inline bool
Polyhedron::process_pending() const {
  PPL_ASSERT(space_dim > 0 && !marked_empty());
  PPL_ASSERT(has_something_pending());

  if (has_pending_constraints()) {
    return process_pending_constraints();
  }

  PPL_ASSERT(has_pending_generators());
  process_pending_generators();
  return true;
}

inline bool
Polyhedron::bounds_from_above(const Linear_Expression& expr) const {
  return bounds(expr, true);
}

inline bool
Polyhedron::bounds_from_below(const Linear_Expression& expr) const {
  return bounds(expr, false);
}

inline bool
Polyhedron::maximize(const Linear_Expression& expr,
                     Coefficient& sup_n, Coefficient& sup_d,
                     bool& maximum) const {
  Generator g(point());
  return max_min(expr, true, sup_n, sup_d, maximum, g);
}

inline bool
Polyhedron::maximize(const Linear_Expression& expr,
                     Coefficient& sup_n, Coefficient& sup_d, bool& maximum,
                     Generator& g) const {
  return max_min(expr, true, sup_n, sup_d, maximum, g);
}

inline bool
Polyhedron::minimize(const Linear_Expression& expr,
                     Coefficient& inf_n, Coefficient& inf_d,
                     bool& minimum) const {
  Generator g(point());
  return max_min(expr, false, inf_n, inf_d, minimum, g);
}

inline bool
Polyhedron::minimize(const Linear_Expression& expr,
                     Coefficient& inf_n, Coefficient& inf_d, bool& minimum,
                     Generator& g) const {
  return max_min(expr, false, inf_n, inf_d, minimum, g);
}

inline Constraint_System
Polyhedron::simplified_constraints() const {
  PPL_ASSERT(constraints_are_up_to_date());
  Constraint_System cs(con_sys);
  if (cs.num_pending_rows() > 0) {
    cs.unset_pending_rows();
  }
  if (has_pending_constraints() || !constraints_are_minimized()) {
    cs.simplify();
  }
  return cs;
}

inline Congruence_System
Polyhedron::congruences() const {
  return Congruence_System(minimized_constraints());
}

inline Congruence_System
Polyhedron::minimized_congruences() const {
  return Congruence_System(minimized_constraints());
}

inline void
Polyhedron::add_recycled_congruences(Congruence_System& cgs) {
  add_congruences(cgs);
}

template <typename FP_Format, typename Interval_Info>
inline void
Polyhedron::generalized_refine_with_linear_form_inequality(
            const Linear_Form< Interval<FP_Format, Interval_Info> >& left,
            const Linear_Form< Interval<FP_Format, Interval_Info> >& right,
            const Relation_Symbol relsym) {
  switch (relsym) {
  case EQUAL:
    // TODO: see if we can handle this case more efficiently.
    refine_with_linear_form_inequality(left, right, false);
    refine_with_linear_form_inequality(right, left, false);
    break;
  case LESS_THAN:
    refine_with_linear_form_inequality(left, right, true);
    break;
  case LESS_OR_EQUAL:
    refine_with_linear_form_inequality(left, right, false);
    break;
  case GREATER_THAN:
    refine_with_linear_form_inequality(right, left, true);
    break;
  case GREATER_OR_EQUAL:
    refine_with_linear_form_inequality(right, left, false);
    break;
  case NOT_EQUAL:
    break;
  default:
    PPL_UNREACHABLE;
    break;
  }
}

template <typename FP_Format, typename Interval_Info>
inline void
Polyhedron::
refine_fp_interval_abstract_store(
       Box< Interval<FP_Format, Interval_Info> >& store) const {

  // Check that FP_Format is indeed a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<FP_Format>::is_exact,
                     "Polyhedron::refine_fp_interval_abstract_store:"
                     " T not a floating point type.");

  typedef Interval<FP_Format, Interval_Info> FP_Interval_Type;
  store.intersection_assign(Box<FP_Interval_Type>(*this));

}

/*! \relates Polyhedron */
inline bool
operator!=(const Polyhedron& x, const Polyhedron& y) {
  return !(x == y);
}

inline bool
Polyhedron::strictly_contains(const Polyhedron& y) const {
  const Polyhedron& x = *this;
  return x.contains(y) && !y.contains(x);
}

inline void
Polyhedron::drop_some_non_integer_points(Complexity_Class complexity) {
  const Variables_Set* const p_vs = 0;
  drop_some_non_integer_points(p_vs, complexity);
}

inline void
Polyhedron::drop_some_non_integer_points(const Variables_Set& vars,
                                         Complexity_Class complexity) {
  drop_some_non_integer_points(&vars, complexity);
}


namespace Interfaces {

inline bool
is_necessarily_closed_for_interfaces(const Polyhedron& ph) {
  return ph.is_necessarily_closed();
}

} // namespace Interfaces

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Polyhedron_templates.hh line 1. */
/* Polyhedron class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/MIP_Problem_defs.hh line 1. */
/* MIP_Problem class declaration.
*/


/* Automatically generated from PPL source file ../src/Matrix_defs.hh line 1. */
/* Matrix class declaration.
*/


/* Automatically generated from PPL source file ../src/Matrix_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename Row>
class Matrix;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Matrix_defs.hh line 31. */
#include <ostream>

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A sparse matrix of Coefficient.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Row>
class Parma_Polyhedra_Library::Matrix {

public:
  typedef typename Swapping_Vector<Row>::iterator iterator;
  typedef typename Swapping_Vector<Row>::const_iterator const_iterator;

  //! Returns the maximum number of rows of a Sparse_Matrix.
  static dimension_type max_num_rows();

  //! Returns the maximum number of columns of a Sparse_Matrix.
  static dimension_type max_num_columns();

  /*!
    \brief Constructs a square matrix with the given size, filled with
           unstored zeroes.

    \param n
    The size of the new square matrix.

    This method takes \f$O(n)\f$ time.
  */
  explicit Matrix(dimension_type n = 0);

  /*!
    \brief Constructs a matrix with the given dimensions, filled with unstored
           zeroes.

    \param num_rows
    The number of rows in the new matrix.

    \param num_columns
    The number of columns in the new matrix.

    This method takes \f$O(n)\f$ time, where n is \p num_rows.
  */
  Matrix(dimension_type num_rows, dimension_type num_columns);

  //! Swaps (*this) with x.
  /*!

    \param x
    The matrix that will be swapped with *this.

    This method takes \f$O(1)\f$ time.
  */
  void m_swap(Matrix& x);

  //! Returns the number of rows in the matrix.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  dimension_type num_rows() const;

  //! Returns the number of columns in the matrix.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  dimension_type num_columns() const;

  // TODO: Check if this can be removed.
  //! Returns the capacity of the row vector.
  dimension_type capacity() const;

  //! Returns <CODE>true</CODE> if and only if \p *this has no rows.
  /*!
    \note
    The unusual naming for this method is \em intentional:
    we do not want it to be named \c empty because this would cause
    an error prone name clash with the corresponding methods in derived
    classes Constraint_System and Congruence_System (which have a
    different semantics).
  */
  bool has_no_rows() const;

  //! Equivalent to resize(n, n).
  void resize(dimension_type n);

  // TODO: Check if this can become private.
  //! Reserves space for at least \p n rows.
  void reserve_rows(dimension_type n);

  //! Resizes this matrix to the specified dimensions.
  /*!

    \param num_rows
    The desired numer of rows.

    \param num_columns
    The desired numer of columns.

    New rows and columns will contain non-stored zeroes.

    This operation invalidates existing iterators.

    Adding n rows takes \f$O(n)\f$ amortized time.

    Adding n columns takes \f$O(r)\f$ time, where r is \p num_rows.

    Removing n rows takes \f$O(n+k)\f$ amortized time, where k is the total
    number of elements stored in the removed rows.

    Removing n columns takes \f$O(\sum_{j=1}^{r} (k_j*\log^2 n_j))\f$ time,
    where r is the number of rows, \f$k_j\f$ is the number of elements stored
    in the columns of the j-th row that must be removed and \f$n_j\f$ is the
    total number of elements stored in the j-th row.
    A weaker (but simpler) bound is \f$O(r+k*\log^2 c)\f$, where r is the
    number of rows, k is the number of elements that have to be removed and c
    is the number of columns.
  */
  void resize(dimension_type num_rows, dimension_type num_columns);

  //! Adds \p n rows and \p m columns of zeroes to the matrix.
  /*!
    \param n
    The number of rows to be added: must be strictly positive.

    \param m
    The number of columns to be added: must be strictly positive.

    Turns the \f$r \times c\f$ matrix \f$M\f$ into
    the \f$(r+n) \times (c+m)\f$ matrix
    \f$\bigl(\genfrac{}{}{0pt}{}{M}{0} \genfrac{}{}{0pt}{}{0}{0}\bigr)\f$.
    The matrix is expanded avoiding reallocation whenever possible.

    This method takes \f$O(r)\f$ time, where r is the number of the matrix's
    rows after the operation.
  */
  void add_zero_rows_and_columns(dimension_type n, dimension_type m);

  //! Adds to the matrix \p n rows of zeroes.
  /*!
    \param n
    The number of rows to be added: must be strictly positive.

    Turns the \f$r \times c\f$ matrix \f$M\f$ into
    the \f$(r+n) \times c\f$ matrix \f$\genfrac{(}{)}{0pt}{}{M}{0}\f$.
    The matrix is expanded avoiding reallocation whenever possible.

    This method takes \f$O(k)\f$ amortized time, where k is the number of the
    new rows.
  */
  void add_zero_rows(dimension_type n);

  //! Adds a copy of the row \p x at the end of the matrix.
  /*!

    \param x
    The row that will be appended to the matrix.

    This operation invalidates existing iterators.

    This method takes \f$O(n)\f$ amortized time, where n is the numer of
    elements stored in \p x.
  */
  void add_row(const Row& x);

  //! Adds the row \p y to the matrix.
  /*!
    \param y
    The row to be added: it must have the same size and capacity as
    \p *this. It is not declared <CODE>const</CODE> because its
    data-structures will recycled to build the new matrix row.

    Turns the \f$r \times c\f$ matrix \f$M\f$ into
    the \f$(r+1) \times c\f$ matrix
    \f$\genfrac{(}{)}{0pt}{}{M}{y}\f$.
    The matrix is expanded avoiding reallocation whenever possible.
  */
  void add_recycled_row(Row& y);

  /*! \brief
    Removes from the matrix the last \p n rows.

    \param n
    The number of row that will be removed.

    It is equivalent to num_rows() - n, num_columns()).

    This method takes \f$O(n+k)\f$ amortized time, where k is the total number
    of elements stored in the removed rows and n is the number of removed
    rows.
  */
  void remove_trailing_rows(dimension_type n);

  void remove_rows(iterator first, iterator last);

  //! Permutes the columns of the matrix.
  /*!
    This method may be slow for some Row types, and should be avoided if
    possible.

    \param cycles
    A vector representing the non-trivial cycles of the permutation
    according to which the columns must be rearranged.

    The \p cycles vector contains, one after the other, the
    non-trivial cycles (i.e., the cycles of length greater than one)
    of a permutation of \e non-zero column indexes.  Each cycle is
    terminated by zero.  For example, assuming the matrix has 7
    columns, the permutation \f$ \{ 1 \mapsto 3, 2 \mapsto 4,
    3 \mapsto 6, 4 \mapsto 2, 5 \mapsto 5, 6 \mapsto 1 \}\f$ can be
    represented by the non-trivial cycles \f$(1 3 6)(2 4)\f$ that, in
    turn can be represented by a vector of 6 elements containing 1, 3,
    6, 0, 2, 4, 0.

    This method takes \f$O(k*\sum_{j=1}^{r} \log^2 n_j)\f$ expected time,
    where k is the size of the \p cycles vector, r the number of rows and
    \f$n_j\f$ the number of elements stored in row j.
    A weaker (but simpler) bound is \f$O(k*r*\log^2 c)\f$, where k is the size
    of the \p cycles vector, r is the number of rows and c is the number of
    columns.

    \note
    The first column of the matrix, having index zero, is never involved
    in a permutation.
  */
  void permute_columns(const std::vector<dimension_type>& cycles);

  //! Swaps the columns having indexes \p i and \p j.
  void swap_columns(dimension_type i,  dimension_type j);

  //! Adds \p n columns of zeroes to the matrix.
  /*!
    \param n
    The number of columns to be added: must be strictly positive.

    Turns the \f$r \times c\f$ matrix \f$M\f$ into
    the \f$r \times (c+n)\f$ matrix \f$(M \, 0)\f$.

    This method takes \f$O(r)\f$ amortized time, where r is the numer of the
    matrix's rows.
  */
  void add_zero_columns(dimension_type n);

  //! Adds \p n columns of non-stored zeroes to the matrix before column i.
  /*!

    \param n
    The numer of columns that will be added.

    \param i
    The index of the column before which the new columns will be added.

    This operation invalidates existing iterators.

    This method takes \f$O(\sum_{j=1}^{r} (k_j+\log n_j))\f$ time, where r is
    the number of rows, \f$k_j\f$ is the number of elements stored in the
    columns of the j-th row that must be shifted and \f$n_j\f$ is the number
    of elements stored in the j-th row.
    A weaker (but simpler) bound is \f$O(k+r*\log c)\f$ time, where k is the
    number of elements that must be shifted, r is the number of the rows and c
    is the number of the columns.
  */
  void add_zero_columns(dimension_type n, dimension_type i);

  //! Removes the i-th from the matrix, shifting other columns to the left.
  /*!

    \param i
    The index of the column that will be removed.

    This operation invalidates existing iterators on rows' elements.

    This method takes \f$O(k + \sum_{j=1}^{r} (\log^2 n_j))\f$ amortized time,
    where k is the number of elements stored with column index greater than i,
    r the number of rows in this matrix and \f$n_j\f$ the number of elements
    stored in row j.
    A weaker (but simpler) bound is \f$O(r*(c-i+\log^2 c))\f$, where r is the
    number of rows, c is the number of columns and i is the parameter passed
    to this method.
  */
  void remove_column(dimension_type i);

  //! Shrinks the matrix by removing its \p n trailing columns.
  /*!

    \param n
    The number of trailing columns that will be removed.

    This operation invalidates existing iterators.

    This method takes \f$O(\sum_{j=1}^r (k_j*\log n_j))\f$ amortized time,
    where r is the number of rows, \f$k_j\f$ is the number of elements that
    have to be removed from row j and \f$n_j\f$ is the total number of
    elements stored in row j.
    A weaker (but simpler) bound is \f$O(r*n*\log c)\f$, where r is the number
    of rows, c the number of columns and n the parameter passed to this
    method.
  */
  void remove_trailing_columns(dimension_type n);

  //! Equivalent to resize(0,0).
  void clear();

  //! Returns an %iterator pointing to the first row.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  iterator begin();

  //! Returns an %iterator pointing after the last row.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  iterator end();

  //! Returns an %iterator pointing to the first row.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  const_iterator begin() const;

  //! Returns an %iterator pointing after the last row.
  /*!
    This method takes \f$O(1)\f$ time.
  */
  const_iterator end() const;

  //! Returns a reference to the i-th row.
  /*!
    \param i
    The index of the desired row.

    This method takes \f$O(1)\f$ time.
  */
  Row& operator[](dimension_type i);

  //! Returns a const reference to the i-th row.
  /*!
    \param i
    The index of the desired row.

    This method takes \f$O(1)\f$ time.
  */
  const Row& operator[](dimension_type i) const;

  //! Loads the row from an ASCII representation generated using ascii_dump().
  /*!
    \param s
    The stream from which read the ASCII representation.

    This method takes \f$O(n*\log n)\f$ time.
  */
  bool ascii_load(std::istream& s);

  PPL_OUTPUT_DECLARATIONS

  //! Returns the total size in bytes of the memory occupied by \p *this.
  /*!
    This method is \f$O(r+k)\f$, where r is the number of rows and k is the
    number of elements stored in the matrix.
  */
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  /*!
    This method is \f$O(r+k)\f$, where r is the number of rows and k is the
    number of elements stored in the matrix.
  */
  memory_size_type external_memory_in_bytes() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

private:
  //! The vector that stores the matrix's elements.
  Swapping_Vector<Row> rows;

  //! The number of columns in this matrix.
  dimension_type num_columns_;
};

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Row>
void swap(Matrix<Row>& x, Matrix<Row>& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are identical.
/*! \relates Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Row>
bool operator==(const Matrix<Row>& x, const Matrix<Row>& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are different.
/*! \relates Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Row>
bool operator!=(const Matrix<Row>& x, const Matrix<Row>& y);

} // namespace Parma_Polyhedra_Library


/* Automatically generated from PPL source file ../src/Matrix_inlines.hh line 1. */
/* Matrix class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

template <typename Row>
inline dimension_type
Matrix<Row>::max_num_rows() {
  return std::vector<Row>().max_size();
}

template <typename Row>
inline dimension_type
Matrix<Row>::max_num_columns() {
  return Row::max_size();
}

template <typename Row>
inline void
Matrix<Row>::m_swap(Matrix& x) {
  using std::swap;
  swap(rows, x.rows);
  swap(num_columns_, x.num_columns_);
}

template <typename Row>
inline dimension_type
Matrix<Row>::num_rows() const {
  return rows.size();
}

template <typename Row>
inline dimension_type
Matrix<Row>::num_columns() const {
  return num_columns_;
}

template <typename Row>
inline dimension_type
Matrix<Row>::capacity() const {
  return rows.capacity();
}

template <typename Row>
inline bool
Matrix<Row>::has_no_rows() const {
  return num_rows() == 0;
}

template <typename Row>
inline void
Matrix<Row>::resize(dimension_type n) {
  resize(n, n);
}

template <typename Row>
inline void
Matrix<Row>::reserve_rows(dimension_type requested_capacity) {

  rows.reserve(requested_capacity);
}

template <typename Row>
inline void
Matrix<Row>::add_zero_rows_and_columns(dimension_type n, dimension_type m) {
  resize(num_rows() + n, num_columns() + m);
}

template <typename Row>
inline void
Matrix<Row>::add_zero_rows(dimension_type n) {
  resize(num_rows() + n, num_columns());
}

template <typename Row>
inline void
Matrix<Row>::add_row(const Row& x) {
  // TODO: Optimize this.
  Row row(x);
  add_zero_rows(1);
  // Now x may have been invalidated, if it was a row of this matrix.
  swap(rows.back(), row);
  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Matrix<Row>::add_recycled_row(Row& x) {
  add_zero_rows(1);
  swap(rows.back(), x);
  PPL_ASSERT(OK());
}

template <typename Row>
inline void
Matrix<Row>::remove_trailing_rows(dimension_type n) {
  resize(num_rows() - n, num_columns());
}

template <typename Row>
inline void
Matrix<Row>::remove_rows(iterator first, iterator last) {
  rows.erase(first, last);
}

template <typename Row>
inline void
Matrix<Row>::add_zero_columns(dimension_type n) {
  resize(num_rows(), num_columns() + n);
}

template <typename Row>
inline void
Matrix<Row>::remove_trailing_columns(dimension_type n) {
  PPL_ASSERT(n <= num_columns());
  resize(num_rows(), num_columns() - n);
}

template <typename Row>
inline void
Matrix<Row>::clear() {
  resize(0, 0);
}

template <typename Row>
inline typename Matrix<Row>::iterator
Matrix<Row>::begin() {
  return rows.begin();
}

template <typename Row>
inline typename Matrix<Row>::iterator
Matrix<Row>::end() {
  return rows.end();
}

template <typename Row>
inline typename Matrix<Row>::const_iterator
Matrix<Row>::begin() const {
  return rows.begin();
}

template <typename Row>
inline typename Matrix<Row>::const_iterator
Matrix<Row>::end() const {
  return rows.end();
}

template <typename Row>
inline Row&
Matrix<Row>::operator[](dimension_type i) {
  PPL_ASSERT(i < rows.size());
  return rows[i];
}

template <typename Row>
inline const Row&
Matrix<Row>::operator[](dimension_type i) const {
  PPL_ASSERT(i < rows.size());
  return rows[i];
}

template <typename Row>
inline memory_size_type
Matrix<Row>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename Row>
inline void
swap(Matrix<Row>& x, Matrix<Row>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Matrix_templates.hh line 1. */
/* Matrix class implementation: non-inline template functions.
*/


namespace Parma_Polyhedra_Library {

template <typename Row>
Matrix<Row>::Matrix(dimension_type n)
  : rows(n), num_columns_(n) {
  for (dimension_type i = 0; i < rows.size(); ++i) {
    rows[i].resize(num_columns_);
  }
  PPL_ASSERT(OK());
}

template <typename Row>
Matrix<Row>::Matrix(dimension_type num_rows, dimension_type num_columns)
  : rows(num_rows), num_columns_(num_columns) {
  for (dimension_type i = 0; i < rows.size(); ++i) {
    rows[i].resize(num_columns_);
  }
  PPL_ASSERT(OK());
}

template <typename Row>
void
Matrix<Row>::resize(dimension_type num_rows, dimension_type num_columns) {
  const dimension_type old_num_rows = rows.size();
  rows.resize(num_rows);
  if (old_num_rows < num_rows) {
    for (dimension_type i = old_num_rows; i < num_rows; ++i) {
      rows[i].resize(num_columns);
    }
    if (num_columns_ != num_columns) {
      num_columns_ = num_columns;
      for (dimension_type i = 0; i < old_num_rows; ++i) {
        rows[i].resize(num_columns);
      }
    }
  }
  else
    if (num_columns_ != num_columns) {
      num_columns_ = num_columns;
      for (dimension_type i = 0; i < num_rows; ++i) {
        rows[i].resize(num_columns);
      }
    }
  PPL_ASSERT(OK());
}

template <typename Row>
void
Matrix<Row>::permute_columns(const std::vector<dimension_type>& cycles) {
  PPL_DIRTY_TEMP_COEFFICIENT(tmp);
  const dimension_type n = cycles.size();
  PPL_ASSERT(cycles[n - 1] == 0);
  for (dimension_type k = num_rows(); k-- > 0; ) {
    Row& rows_k = (*this)[k];
    for (dimension_type i = 0, j = 0; i < n; i = ++j) {
      // Make `j' be the index of the next cycle terminator.
      while (cycles[j] != 0) {
        ++j;
      }
      // Cycles of length less than 2 are not allowed.
      PPL_ASSERT(j - i >= 2);
      if (j - i == 2) {
        // For cycles of length 2 no temporary is needed, just a swap.
        rows_k.swap_coefficients(cycles[i], cycles[i + 1]);
      }
      else {
        // Longer cycles need a temporary.
        tmp = rows_k.get(cycles[j - 1]);
        for (dimension_type l = (j - 1); l > i; --l) {
          rows_k.swap_coefficients(cycles[l-1], cycles[l]);
        }
        if (tmp == 0) {
          rows_k.reset(cycles[i]);
        }
        else {
          using std::swap;
          swap(tmp, rows_k[cycles[i]]);
        }
      }
    }
  }
}

template <typename Row>
void
Matrix<Row>::swap_columns(dimension_type i, dimension_type j) {
  for (dimension_type k = num_rows(); k-- > 0; ) {
    (*this)[k].swap_coefficients(i, j);
  }
}

template <typename Row>
void
Matrix<Row>::add_zero_columns(dimension_type n, dimension_type i) {
  for (dimension_type j = rows.size(); j-- > 0; ) {
    rows[j].add_zeroes_and_shift(n, i);
  }
  num_columns_ += n;
  PPL_ASSERT(OK());
}

template <typename Row>
void
Matrix<Row>::remove_column(dimension_type i) {
  for (dimension_type j = rows.size(); j-- > 0; ) {
    rows[j].delete_element_and_shift(i);
  }
  --num_columns_;
  PPL_ASSERT(OK());
}

template <typename Row>
void
Matrix<Row>::ascii_dump(std::ostream& s) const {
  s << num_rows() << " x ";
  s << num_columns() << "\n";
  for (const_iterator i = begin(), i_end = end(); i !=i_end; ++i) {
    i->ascii_dump(s);
  }
}

PPL_OUTPUT_TEMPLATE_DEFINITIONS_ASCII_ONLY(Row, Matrix<Row>)

template <typename Row>
bool
Matrix<Row>::ascii_load(std::istream& s) {
  std::string str;
  dimension_type new_num_rows;
  dimension_type new_num_cols;
  if (!(s >> new_num_rows)) {
    return false;
  }
  if (!(s >> str) || str != "x") {
    return false;
  }
  if (!(s >> new_num_cols)) {
    return false;
  }

  for (iterator i = rows.begin(), i_end = rows.end();
       i != i_end; ++i) {
    i->clear();
  }

  resize(new_num_rows, new_num_cols);

  for (dimension_type row = 0; row < new_num_rows; ++row) {
    if (!rows[row].ascii_load(s)) {
      return false;
    }
  }

  // Check invariants.
  PPL_ASSERT(OK());
  return true;
}

template <typename Row>
memory_size_type
Matrix<Row>::external_memory_in_bytes() const {
  return rows.external_memory_in_bytes();
}

template <typename Row>
bool
Matrix<Row>::OK() const {
  for (const_iterator i = begin(), i_end = end(); i != i_end; ++i) {
    if (i->size() != num_columns_) {
      return false;
    }
  }
  return true;
}

/*! \relates Parma_Polyhedra_Library::Matrix */
template <typename Row>
bool
operator==(const Matrix<Row>& x, const Matrix<Row>& y) {
  if (x.num_rows() != y.num_rows()) {
    return false;
  }
  if (x.num_columns() != y.num_columns()) {
    return false;
  }
  for (dimension_type i = x.num_rows(); i-- > 0; ) {
    if (x[i] != y[i]) {
      return false;
    }
  }
  return true;
}

/*! \relates Parma_Polyhedra_Library::Matrix */
template <typename Row>
bool
operator!=(const Matrix<Row>& x, const Matrix<Row>& y) {
  return !(x == y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Matrix_defs.hh line 436. */

/* Automatically generated from PPL source file ../src/MIP_Problem_defs.hh line 37. */
#include <vector>
#include <deque>
#include <iterator>
#include <iosfwd>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::MIP_Problem */
std::ostream&
operator<<(std::ostream& s, const MIP_Problem& mip);

} // namespace IO_Operators

//! Swaps \p x with \p y.
/*! \relates MIP_Problem */
void swap(MIP_Problem& x, MIP_Problem& y);

} // namespace Parma_Polyhedra_Library

//! A Mixed Integer (linear) Programming problem.
/*! \ingroup PPL_CXX_interface
  An object of this class encodes a mixed integer (linear) programming
  problem.
  The MIP problem is specified by providing:
   - the dimension of the vector space;
   - the feasible region, by means of a finite set of linear equality
     and non-strict inequality constraints;
   - the subset of the unknown variables that range over the integers
     (the other variables implicitly ranging over the reals);
   - the objective function, described by a Linear_Expression;
   - the optimization mode (either maximization or minimization).

  The class provides support for the (incremental) solution of the
  MIP problem based on variations of the revised simplex method and
  on branch-and-bound techniques. The result of the resolution
  process is expressed in terms of an enumeration, encoding the
  feasibility and the unboundedness of the optimization problem.
  The class supports simple feasibility tests (i.e., no optimization),
  as well as the extraction of an optimal (resp., feasible) point,
  provided the MIP_Problem is optimizable (resp., feasible).

  By exploiting the incremental nature of the solver, it is possible
  to reuse part of the computational work already done when solving
  variants of a given MIP_Problem: currently, incremental resolution
  supports the addition of space dimensions, the addition of constraints,
  the change of objective function and the change of optimization mode.
*/
class Parma_Polyhedra_Library::MIP_Problem {
public:
  //! Builds a trivial MIP problem.
  /*!
    A trivial MIP problem requires to maximize the objective function
    \f$0\f$ on a vector space under no constraints at all:
    the origin of the vector space is an optimal solution.

    \param dim
    The dimension of the vector space enclosing \p *this
    (optional argument with default value \f$0\f$).

    \exception std::length_error
    Thrown if \p dim exceeds <CODE>max_space_dimension()</CODE>.
  */
  explicit MIP_Problem(dimension_type dim = 0);

  /*! \brief
    Builds an MIP problem having space dimension \p dim
    from the sequence of constraints in the range
    \f$[\mathrm{first}, \mathrm{last})\f$,
    the objective function \p obj and optimization mode \p mode;
    those dimensions whose indices occur in \p int_vars are
    constrained to take an integer value.

    \param dim
    The dimension of the vector space enclosing \p *this.

    \param first
    An input iterator to the start of the sequence of constraints.

    \param last
    A past-the-end input iterator to the sequence of constraints.

    \param int_vars
    The set of variables' indexes that are constrained to take integer values.

    \param obj
    The objective function (optional argument with default value \f$0\f$).

    \param mode
    The optimization mode (optional argument with default value
    <CODE>MAXIMIZATION</CODE>).

    \exception std::length_error
    Thrown if \p dim exceeds <CODE>max_space_dimension()</CODE>.

    \exception std::invalid_argument
    Thrown if a constraint in the sequence is a strict inequality,
    if the space dimension of a constraint (resp., of the
    objective function or of the integer variables) or the space dimension
    of the integer variable set is strictly greater than \p dim.
  */
  template <typename In>
  MIP_Problem(dimension_type dim,
              In first, In last,
              const Variables_Set& int_vars,
              const Linear_Expression& obj = Linear_Expression::zero(),
              Optimization_Mode mode = MAXIMIZATION);

  /*! \brief
    Builds an MIP problem having space dimension \p dim
    from the sequence of constraints in the range
    \f$[\mathrm{first}, \mathrm{last})\f$,
    the objective function \p obj and optimization mode \p mode.

    \param dim
    The dimension of the vector space enclosing \p *this.

    \param first
    An input iterator to the start of the sequence of constraints.

    \param last
    A past-the-end input iterator to the sequence of constraints.

    \param obj
    The objective function (optional argument with default value \f$0\f$).

    \param mode
    The optimization mode (optional argument with default value
    <CODE>MAXIMIZATION</CODE>).

    \exception std::length_error
    Thrown if \p dim exceeds <CODE>max_space_dimension()</CODE>.

    \exception std::invalid_argument
    Thrown if a constraint in the sequence is a strict inequality
    or if the space dimension of a constraint (resp., of the
    objective function or of the integer variables) is strictly
    greater than \p dim.
  */
  template <typename In>
  MIP_Problem(dimension_type dim,
              In first, In last,
              const Linear_Expression& obj = Linear_Expression::zero(),
              Optimization_Mode mode = MAXIMIZATION);

  /*! \brief
    Builds an MIP problem having space dimension \p dim from the constraint
    system \p cs, the objective function \p obj and optimization mode \p mode.

    \param dim
    The dimension of the vector space enclosing \p *this.

    \param cs
    The constraint system defining the feasible region.

    \param obj
    The objective function (optional argument with default value \f$0\f$).

    \param mode
    The optimization mode (optional argument with default value
    <CODE>MAXIMIZATION</CODE>).

    \exception std::length_error
    Thrown if \p dim exceeds <CODE>max_space_dimension()</CODE>.

    \exception std::invalid_argument
    Thrown if the constraint system contains any strict inequality
    or if the space dimension of the constraint system (resp., the
    objective function) is strictly greater than \p dim.
  */
  MIP_Problem(dimension_type dim,
              const Constraint_System& cs,
              const Linear_Expression& obj = Linear_Expression::zero(),
              Optimization_Mode mode = MAXIMIZATION);

  //! Ordinary copy constructor.
  MIP_Problem(const MIP_Problem& y);

  //! Destructor.
  ~MIP_Problem();

  //! Assignment operator.
  MIP_Problem& operator=(const MIP_Problem& y);

  //! Returns the maximum space dimension an MIP_Problem can handle.
  static dimension_type max_space_dimension();

  //! Returns the space dimension of the MIP problem.
  dimension_type space_dimension() const;

  /*! \brief
    Returns a set containing all the variables' indexes constrained
    to be integral.
  */
  const Variables_Set& integer_space_dimensions() const;

private:
  //! A type alias for a sequence of constraints.
  typedef std::vector<Constraint*> Constraint_Sequence;

public:
  //! A read-only iterator on the constraints defining the feasible region.
  class const_iterator {
  private:
    typedef Constraint_Sequence::const_iterator Base;
    typedef std::iterator_traits<Base> Base_Traits;

  public:
    typedef Base_Traits::iterator_category iterator_category;
    typedef Base_Traits::difference_type difference_type;
    typedef const Constraint value_type;
    typedef const Constraint* pointer;
    typedef const Constraint& reference;

    //! Iterator difference: computes distances.
    difference_type operator-(const const_iterator& y) const;

    //! Prefix increment.
    const_iterator& operator++();

    //! Prefix decrement.
    const_iterator& operator--();

    //! Postfix increment.
    const_iterator operator++(int);

    //! Postfix decrement.
    const_iterator operator--(int);

    //! Moves iterator forward of \p n positions.
    const_iterator& operator+=(difference_type n);

    //! Moves iterator backward of \p n positions.
    const_iterator& operator-=(difference_type n);

    //! Returns an iterator \p n positions forward.
    const_iterator operator+(difference_type n) const;

    //! Returns an iterator \p n positions backward.
    const_iterator operator-(difference_type n) const;

    //! Returns a reference to the "pointed" object.
    reference operator*() const;

    //! Returns the address of the "pointed" object.
    pointer operator->() const;

    //! Compares \p *this with y.
    /*!
      \param y
      The %iterator that will be compared with *this.
    */
    bool operator==(const const_iterator& y) const;

    //! Compares \p *this with y.
    /*!
      \param y
      The %iterator that will be compared with *this.
    */
    bool operator!=(const const_iterator& y) const;

  private:
    //! Constructor from a Base iterator.
    explicit const_iterator(Base b);

    //! The Base iterator on the Constraint_Sequence.
    Base itr;

    friend class MIP_Problem;
  };

  /*! \brief
    Returns a read-only iterator to the first constraint defining
    the feasible region.
  */
  const_iterator constraints_begin() const;

  /*! \brief
    Returns a past-the-end read-only iterator to the sequence of
    constraints defining the feasible region.
  */
  const_iterator constraints_end() const;

  //! Returns the objective function.
  const Linear_Expression& objective_function() const;

  //! Returns the optimization mode.
  Optimization_Mode optimization_mode() const;

  //! Resets \p *this to be equal to the trivial MIP problem.
  /*!
    The space dimension is reset to \f$0\f$.
  */
  void clear();

  /*! \brief
    Adds \p m new space dimensions and embeds the old MIP problem
    in the new vector space.

    \param m
    The number of dimensions to add.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the
    vector space to exceed dimension <CODE>max_space_dimension()</CODE>.

    The new space dimensions will be those having the highest indexes
    in the new MIP problem; they are initially unconstrained.
  */
  void add_space_dimensions_and_embed(dimension_type m);

  /*! \brief
    Sets the variables whose indexes are in set \p i_vars to be
    integer space dimensions.

    \exception std::invalid_argument
    Thrown if some index in \p i_vars does not correspond to
    a space dimension in \p *this.
  */
  void add_to_integer_space_dimensions(const Variables_Set& i_vars);

  /*! \brief
    Adds a copy of constraint \p c to the MIP problem.

    \exception std::invalid_argument
    Thrown if the constraint \p c is a strict inequality or if its space
    dimension is strictly greater than the space dimension of \p *this.
  */
  void add_constraint(const Constraint& c);

  /*! \brief
    Adds a copy of the constraints in \p cs to the MIP problem.

    \exception std::invalid_argument
    Thrown if the constraint system \p cs contains any strict inequality
    or if its space dimension is strictly greater than the space dimension
    of \p *this.
  */
  void add_constraints(const Constraint_System& cs);

  //! Sets the objective function to \p obj.
  /*!
    \exception std::invalid_argument
    Thrown if the space dimension of \p obj is strictly greater than
    the space dimension of \p *this.
  */
  void set_objective_function(const Linear_Expression& obj);

  //! Sets the optimization mode to \p mode.
  void set_optimization_mode(Optimization_Mode mode);

  //! Checks satisfiability of \p *this.
  /*!
    \return
    <CODE>true</CODE> if and only if the MIP problem is satisfiable.
  */
  bool is_satisfiable() const;

  //! Optimizes the MIP problem.
  /*!
    \return
    An MIP_Problem_Status flag indicating the outcome of the optimization
    attempt (unfeasible, unbounded or optimized problem).
  */
  MIP_Problem_Status solve() const;

  /*! \brief
    Sets \p num and \p denom so that
    \f$\frac{\mathtt{numer}}{\mathtt{denom}}\f$ is the result of
    evaluating the objective function on \p evaluating_point.

    \param evaluating_point
    The point on which the objective function will be evaluated.

    \param numer
    On exit will contain the numerator of the evaluated value.

    \param denom
    On exit will contain the denominator of the evaluated value.

    \exception std::invalid_argument
    Thrown if \p *this and \p evaluating_point are dimension-incompatible
    or if the generator \p evaluating_point is not a point.
  */
  void evaluate_objective_function(const Generator& evaluating_point,
                                   Coefficient& numer,
                                   Coefficient& denom) const;

  //! Returns a feasible point for \p *this, if it exists.
  /*!
    \exception std::domain_error
    Thrown if the MIP problem is not satisfiable.
  */
  const Generator& feasible_point() const;

  //! Returns an optimal point for \p *this, if it exists.
  /*!
    \exception std::domain_error
    Thrown if \p *this does not not have an optimizing point, i.e.,
    if the MIP problem is unbounded or not satisfiable.
  */
  const Generator& optimizing_point() const;

  /*! \brief
    Sets \p numer and \p denom so that
    \f$\frac{\mathtt{numer}}{\mathtt{denom}}\f$ is the solution of the
    optimization problem.

    \exception std::domain_error
    Thrown if \p *this does not not have an optimizing point, i.e.,
    if the MIP problem is unbounded or not satisfiable.
  */
  void optimal_value(Coefficient& numer, Coefficient& denom) const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Swaps \p *this with \p y.
  void m_swap(MIP_Problem& y);

  //! Names of MIP problems' control parameters.
  enum Control_Parameter_Name {
    //! The pricing rule.
    PRICING
  };

  //! Possible values for MIP problem's control parameters.
  enum Control_Parameter_Value {
    //! Steepest edge pricing method, using floating points (default).
    PRICING_STEEPEST_EDGE_FLOAT,
    //! Steepest edge pricing method, using Coefficient.
    PRICING_STEEPEST_EDGE_EXACT,
    //! Textbook pricing method.
    PRICING_TEXTBOOK
  };

  //! Returns the value of the control parameter \p name.
  Control_Parameter_Value
  get_control_parameter(Control_Parameter_Name name) const;

  //! Sets control parameter \p value.
  void set_control_parameter(Control_Parameter_Value value);

private:
  //! The dimension of the vector space.
  dimension_type external_space_dim;

  /*! \brief
    The space dimension of the current (partial) solution of the
    MIP problem; it may be smaller than \p external_space_dim.
  */
  dimension_type internal_space_dim;

#if PPL_USE_SPARSE_MATRIX
  typedef Sparse_Row Row;
#else
  typedef Dense_Row Row;
#endif

  //! The matrix encoding the current feasible region in tableau form.
  Matrix<Row> tableau;

  typedef Row working_cost_type;

  //! The working cost function.
  working_cost_type working_cost;

  //! A map between the variables of `input_cs' and `tableau'.
  /*!
    Contains all the pairs (i, j) such that mapping[i].first encodes the index
    of the column in the tableau where input_cs[i] is stored; if
    mapping[i].second is not a zero, it encodes the split part of the tableau
    of input_cs[i].
    The "positive" one is represented by mapping[i].first and the "negative"
    one is represented by mapping[i].second.
  */
  std::vector<std::pair<dimension_type, dimension_type> > mapping;

  //! The current basic solution.
  std::vector<dimension_type> base;

  //! An enumerated type describing the internal status of the MIP problem.
  enum Status {
    //! The MIP problem is unsatisfiable.
    UNSATISFIABLE,
    //! The MIP problem is satisfiable; a feasible solution has been computed.
    SATISFIABLE,
    //! The MIP problem is unbounded; a feasible solution has been computed.
    UNBOUNDED,
    //! The MIP problem is optimized; an optimal solution has been computed.
    OPTIMIZED,
    /*! \brief
      The feasible region of the MIP problem has been changed by adding
      new space dimensions or new constraints; a feasible solution for
      the old feasible region is still available.
    */
    PARTIALLY_SATISFIABLE
  };

  //! The internal state of the MIP problem.
  Status status;

  // TODO: merge `status', `initialized', `pricing' and (maybe) `opt_mode'
  // into a single bitset status word, so as to save space and allow
  // for other control parameters.

  //! The pricing method in use.
  Control_Parameter_Value pricing;

  /*! \brief
    A Boolean encoding whether or not internal data structures have
    already been properly sized and populated: useful to allow for
    deeper checks in method OK().
  */
  bool initialized;

  //! The sequence of constraints describing the feasible region.
  std::vector<Constraint*> input_cs;

  /*! \brief
    The number of constraints that are inherited from our parent
    in the recursion tree built when solving via branch-and-bound.

    The first \c inherited_constraints elements in \c input_cs point to
    the inherited constraints, whose resources are owned by our ancestors.
    The resources of the other elements in \c input_cs are owned by \c *this
    and should be appropriately released on destruction.
  */
  dimension_type inherited_constraints;

  //! The first index of `input_cs' containing a pending constraint.
  dimension_type first_pending_constraint;

  //! The objective function to be optimized.
  Linear_Expression input_obj_function;

  //! The optimization mode requested.
  Optimization_Mode opt_mode;

  //! The last successfully computed feasible or optimizing point.
  Generator last_generator;

  /*! \brief
    A set containing all the indexes of variables that are constrained
    to have an integer value.
  */
  Variables_Set i_variables;

  //! A helper class to temporarily relax a MIP problem using RAII.
  struct RAII_Temporary_Real_Relaxation {
    MIP_Problem& lp;
    Variables_Set i_vars;

    RAII_Temporary_Real_Relaxation(MIP_Problem& mip)
      : lp(mip), i_vars() {
      // Turn mip into an LP problem (saving i_variables in i_vars).
      using std::swap;
      swap(i_vars, lp.i_variables);
    }

    ~RAII_Temporary_Real_Relaxation() {
      // Restore the original set of integer variables.
      using std::swap;
      swap(i_vars, lp.i_variables);
    }
  };
  friend struct RAII_Temporary_Real_Relaxation;

  //! A tag type to distinguish normal vs. inheriting copy constructor.
  struct Inherit_Constraints {};

  //! Copy constructor inheriting constraints.
  MIP_Problem(const MIP_Problem& y, Inherit_Constraints);

  //! Helper method: implements exception safe addition.
  void add_constraint_helper(const Constraint& c);

  //! Processes the pending constraints of \p *this.
  void process_pending_constraints();

  /*! \brief
    Optimizes the MIP problem using the second phase of the
    primal simplex algorithm.
  */
  void second_phase();

  /*! \brief
    Assigns to \p this->tableau a simplex tableau representing the
    MIP problem, inserting into \p this->mapping the information
    that is required to recover the original MIP problem.

    \return
    <CODE>UNFEASIBLE_MIP_PROBLEM</CODE> if the constraint system contains
    any trivially unfeasible constraint (tableau was not computed);
    <CODE>UNBOUNDED_MIP_PROBLEM</CODE> if the problem is trivially unbounded
    (the computed tableau contains no constraints);
    <CODE>OPTIMIZED_MIP_PROBLEM></CODE> if the problem is neither trivially
    unfeasible nor trivially unbounded (the tableau was computed
    successfully).
  */
  MIP_Problem_Status
  compute_tableau(std::vector<dimension_type>& worked_out_row);

  /*! \brief
    Parses the pending constraints to gather information on
    how to resize the tableau.

    \note
    All of the method parameters are output parameters; their value
    is only meaningful when the function exit returning value \c true.

    \return
    \c false if a trivially false constraint is detected, \c true otherwise.

    \param additional_tableau_rows
    On exit, this will store the number of rows that have to be added
    to the original tableau.

    \param additional_slack_variables
    This will store the number of slack variables that have to be added
    to the original tableau.

    \param is_tableau_constraint
    This container of Boolean flags is initially empty. On exit, it size
    will be equal to the number of pending constraints in \c input_cs.
    For each pending constraint index \c i, the corresponding element
    of this container (having index <CODE>i - first_pending_constraint</CODE>)
    will be set to \c true if and only if the constraint has to be included
    in the tableau.

    \param is_satisfied_inequality
    This container of Boolean flags is initially empty. On exit, its size
    will be equal to the number of pending constraints in \c input_cs.
    For each pending constraint index \c i, the corresponding element
    of this container (having index <CODE>i - first_pending_constraint</CODE>)
    will be set to \c true if and only if it is an inequality and it
    is already satisfied by \c last_generator (hence it does not require
    the introduction of an artificial variable).

    \param is_nonnegative_variable
    This container of Boolean flags is initially empty.
    On exit, it size is equal to \c external_space_dim.
    For each variable (index), the corresponding element of this container
    is \c true if the variable is known to be nonnegative (and hence should
    not be split into a positive and a negative part).

    \param is_remergeable_variable
    This container of Boolean flags is initially empty.
    On exit, it size is equal to \c internal_space_dim.
    For each variable (index), the corresponding element of this container
    is \c true if the variable was previously split into positive and
    negative parts that can now be merged back, since it is known
    that the variable is nonnegative.
  */
  bool parse_constraints(dimension_type& additional_tableau_rows,
                         dimension_type& additional_slack_variables,
                         std::deque<bool>& is_tableau_constraint,
                         std::deque<bool>& is_satisfied_inequality,
                         std::deque<bool>& is_nonnegative_variable,
                         std::deque<bool>& is_remergeable_variable) const;

  /*! \brief
    Computes the row index of the variable exiting the base
    of the MIP problem. Implemented with anti-cycling rule.

    \return
    The row index of the variable exiting the base.

    \param entering_var_index
    The column index of the variable entering the base.
  */
  dimension_type
  get_exiting_base_index(dimension_type entering_var_index) const;

  //! Linearly combines \p x with \p y so that <CODE>*this[k]</CODE> is 0.
  /*!
    \param x
    The row that will be combined with \p y object.

    \param y
    The row that will be combined with \p x object.

    \param k
    The position of \p *this that have to be \f$0\f$.

    Computes a linear combination of \p x and \p y having
    the element of index \p k equal to \f$0\f$. Then it assigns
    the resulting Row to \p x and normalizes it.
  */
  static void linear_combine(Row& x, const Row& y, const dimension_type k);

  // TODO: Remove this when the sparse working cost has been tested enough.
#if PPL_USE_SPARSE_MATRIX

  //! Linearly combines \p x with \p y so that <CODE>*this[k]</CODE> is 0.
  /*!
    \param x
    The row that will be combined with \p y object.

    \param y
    The row that will be combined with \p x object.

    \param k
    The position of \p *this that have to be \f$0\f$.

    Computes a linear combination of \p x and \p y having
    the element of index \p k equal to \f$0\f$. Then it assigns
    the resulting Dense_Row to \p x and normalizes it.
  */
  static void linear_combine(Dense_Row& x, const Sparse_Row& y,
                             const dimension_type k);

#endif // defined(PPL_USE_SPARSE_MATRIX)

  static bool is_unbounded_obj_function(
    const Linear_Expression& obj_function,
    const std::vector<std::pair<dimension_type, dimension_type> >& mapping,
    Optimization_Mode optimization_mode);

  /*! \brief
    Performs the pivoting operation on the tableau.

    \param entering_var_index
    The index of the variable entering the base.

    \param exiting_base_index
    The index of the row exiting the base.
  */
  void pivot(dimension_type entering_var_index,
             dimension_type exiting_base_index);

  /*! \brief
    Computes the column index of the variable entering the base,
    using the textbook algorithm with anti-cycling rule.

    \return
    The column index of the variable that enters the base.
    If no such variable exists, optimality was achieved
    and <CODE>0</CODE> is returned.
  */
  dimension_type textbook_entering_index() const;

  /*! \brief
    Computes the column index of the variable entering the base,
    using an exact steepest-edge algorithm with anti-cycling rule.

    \return
    The column index of the variable that enters the base.
    If no such variable exists, optimality was achieved
    and <CODE>0</CODE> is returned.

    To compute the entering_index, the steepest edge algorithm chooses
    the index `j' such that \f$\frac{d_{j}}{\|\Delta x^{j} \|}\f$ is the
    largest in absolute value, where
    \f[
      \|\Delta x^{j} \|
        = \left(
            1+\sum_{i=1}^{m} \alpha_{ij}^2
          \right)^{\frac{1}{2}}.
    \f]
    Recall that, due to the exact integer implementation of the algorithm,
    our tableau does not contain the ``real'' \f$\alpha\f$ values, but these
    can be computed dividing the value of the coefficient by the value of
    the variable in base. Obviously the result may not be an integer, so
    we will proceed in another way: we compute the lcm of all the variables
    in base to get the good ``weight'' of each Coefficient of the tableau.
  */
  dimension_type steepest_edge_exact_entering_index() const;

  /*! \brief
    Same as steepest_edge_exact_entering_index,
    but using floating points.

    \note
    Due to rounding errors, the index of the variable entering the base
    of the MIP problem is not predictable across different architectures.
    Hence, the overall simplex computation may differ in the path taken
    to reach the optimum. Anyway, the exact final result will be computed
    for the MIP_Problem.
  */
  dimension_type steepest_edge_float_entering_index() const;

  /*! \brief
    Returns <CODE>true</CODE> if and if only the algorithm successfully
    computed a feasible solution.

    \note
    Uses an exact pricing method (either textbook or exact steepest edge),
    so that the result is deterministic across different architectures.
  */
  bool compute_simplex_using_exact_pricing();

  /*! \brief
    Returns <CODE>true</CODE> if and if only the algorithm successfully
    computed a feasible solution.

    \note
    Uses a floating point implementation of the steepest edge pricing
    method, so that the result is correct, but not deterministic across
    different architectures.
  */
  bool compute_simplex_using_steepest_edge_float();

  /*! \brief
    Drop unnecessary artificial variables from the tableau and get ready
    for the second phase of the simplex algorithm.

    \note
    The two parameters denote a STL-like half-open range.
    It is assumed that \p begin_artificials is strictly greater than 0
    and smaller than \p end_artificials.

    \param begin_artificials
    The start of the tableau column index range for artificial variables.

    \param end_artificials
    The end of the tableau column index range for artificial variables.
    Note that column index end_artificial is \e excluded from the range.
  */
  void erase_artificials(dimension_type begin_artificials,
                         dimension_type end_artificials);

  bool is_in_base(dimension_type var_index,
                  dimension_type& row_index) const;

  /*! \brief
    Computes a valid generator that satisfies all the constraints of the
    Linear Programming problem associated to \p *this.
  */
  void compute_generator() const;

  /*! \brief
    Merges back the positive and negative part of a (previously split)
    variable after detecting a corresponding nonnegativity constraint.

    \return
    If the negative part of \p var_index was in base, the index of
    the corresponding tableau row (which has become non-feasible);
    otherwise \c not_a_dimension().

    \param var_index
    The index of the variable that has to be merged.
  */
  dimension_type merge_split_variable(dimension_type var_index);

  //! Returns <CODE>true</CODE> if and only if \p c is satisfied by \p g.
  static bool is_satisfied(const Constraint& c, const Generator& g);

  //! Returns <CODE>true</CODE> if and only if \p c is saturated by \p g.
  static bool is_saturated(const Constraint& c, const Generator& g);

  /*! \brief
    Returns a status that encodes the solution of the MIP problem.

    \param have_incumbent_solution
    It is used to store if the solving process has found a provisional
    optimum point.

    \param incumbent_solution_value
    Encodes the evaluated value of the provisional optimum point found.

    \param incumbent_solution_point
    If the method returns `OPTIMIZED', this will contain the optimality point.

    \param mip
    The problem that has to be solved.

    \param i_vars
    The variables that are constrained to take an integer value.
  */
  static MIP_Problem_Status solve_mip(bool& have_incumbent_solution,
                                      mpq_class& incumbent_solution_value,
                                      Generator& incumbent_solution_point,
                                      MIP_Problem& mip,
                                      const Variables_Set& i_vars);

  /*! \brief
    Returns \c true if and if only the LP problem is satisfiable.
  */
  bool is_lp_satisfiable() const;

  /*! \brief
    Returns \c true if and if only the MIP problem \p mip is satisfiable
    when variables in \p i_vars can only take integral values.

    \param mip
    The MIP problem. This is assumed to have no integral space dimension
    (so that it is a pure LP problem).

    \param i_vars
    The variables that are constrained to take integral values.

    \param p
    If \c true is returned, it will encode a feasible point.
  */
  static bool is_mip_satisfiable(MIP_Problem& mip,
                                 const Variables_Set& i_vars,
                                 Generator& p);

  /*! \brief
    Returns \c true if and if only \c mip.last_generator satisfies all the
    integrality conditions implicitly stated using by \p i_vars.

    \param mip
    The MIP problem. This is assumed to have no integral space dimension
    (so that it is a pure LP problem).

    \param i_vars
    The variables that are constrained to take an integer value.

    \param branching_index
    If \c false is returned, this will encode the non-integral variable
    index on which the `branch and bound' algorithm should be applied.
  */
  static bool choose_branching_variable(const MIP_Problem& mip,
                                        const Variables_Set& i_vars,
                                        dimension_type& branching_index);
};

/* Automatically generated from PPL source file ../src/MIP_Problem_inlines.hh line 1. */
/* MIP_Problem class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/MIP_Problem_inlines.hh line 28. */
#include <stdexcept>

namespace Parma_Polyhedra_Library {

inline dimension_type
MIP_Problem::max_space_dimension() {
  return Constraint::max_space_dimension();
}

inline dimension_type
MIP_Problem::space_dimension() const {
  return external_space_dim;
}


inline
MIP_Problem::MIP_Problem(const MIP_Problem& y)
  : external_space_dim(y.external_space_dim),
    internal_space_dim(y.internal_space_dim),
    tableau(y.tableau),
    working_cost(y.working_cost),
    mapping(y.mapping),
    base(y.base),
    status(y.status),
    pricing(y.pricing),
    initialized(y.initialized),
    input_cs(),
    inherited_constraints(0),
    first_pending_constraint(),
    input_obj_function(y.input_obj_function),
    opt_mode(y.opt_mode),
    last_generator(y.last_generator),
    i_variables(y.i_variables) {
  input_cs.reserve(y.input_cs.size());
  for (Constraint_Sequence::const_iterator i = y.input_cs.begin(),
         i_end = y.input_cs.end(); i != i_end; ++i) {
    add_constraint_helper(*(*i));
  }
  PPL_ASSERT(OK());
}

inline
MIP_Problem::MIP_Problem(const MIP_Problem& y, Inherit_Constraints)
  : external_space_dim(y.external_space_dim),
    internal_space_dim(y.internal_space_dim),
    tableau(y.tableau),
    working_cost(y.working_cost),
    mapping(y.mapping),
    base(y.base),
    status(y.status),
    pricing(y.pricing),
    initialized(y.initialized),
    input_cs(y.input_cs),
    // NOTE: The constraints are inherited, NOT copied!
    inherited_constraints(y.input_cs.size()),
    first_pending_constraint(y.first_pending_constraint),
    input_obj_function(y.input_obj_function),
    opt_mode(y.opt_mode),
    last_generator(y.last_generator),
    i_variables(y.i_variables) {
  PPL_ASSERT(OK());
}

inline void
MIP_Problem::add_constraint_helper(const Constraint& c) {
  // For exception safety, reserve space for the new element.
  const dimension_type size = input_cs.size();
  if (size == input_cs.capacity()) {
    const dimension_type max_size = input_cs.max_size();
    if (size == max_size) {
      throw std::length_error("MIP_Problem::add_constraint(): "
                              "too many constraints");
    }
    // Use an exponential grow policy to avoid too many reallocations.
    input_cs.reserve(compute_capacity(size + 1, max_size));
  }

  // This operation does not throw, because the space for the new element
  // has already been reserved: hence the new-ed Constraint is safe.
  input_cs.push_back(new Constraint(c));
}

inline
MIP_Problem::~MIP_Problem() {
  // NOTE: do NOT delete inherited constraints; they are owned
  // (and will eventually be deleted) by ancestors.
  for (Constraint_Sequence::const_iterator
         i = nth_iter(input_cs, inherited_constraints),
         i_end = input_cs.end(); i != i_end; ++i) {
    delete *i;
  }
}


inline void
MIP_Problem::set_optimization_mode(const Optimization_Mode mode) {
  if (opt_mode != mode) {
    opt_mode = mode;
    if (status == UNBOUNDED || status == OPTIMIZED) {
      status = SATISFIABLE;
    }
    PPL_ASSERT(OK());
  }
}

inline const Linear_Expression&
MIP_Problem::objective_function() const {
  return input_obj_function;
}

inline Optimization_Mode
MIP_Problem::optimization_mode() const {
  return opt_mode;
}

inline void
MIP_Problem::optimal_value(Coefficient& numer, Coefficient& denom) const {
  const Generator& g = optimizing_point();
  evaluate_objective_function(g, numer, denom);
}

inline MIP_Problem::const_iterator
MIP_Problem::constraints_begin() const {
  return const_iterator(input_cs.begin());
}

inline MIP_Problem::const_iterator
MIP_Problem::constraints_end() const {
  return const_iterator(input_cs.end());
}

inline const Variables_Set&
MIP_Problem::integer_space_dimensions() const {
  return i_variables;
}

inline MIP_Problem::Control_Parameter_Value
MIP_Problem::get_control_parameter(Control_Parameter_Name name) const {
  PPL_USED(name);
  PPL_ASSERT(name == PRICING);
  return pricing;
}

inline void
MIP_Problem::set_control_parameter(Control_Parameter_Value value) {
  pricing = value;
}

inline void
MIP_Problem::m_swap(MIP_Problem& y) {
  using std::swap;
  swap(external_space_dim, y.external_space_dim);
  swap(internal_space_dim, y.internal_space_dim);
  swap(tableau, y.tableau);
  swap(working_cost, y.working_cost);
  swap(mapping, y.mapping);
  swap(initialized, y.initialized);
  swap(base, y.base);
  swap(status, y.status);
  swap(pricing, y.pricing);
  swap(input_cs, y.input_cs);
  swap(inherited_constraints, y.inherited_constraints);
  swap(first_pending_constraint, y.first_pending_constraint);
  swap(input_obj_function, y.input_obj_function);
  swap(opt_mode, y.opt_mode);
  swap(last_generator, y.last_generator);
  swap(i_variables, y.i_variables);
}

inline MIP_Problem&
MIP_Problem::operator=(const MIP_Problem& y) {
  MIP_Problem tmp(y);
  m_swap(tmp);
  return *this;
}

inline void
MIP_Problem::clear() {
  MIP_Problem tmp;
  m_swap(tmp);
}


inline memory_size_type
MIP_Problem::external_memory_in_bytes() const {
  memory_size_type n
    = working_cost.external_memory_in_bytes()
    + tableau.external_memory_in_bytes()
    + input_obj_function.external_memory_in_bytes()
    + last_generator.external_memory_in_bytes();

  // Adding the external memory for `input_cs'.
  // NOTE: disregard inherited constraints, as they are owned by ancestors.
  n += input_cs.capacity() * sizeof(Constraint*);
  for (Constraint_Sequence::const_iterator
         i = nth_iter(input_cs, inherited_constraints),
         i_end = input_cs.end(); i != i_end; ++i) {
    n += ((*i)->total_memory_in_bytes());
  }

  // Adding the external memory for `base'.
  n += base.capacity() * sizeof(dimension_type);
  // Adding the external memory for `mapping'.
  n += mapping.capacity() * sizeof(std::pair<dimension_type, dimension_type>);
  return n;
}

inline memory_size_type
MIP_Problem::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

inline
MIP_Problem::const_iterator::const_iterator(Base b)
  : itr(b) {
}

inline MIP_Problem::const_iterator::difference_type
MIP_Problem::const_iterator::operator-(const const_iterator& y) const {
  return itr - y.itr;
}

inline MIP_Problem::const_iterator&
MIP_Problem::const_iterator::operator++() {
  ++itr;
  return *this;
}

inline MIP_Problem::const_iterator&
MIP_Problem::const_iterator::operator--() {
  --itr;
  return *this;
}

inline MIP_Problem::const_iterator
MIP_Problem::const_iterator::operator++(int) {
  const_iterator x = *this;
  operator++();
  return x;
}

inline MIP_Problem::const_iterator
MIP_Problem::const_iterator::operator--(int) {
  const_iterator x = *this;
  operator--();
  return x;
}

inline MIP_Problem::const_iterator
MIP_Problem::const_iterator::operator+(difference_type n) const {
  return const_iterator(itr + n);
}

inline MIP_Problem::const_iterator
MIP_Problem::const_iterator::operator-(difference_type n) const {
  return const_iterator(itr - n);
}

inline MIP_Problem::const_iterator&
MIP_Problem::const_iterator::operator+=(difference_type n) {
  itr += n;
  return *this;
}

inline MIP_Problem::const_iterator&
MIP_Problem::const_iterator::operator-=(difference_type n) {
  itr -= n;
  return *this;
}

inline MIP_Problem::const_iterator::reference
MIP_Problem::const_iterator::operator*() const {
  return *(*itr);
}

inline MIP_Problem::const_iterator::pointer
MIP_Problem::const_iterator::operator->() const {
  return *itr;
}

inline bool
MIP_Problem::const_iterator::operator==(const const_iterator& y) const {
  return itr == y.itr;
}

inline bool
MIP_Problem::const_iterator::operator!=(const const_iterator& y) const {
  return itr != y.itr;
}

/*! \relates MIP_Problem */
inline void
swap(MIP_Problem& x, MIP_Problem& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/MIP_Problem_templates.hh line 1. */
/* MIP_Problem class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/MIP_Problem_templates.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename In>
MIP_Problem::MIP_Problem(const dimension_type dim,
                         In first, In last,
                         const Variables_Set& int_vars,
                         const Linear_Expression& obj,
                         const Optimization_Mode mode)
  : external_space_dim(dim),
    internal_space_dim(0),
    tableau(),
    working_cost(0),
    mapping(),
    base(),
    status(PARTIALLY_SATISFIABLE),
    pricing(PRICING_STEEPEST_EDGE_FLOAT),
    initialized(false),
    input_cs(),
    inherited_constraints(0),
    first_pending_constraint(0),
    input_obj_function(obj),
    opt_mode(mode),
    last_generator(point()),
    i_variables(int_vars) {
  // Check that integer Variables_Set does not exceed the space dimension
  // of the problem.
  if (i_variables.space_dimension() > external_space_dim) {
    std::ostringstream s;
    s << "PPL::MIP_Problem::MIP_Problem"
      << "(dim, first, last, int_vars, obj, mode):\n"
      << "dim == "<< external_space_dim << " and int_vars.space_dimension() =="
      << " " << i_variables.space_dimension() << " are dimension"
      "incompatible.";
    throw std::invalid_argument(s.str());
  }

  // Check for space dimension overflow.
  if (dim > max_space_dimension()) {
    throw std::length_error("PPL::MIP_Problem:: MIP_Problem(dim, first, "
                            "last, int_vars, obj, mode):\n"
                            "dim exceeds the maximum allowed"
                            "space dimension.");
  }
  // Check the objective function.
  if (obj.space_dimension() > dim) {
    std::ostringstream s;
    s << "PPL::MIP_Problem::MIP_Problem(dim, first, last,"
      << "int_vars, obj, mode):\n"
      << "obj.space_dimension() == "<< obj.space_dimension()
      << " exceeds d == "<< dim << ".";
    throw std::invalid_argument(s.str());
  }
  // Check the constraints.
  try {
    for (In i = first; i != last; ++i) {
      if (i->is_strict_inequality()) {
        throw std::invalid_argument("PPL::MIP_Problem::"
                                    "MIP_Problem(dim, first, last, int_vars,"
                                    "obj, mode):\nrange [first, last) contains"
                                    "a strict inequality constraint.");
      }
      if (i->space_dimension() > dim) {
        std::ostringstream s;
        s << "PPL::MIP_Problem::"
          << "MIP_Problem(dim, first, last, int_vars, obj, mode):\n"
          << "range [first, last) contains a constraint having space"
          << "dimension  == " << i->space_dimension() << " that exceeds"
          "this->space_dimension == " << dim << ".";
        throw std::invalid_argument(s.str());
      }
      add_constraint_helper(*i);
    }
  } catch (...) {
    // Delete the allocated constraints, to avoid memory leaks.

    for (Constraint_Sequence::const_iterator
          i = input_cs.begin(), i_end = input_cs.end();
          i != i_end; ++i) {
      delete *i;
    }

    throw;
  }
  PPL_ASSERT(OK());
}

template <typename In>
MIP_Problem::MIP_Problem(dimension_type dim,
                         In first, In last,
                         const Linear_Expression& obj,
                         Optimization_Mode mode)
  : external_space_dim(dim),
    internal_space_dim(0),
    tableau(),
    working_cost(0),
    mapping(),
    base(),
    status(PARTIALLY_SATISFIABLE),
    pricing(PRICING_STEEPEST_EDGE_FLOAT),
    initialized(false),
    input_cs(),
    inherited_constraints(0),
    first_pending_constraint(0),
    input_obj_function(obj),
    opt_mode(mode),
    last_generator(point()),
    i_variables() {
  // Check for space dimension overflow.
  if (dim > max_space_dimension()) {
    throw std::length_error("PPL::MIP_Problem::"
                            "MIP_Problem(dim, first, last, obj, mode):\n"
                            "dim exceeds the maximum allowed space "
                            "dimension.");
  }
  // Check the objective function.
  if (obj.space_dimension() > dim) {
    std::ostringstream s;
    s << "PPL::MIP_Problem::MIP_Problem(dim, first, last,"
      << " obj, mode):\n"
      << "obj.space_dimension() == "<< obj.space_dimension()
      << " exceeds d == "<< dim << ".";
    throw std::invalid_argument(s.str());
  }
  // Check the constraints.
  try {
    for (In i = first; i != last; ++i) {
      if (i->is_strict_inequality()) {
        throw std::invalid_argument("PPL::MIP_Problem::"
                                    "MIP_Problem(dim, first, last, obj, mode):"
                                    "\n"
                                    "range [first, last) contains a strict "
                                    "inequality constraint.");
      }
      if (i->space_dimension() > dim) {
        std::ostringstream s;
        s << "PPL::MIP_Problem::"
          << "MIP_Problem(dim, first, last, obj, mode):\n"
          << "range [first, last) contains a constraint having space"
          << "dimension" << " == " << i->space_dimension() << " that exceeds"
          "this->space_dimension == " << dim << ".";
        throw std::invalid_argument(s.str());
      }
      add_constraint_helper(*i);
    }
  } catch (...) {
    // Delete the allocated constraints, to avoid memory leaks.

    for (Constraint_Sequence::const_iterator
          i = input_cs.begin(), i_end = input_cs.end();
          i != i_end; ++i) {
      delete *i;
    }

    throw;
  }
  PPL_ASSERT(OK());
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/MIP_Problem_defs.hh line 975. */

/* Automatically generated from PPL source file ../src/Polyhedron_templates.hh line 31. */
// For static method overflows.
/* Automatically generated from PPL source file ../src/Floating_Point_Expression_defs.hh line 1. */
/* Declarations for the Floating_Point_Expression class and its constituents.
*/


/* Automatically generated from PPL source file ../src/Floating_Point_Expression_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
class Floating_Point_Expression;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Floating_Point_Expression_defs.hh line 31. */
#include <cmath>
#include <map>

namespace Parma_Polyhedra_Library {

/*! \ingroup PPL_CXX_Interface \brief
  A floating point expression on a given format.

  This class represents a concrete <EM>floating point expression</EM>. This
  includes constants, floating point variables, binary and unary
  arithmetic operators.

  \par Template type parameters

  - The class template type parameter \p FP_Interval_Type represents the type
  of the intervals used in the abstract domain. The interval bounds
  should have a floating point type.
  - The class template type parameter \p FP_Format represents the floating
  point format used in the concrete domain.
  This parameter must be a struct similar to the ones defined in file
  Float_defs.hh, even though it is sufficient to define the three
  fields BASE, MANTISSA_BITS and EXPONENT_BIAS.
*/
template <typename FP_Interval_Type, typename FP_Format>
class Floating_Point_Expression {

public:

  //! Alias for a linear form with template argument \p FP_Interval_Type.
  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;

  //! Alias for a map that associates a variable index to an interval.
  /*! \brief
    Alias for a Box storing lower and upper bounds for floating point
    variables.

    The type a linear form abstract store associating each variable with an
    interval that correctly approximates its value.
  */
  typedef Box<FP_Interval_Type> FP_Interval_Abstract_Store;

  //! Alias for a map that associates a variable index to a linear form.
  /*!
    The type a linear form abstract store associating each variable with a
    linear form that correctly approximates its value.
  */
  typedef std::map<dimension_type, FP_Linear_Form>
          FP_Linear_Form_Abstract_Store;

  //! The floating point format used by the analyzer.
  typedef typename FP_Interval_Type::boundary_type boundary_type;

  //! The interval policy used by \p FP_Interval_Type.
  typedef typename FP_Interval_Type::info_type info_type;

  //! Destructor.
  virtual ~Floating_Point_Expression();

  //! Linearizes a floating point expression.
  /*! \brief
    Makes \p result become a linear form that correctly approximates the
    value of the floating point expression in the given composite
    abstract store.

    \param int_store The interval abstract store.
    \param lf_store The linear form abstract store.
    \param result Becomes the linearized expression.

    \return <CODE>true</CODE> if the linearization succeeded,
    <CODE>false</CODE> otherwise.

    Formally, if \p *this represents the expression \f$e\f$,
    \p int_store represents the interval abstract store \f$\rho^{\#}\f$ and
    \p lf_store represents the linear form abstract store \f$\rho^{\#}_l\f$,
    then \p result will become
    \f$\linexprenv{e}{\rho^{\#}}{\rho^{\#}_l}\f$
    if the linearization succeeds.

    All variables occurring in the floating point expression MUST have
    an associated interval in \p int_store.
    If this precondition is not met, calling the method causes an
    undefined behavior.
  */
  virtual bool linearize(const FP_Interval_Abstract_Store& int_store,
                         const FP_Linear_Form_Abstract_Store& lf_store,
                         FP_Linear_Form& result) const = 0;

  /*! \brief
    Absolute error.

    Represents the interval \f$[-\omega, \omega]\f$ where \f$\omega\f$ is the
    smallest non-zero positive number in the less precise floating point
    format between the analyzer format and the analyzed format.

  */
  static FP_Interval_Type absolute_error;

  // FIXME: this may not be the best place for them.
  /*! \brief
    Verifies if a given linear form overflows.
    \param lf The linear form to verify.
    \return
    Returns <CODE>false</CODE> if all coefficients in \p lf are bounded,
    <CODE>true</CODE> otherwise.
  */
  static bool overflows(const FP_Linear_Form& lf);

  /*! \brief
    Computes the relative error of a given linear form.

    Static helper method that is used by <CODE>linearize</CODE>
    to account for the relative errors on \p lf.
    \param lf The linear form used to compute the relative error.
    \param result Becomes the linear form corresponding to a relative
    error committed on \p lf.

    This method makes <CODE>result</CODE> become a linear form
    obtained by evaluating the function \f$\varepsilon_{\mathbf{f}}(l)\f$
    on the linear form \p lf. This function is defined
    such as:
    \f[
    \varepsilon_{\mathbf{f}}\left([a, b]+\sum_{v \in \cV}[a_{v}, b_{v}]v\right)
    \defeq
    (\textrm{max}(|a|, |b|) \amifp [-\beta^{-\textrm{p}}, \beta^{-\textrm{p}}])
    +
    \sum_{v \in \cV}(\textrm{max}(|a_{v}|,|b_{v}|)
    \amifp
    [-\beta^{-\textrm{p}}, \beta^{-\textrm{p}}])v
    \f]
    where p is the fraction size in bits for the format \f$\mathbf{f}\f$ and
    \f$\beta\f$ the base.
  */
  static void relative_error(const FP_Linear_Form& lf,
                             FP_Linear_Form& result);

  /*! \brief
    Makes \p result become an interval that overapproximates all the
    possible values of \p lf in the interval abstract store \p store.

    \param lf The linear form to aproximate.
    \param store The abstract store.
    \param result The linear form that will be modified.

    This method makes <CODE>result</CODE> become
    \f$\iota(lf)\rho^{\#}\f$, that is an interval defined as:
    \f[
    \iota\left(i + \sum_{v \in \cV}i_{v}v\right)\rho^{\#}
    \defeq
    i \asifp \left(\bigoplus_{v \in \cV}{}^{\#}i_{v} \amifp
    \rho^{\#}(v)\right)
    \f]
  */
  static void intervalize(const FP_Linear_Form& lf,
                          const FP_Interval_Abstract_Store& store,
                          FP_Interval_Type& result);

private:

  /*! \brief
    Computes the absolute error.

    Static helper method that is used to compute the value of the public
    static field <CODE>absolute_error</CODE>.

    \return The interval \f$[-\omega, \omega]\f$ corresponding to the value
    of <CODE>absolute_error</CODE>
  */
  static FP_Interval_Type compute_absolute_error();

}; // class Floating_Point_Expression


template <typename FP_Interval_Type, typename FP_Format>
FP_Interval_Type Floating_Point_Expression<FP_Interval_Type, FP_Format>
  ::absolute_error = compute_absolute_error();

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Floating_Point_Expression_inlines.hh line 1. */
/* Floating_Point_Expression class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Floating_Point_Expression_inlines.hh line 29. */

namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
inline
Floating_Point_Expression<FP_Interval_Type, FP_Format>
::~Floating_Point_Expression() {}

template <typename FP_Interval_Type, typename FP_Format>
inline bool
Floating_Point_Expression<FP_Interval_Type, FP_Format>
::overflows(const FP_Linear_Form& lf) {
  if (!lf.inhomogeneous_term().is_bounded()) {
    return true;
  }

  dimension_type dimension = lf.space_dimension();
  for (dimension_type i = 0; i < dimension; ++i) {
    if (!lf.coefficient(Variable(i)).is_bounded()) {
      return true;
    }
  }

  return false;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Floating_Point_Expression_templates.hh line 1. */
/* Floating_Point_Expression class implementation:
   non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/Floating_Point_Expression_templates.hh line 29. */
#include <cmath>

namespace Parma_Polyhedra_Library {

template<typename FP_Interval_Type, typename FP_Format>
void
Floating_Point_Expression<FP_Interval_Type, FP_Format>
::relative_error(const FP_Linear_Form& lf, FP_Linear_Form& result) {

  FP_Interval_Type error_propagator;
  boundary_type lb = -pow(FP_Format::BASE,
  -static_cast<typename Floating_Point_Expression<FP_Interval_Type, FP_Format>
  ::boundary_type>(FP_Format::MANTISSA_BITS));
  error_propagator.build(i_constraint(GREATER_OR_EQUAL, lb),
                         i_constraint(LESS_OR_EQUAL, -lb));

  // Handle the inhomogeneous term.
  const FP_Interval_Type* current_term = &lf.inhomogeneous_term();
  assert(current_term->is_bounded());

  FP_Interval_Type
    current_multiplier(std::max(std::abs(current_term->lower()),
                                std::abs(current_term->upper())));
  FP_Linear_Form current_result_term(current_multiplier);
  current_result_term *= error_propagator;
  result = FP_Linear_Form(current_result_term);

  // Handle the other terms.
  dimension_type dimension = lf.space_dimension();
  for (dimension_type i = 0; i < dimension; ++i) {
    current_term = &lf.coefficient(Variable(i));
    assert(current_term->is_bounded());
    current_multiplier
      = FP_Interval_Type(std::max(std::abs(current_term->lower()),
                                  std::abs(current_term->upper())));
    current_result_term = FP_Linear_Form(Variable(i));
    current_result_term *= current_multiplier;
    current_result_term *= error_propagator;
    result += current_result_term;
  }

  return;
}

template<typename FP_Interval_Type, typename FP_Format>
void
Floating_Point_Expression<FP_Interval_Type, FP_Format>
::intervalize(const FP_Linear_Form& lf,
              const FP_Interval_Abstract_Store& store,
              FP_Interval_Type& result) {
  result = FP_Interval_Type(lf.inhomogeneous_term());
  dimension_type dimension = lf.space_dimension();
  assert(dimension <= store.space_dimension());
  for (dimension_type i = 0; i < dimension; ++i) {
    FP_Interval_Type current_addend = lf.coefficient(Variable(i));
    const FP_Interval_Type& curr_int = store.get_interval(Variable(i));
    current_addend *= curr_int;
    result += current_addend;
  }

  return;
}

template<typename FP_Interval_Type, typename FP_Format>
FP_Interval_Type
Floating_Point_Expression<FP_Interval_Type, FP_Format>
::compute_absolute_error() {
  typedef typename Floating_Point_Expression<FP_Interval_Type, FP_Format>
    ::boundary_type Boundary;
  boundary_type omega;
  omega = std::max(pow(static_cast<Boundary>(FP_Format::BASE),
                       static_cast<Boundary>(1 - FP_Format::EXPONENT_BIAS
                                             - FP_Format::MANTISSA_BITS)),
                   std::numeric_limits<Boundary>::denorm_min());
  FP_Interval_Type result;
  result.build(i_constraint(GREATER_OR_EQUAL, -omega),
               i_constraint(LESS_OR_EQUAL, omega));
  return result;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Floating_Point_Expression_defs.hh line 211. */

/* Automatically generated from PPL source file ../src/Polyhedron_templates.hh line 33. */
#include <algorithm>
#include <deque>

namespace Parma_Polyhedra_Library {

template <typename Interval>
Polyhedron::Polyhedron(Topology topol,
                       const Box<Interval>& box,
                       Complexity_Class)
  : con_sys(topol, default_con_sys_repr),
    gen_sys(topol, default_gen_sys_repr),
    sat_c(),
    sat_g() {
  // Initialize the space dimension as indicated by the box.
  space_dim = box.space_dimension();

  // Check for emptiness.
  if (box.is_empty()) {
    set_empty();
    return;
  }

  // Zero-dim universe polyhedron.
  if (space_dim == 0) {
    set_zero_dim_univ();
    return;
  }

  // Properly set the space dimension of `con_sys'.
  con_sys.set_space_dimension(space_dim);

  PPL_DIRTY_TEMP_COEFFICIENT(l_n);
  PPL_DIRTY_TEMP_COEFFICIENT(l_d);
  PPL_DIRTY_TEMP_COEFFICIENT(u_n);
  PPL_DIRTY_TEMP_COEFFICIENT(u_d);

  if (topol == NECESSARILY_CLOSED) {
    for (dimension_type k = space_dim; k-- > 0; ) {
      const Variable v_k = Variable(k);
      // See if we have a valid lower bound.
      bool l_closed = false;
      bool l_bounded = box.has_lower_bound(v_k, l_n, l_d, l_closed);
      // See if we have a valid upper bound.
      bool u_closed = false;
      bool u_bounded = box.has_upper_bound(v_k, u_n, u_d, u_closed);

      // See if we have an implicit equality constraint.
      if (l_bounded && u_bounded
          && l_closed && u_closed
          && l_n == u_n && l_d == u_d) {
        // Add the constraint `l_d*v_k == l_n'.
        con_sys.insert(l_d * v_k == l_n);
      }
      else {
        if (l_bounded) {
          // Add the constraint `l_d*v_k >= l_n'.
          con_sys.insert(l_d * v_k >= l_n);
        }
        if (u_bounded) {
          // Add the constraint `u_d*v_k <= u_n'.
          con_sys.insert(u_d * v_k <= u_n);
        }
      }
    }
  }
  else {
    // topol == NOT_NECESSARILY_CLOSED
    for (dimension_type k = space_dim; k-- > 0; ) {
      const Variable v_k = Variable(k);
      // See if we have a valid lower bound.
      bool l_closed = false;
      bool l_bounded = box.has_lower_bound(v_k, l_n, l_d, l_closed);
      // See if we have a valid upper bound.
      bool u_closed = false;
      bool u_bounded = box.has_upper_bound(v_k, u_n, u_d, u_closed);

      // See if we have an implicit equality constraint.
      if (l_bounded && u_bounded
          && l_closed && u_closed
          && l_n == u_n && l_d == u_d) {
        // Add the constraint `l_d*v_k == l_n'.
        con_sys.insert(l_d * v_k == l_n);
      }
      else {
        // Check if a lower bound constraint is required.
        if (l_bounded) {
          if (l_closed) {
            // Add the constraint `l_d*v_k >= l_n'.
            con_sys.insert(l_d * v_k >= l_n);
          }
          else {
            // Add the constraint `l_d*v_k > l_n'.
            con_sys.insert(l_d * v_k > l_n);
          }
        }
        // Check if an upper bound constraint is required.
        if (u_bounded) {
          if (u_closed) {
            // Add the constraint `u_d*v_k <= u_n'.
            con_sys.insert(u_d * v_k <= u_n);
          }
          else {
            // Add the constraint `u_d*v_k < u_n'.
            con_sys.insert(u_d * v_k < u_n);
          }
        }
      }
    }
  }

  // Adding the low-level constraints.
  con_sys.add_low_level_constraints();

  // Constraints are up-to-date.
  set_constraints_up_to_date();
  PPL_ASSERT_HEAVY(OK());
}

template <typename Partial_Function>
void
Polyhedron::map_space_dimensions(const Partial_Function& pfunc) {
  if (space_dim == 0) {
    return;
  }
  if (pfunc.has_empty_codomain()) {
    // All dimensions vanish: the polyhedron becomes zero_dimensional.
    if (marked_empty()
        || (has_pending_constraints()
            && !remove_pending_to_obtain_generators())
        || (!generators_are_up_to_date() && !update_generators())) {
      // Removing all dimensions from the empty polyhedron.
      space_dim = 0;
      con_sys.clear();
    }
    else {
      // Removing all dimensions from a non-empty polyhedron.
      set_zero_dim_univ();
    }

    PPL_ASSERT_HEAVY(OK());
    return;
  }

  const dimension_type new_space_dimension = pfunc.max_in_codomain() + 1;

  if (new_space_dimension == space_dim) {
    // The partial function `pfunc' is indeed total and thus specifies
    // a permutation, that is, a renaming of the dimensions.  For
    // maximum efficiency, we will simply permute the columns of the
    // constraint system and/or the generator system.

    std::vector<Variable> cycle;
    cycle.reserve(space_dim);

    // Used to mark elements as soon as they are inserted in a cycle.
    std::deque<bool> visited(space_dim);

    for (dimension_type i = space_dim; i-- > 0; ) {
      if (visited[i]) {
        continue;
      }

      dimension_type j = i;
      do {
        visited[j] = true;
        // The following initialization is only to make the compiler happy.
        dimension_type k = 0;
        if (!pfunc.maps(j, k)) {
          throw_invalid_argument("map_space_dimensions(pfunc)",
                                 " pfunc is inconsistent");
        }
        if (k == j) {
          break;
        }

        cycle.push_back(Variable(j));
        // Go along the cycle.
        j = k;
      } while (!visited[j]);

      // End of cycle.

      // Permute all that is up-to-date.  Notice that the contents of
      // the saturation matrices is unaffected by the permutation of
      // columns: they remain valid, if they were so.
      if (constraints_are_up_to_date()) {
        con_sys.permute_space_dimensions(cycle);
      }

      if (generators_are_up_to_date()) {
        gen_sys.permute_space_dimensions(cycle);
      }

      cycle.clear();
    }

    PPL_ASSERT_HEAVY(OK());
    return;
  }

  // If control gets here, then `pfunc' is not a permutation and some
  // dimensions must be projected away.

  // If there are pending constraints, using `generators()' we process them.
  const Generator_System& old_gensys = generators();

  if (old_gensys.has_no_rows()) {
    // The polyhedron is empty.
    Polyhedron new_polyhedron(topology(), new_space_dimension, EMPTY);
    m_swap(new_polyhedron);
    PPL_ASSERT_HEAVY(OK());
    return;
  }

  // Make a local copy of the partial function.
  std::vector<dimension_type> pfunc_maps(space_dim, not_a_dimension());
  for (dimension_type j = space_dim; j-- > 0; ) {
    dimension_type pfunc_j;
    if (pfunc.maps(j, pfunc_j)) {
      pfunc_maps[j] = pfunc_j;
    }
  }

  Generator_System new_gensys;
  for (Generator_System::const_iterator i = old_gensys.begin(),
         old_gensys_end = old_gensys.end(); i != old_gensys_end; ++i) {
    const Generator& old_g = *i;
    const Generator::expr_type old_e = old_g.expression();
    Linear_Expression expr;
    expr.set_space_dimension(new_space_dimension);
    bool all_zeroes = true;
    for (Generator::expr_type::const_iterator j = old_e.begin(),
          j_end = old_e.end(); j != j_end; ++j) {
      const dimension_type mapped_id = pfunc_maps[j.variable().id()];
      if (mapped_id != not_a_dimension()) {
        add_mul_assign(expr, *j, Variable(mapped_id));
        all_zeroes = false;
      }
    }
    switch (old_g.type()) {
    case Generator::LINE:
      if (!all_zeroes) {
        new_gensys.insert(line(expr));
      }
      break;
    case Generator::RAY:
      if (!all_zeroes) {
        new_gensys.insert(ray(expr));
      }
      break;
    case Generator::POINT:
      // A point in the origin has all zero homogeneous coefficients.
      new_gensys.insert(point(expr, old_g.divisor()));
      break;
    case Generator::CLOSURE_POINT:
      // A closure point in the origin has all zero homogeneous coefficients.
      new_gensys.insert(closure_point(expr, old_g.divisor()));
      break;
    }
  }
  Polyhedron new_polyhedron(topology(), new_gensys);
  m_swap(new_polyhedron);
  PPL_ASSERT_HEAVY(OK(true));
}

template <typename FP_Format, typename Interval_Info>
void
Polyhedron::refine_with_linear_form_inequality(
  const Linear_Form< Interval<FP_Format, Interval_Info> >& left,
  const Linear_Form< Interval<FP_Format, Interval_Info> >& right,
  const bool is_strict) {

  // Check that FP_Format is indeed a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<FP_Format>::is_exact,
                         "Polyhedron::refine_with_linear_form_inequality:"
                         " FP_Format not a floating point type.");

  // Dimension compatibility checks.
  // The dimensions of left and right should not be greater than the
  // dimension of *this.
  const dimension_type left_space_dim = left.space_dimension();
  if (space_dim < left_space_dim) {
    throw_dimension_incompatible(
          "refine_with_linear_form_inequality(l1, l2, s)", "l1", left);
  }

  const dimension_type right_space_dim = right.space_dimension();
  if (space_dim < right_space_dim) {
    throw_dimension_incompatible(
          "refine_with_linear_form_inequality(l1, l2, s)", "l2", right);
  }

  // We assume that the analyzer will not refine an unreachable test.
  PPL_ASSERT(!marked_empty());

  typedef Interval<FP_Format, Interval_Info> FP_Interval_Type;
  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;

  if (Floating_Point_Expression<FP_Interval_Type, float_ieee754_single>::
      overflows(left)) {
    return;
  }

  if (Floating_Point_Expression<FP_Interval_Type, float_ieee754_single>::
      overflows(right)) {
    return;
  }

  // Overapproximate left - right.
  FP_Linear_Form left_minus_right(left);
  left_minus_right -= right;
  if (Floating_Point_Expression<FP_Interval_Type, float_ieee754_single>::
      overflows(left_minus_right)) {
    return;
  }

  dimension_type lf_space_dim = left_minus_right.space_dimension();
  FP_Linear_Form lf_approx;
  overapproximate_linear_form(left_minus_right, lf_space_dim, lf_approx);
  if (Floating_Point_Expression<FP_Interval_Type, float_ieee754_single>::
      overflows(lf_approx)) {
    return;
  }

  // Normalize left - right.
  Linear_Expression lf_approx_le;
  convert_to_integer_expression(lf_approx, lf_space_dim, lf_approx_le);

  // Finally, do the refinement.
  if (!is_strict || is_necessarily_closed()) {
    refine_with_constraint(lf_approx_le <= 0);
  }
  else {
    refine_with_constraint(lf_approx_le < 0);
  }
}

template <typename FP_Format, typename Interval_Info>
void
Polyhedron::affine_form_image(const Variable var,
const Linear_Form<Interval <FP_Format, Interval_Info> >& lf) {

  // Check that FP_Format is indeed a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<FP_Format>::is_exact,
                         "Polyhedron::affine_form_image:"
                         " FP_Format not a floating point type.");

  // Dimension compatibility checks.
  // The dimension of lf should not be greater than the dimension of *this.
  const dimension_type lf_space_dim = lf.space_dimension();
  if (space_dim < lf_space_dim) {
    throw_dimension_incompatible("affine_form_image(v, l, s)", "l", lf);
  }

  // `var' should be one of the dimensions of the polyhedron.
  const dimension_type var_id = var.id();
  if (space_dim < var_id + 1) {
    throw_dimension_incompatible("affine_form_image(v, l, s)", "v", var);
  }

  // We assume that the analyzer will not perform an unreachable assignment.
  PPL_ASSERT(!marked_empty());

  typedef Interval<FP_Format, Interval_Info> FP_Interval_Type;
  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;

  if (Floating_Point_Expression<FP_Interval_Type, float_ieee754_single>::
      overflows(lf)) {
    *this = Polyhedron(topology(), space_dim, UNIVERSE);
    return;
  }

  // Overapproximate lf.
  FP_Linear_Form lf_approx;
  overapproximate_linear_form(lf, lf_space_dim, lf_approx);

  if (Floating_Point_Expression<FP_Interval_Type, float_ieee754_single>::
      overflows(lf_approx)) {
    *this = Polyhedron(topology(), space_dim, UNIVERSE);
    return;
  }

  // Normalize lf.
  Linear_Expression lf_approx_le;
  PPL_DIRTY_TEMP_COEFFICIENT(lo_coeff);
  PPL_DIRTY_TEMP_COEFFICIENT(hi_coeff);
  PPL_DIRTY_TEMP_COEFFICIENT(denominator);
  convert_to_integer_expressions(lf_approx, lf_space_dim, lf_approx_le,
                                 lo_coeff, hi_coeff, denominator);

  // Finally, do the assignment.
  bounded_affine_image(var, lf_approx_le + lo_coeff, lf_approx_le + hi_coeff,
                       denominator);
}

template <typename FP_Format, typename Interval_Info>
void
Polyhedron
::overapproximate_linear_form(const Linear_Form<Interval <FP_Format, Interval_Info> >& lf,
                              const dimension_type lf_dimension,
                              Linear_Form<Interval <FP_Format, Interval_Info> >& result) {

  // Check that FP_Format is indeed a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<FP_Format>::is_exact,
                         "Polyhedron::overapproximate_linear_form:"
                         " FP_Format not a floating point type.");

  typedef Interval<FP_Format, Interval_Info> FP_Interval_Type;
  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;

  // Build a Box from the Polyhedron so that we can extract upper and
  // lower bounds of variables easily.
  Box<FP_Interval_Type> box(*this);

  result = FP_Linear_Form(lf.inhomogeneous_term());
  // FIXME: this may not be policy-neutral.
  const FP_Interval_Type aux_divisor1(static_cast<FP_Format>(0.5));
  FP_Interval_Type aux_divisor2(aux_divisor1);
  aux_divisor2.lower() = static_cast<FP_Format>(-0.5);

  for (dimension_type i = 0; i < lf_dimension; ++i) {
    Variable curr_var(i);
    const FP_Interval_Type& curr_coeff = lf.coefficient(curr_var);
    PPL_ASSERT(curr_coeff.is_bounded());
    FP_Format curr_lb = curr_coeff.lower();
    FP_Format curr_ub = curr_coeff.upper();
    if (curr_lb != 0 || curr_ub != 0) {
      const FP_Interval_Type& curr_int = box.get_interval(curr_var);
      FP_Interval_Type curr_addend(curr_ub - curr_lb);
      curr_addend *= aux_divisor2;
      curr_addend *= curr_int;
      result += curr_addend;
      curr_addend = FP_Interval_Type(curr_lb + curr_ub);
      curr_addend *= aux_divisor1;
      FP_Linear_Form curr_addend_lf(curr_var);
      curr_addend_lf *= curr_addend;
      result += curr_addend_lf;
    }
  }
}

template <typename FP_Format, typename Interval_Info>
void
Polyhedron::convert_to_integer_expression(
                const Linear_Form<Interval <FP_Format, Interval_Info> >& lf,
                const dimension_type lf_dimension,
                Linear_Expression& result) {
  result = Linear_Expression();

  typedef Interval<FP_Format, Interval_Info> FP_Interval_Type;
  std::vector<Coefficient> numerators(lf_dimension+1);
  std::vector<Coefficient> denominators(lf_dimension+1);

  // Convert each floating point number to a pair <numerator, denominator>
  // and compute the lcm of all denominators.
  PPL_DIRTY_TEMP_COEFFICIENT(lcm);
  lcm = 1;
  const FP_Interval_Type& b = lf.inhomogeneous_term();
  // FIXME: are these checks numerator[i] != 0 really necessary?
  numer_denom(b.lower(), numerators[lf_dimension],
                         denominators[lf_dimension]);
  if (numerators[lf_dimension] != 0) {
      lcm_assign(lcm, lcm, denominators[lf_dimension]);
  }

  for (dimension_type i = 0; i < lf_dimension; ++i) {
    const FP_Interval_Type& curr_int = lf.coefficient(Variable(i));
    numer_denom(curr_int.lower(), numerators[i], denominators[i]);
    if (numerators[i] != 0) {
      lcm_assign(lcm, lcm, denominators[i]);
    }
  }

  for (dimension_type i = 0; i < lf_dimension; ++i) {
    if (numerators[i] != 0) {
      exact_div_assign(denominators[i], lcm, denominators[i]);
      numerators[i] *= denominators[i];
      result += numerators[i] * Variable(i);
    }
  }

  if (numerators[lf_dimension] != 0) {
    exact_div_assign(denominators[lf_dimension],
                     lcm, denominators[lf_dimension]);
    numerators[lf_dimension] *= denominators[lf_dimension];
    result += numerators[lf_dimension];
  }
}

template <typename FP_Format, typename Interval_Info>
void
Polyhedron::convert_to_integer_expressions(
                const Linear_Form<Interval <FP_Format, Interval_Info> >& lf,
                const dimension_type lf_dimension, Linear_Expression& res,
                Coefficient& res_low_coeff, Coefficient& res_hi_coeff,
                Coefficient& denominator) {
  res = Linear_Expression();

  typedef Interval<FP_Format, Interval_Info> FP_Interval_Type;
  std::vector<Coefficient> numerators(lf_dimension+2);
  std::vector<Coefficient> denominators(lf_dimension+2);

  // Convert each floating point number to a pair <numerator, denominator>
  // and compute the lcm of all denominators.
  Coefficient& lcm = denominator;
  lcm = 1;
  const FP_Interval_Type& b = lf.inhomogeneous_term();
  numer_denom(b.lower(), numerators[lf_dimension], denominators[lf_dimension]);
  // FIXME: are these checks numerator[i] != 0 really necessary?
  if (numerators[lf_dimension] != 0) {
      lcm_assign(lcm, lcm, denominators[lf_dimension]);
  }

  numer_denom(b.upper(), numerators[lf_dimension+1],
                         denominators[lf_dimension+1]);
  if (numerators[lf_dimension+1] != 0) {
      lcm_assign(lcm, lcm, denominators[lf_dimension+1]);
  }

  for (dimension_type i = 0; i < lf_dimension; ++i) {
    const FP_Interval_Type& curr_int = lf.coefficient(Variable(i));
    numer_denom(curr_int.lower(), numerators[i], denominators[i]);
    if (numerators[i] != 0) {
      lcm_assign(lcm, lcm, denominators[i]);
    }
  }

  for (dimension_type i = 0; i < lf_dimension; ++i) {
    if (numerators[i] != 0) {
      exact_div_assign(denominators[i], lcm, denominators[i]);
      numerators[i] *= denominators[i];
      res += numerators[i] * Variable(i);
    }
  }

  if (numerators[lf_dimension] != 0) {
    exact_div_assign(denominators[lf_dimension],
                     lcm, denominators[lf_dimension]);
    numerators[lf_dimension] *= denominators[lf_dimension];
    res_low_coeff = numerators[lf_dimension];
  }
  else {
    res_low_coeff = 0;
  }

  if (numerators[lf_dimension+1] != 0) {
    exact_div_assign(denominators[lf_dimension+1],
                     lcm, denominators[lf_dimension+1]);
    numerators[lf_dimension+1] *= denominators[lf_dimension+1];
    res_hi_coeff = numerators[lf_dimension+1];
  }
  else {
    res_hi_coeff = 0;
  }
}

template <typename C>
void
Polyhedron::throw_dimension_incompatible(const char* method,
                                         const char* lf_name,
                                         const Linear_Form<C>& lf) const {
  throw_dimension_incompatible(method, lf_name, lf.space_dimension());
}

template <typename Input>
Input&
Polyhedron::check_obj_space_dimension_overflow(Input& input,
                                               const Topology topol,
                                               const char* method,
                                               const char* reason) {
  check_space_dimension_overflow(input.space_dimension(),
                                 max_space_dimension(),
                                 topol, method, reason);
  return input;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Polyhedron_chdims_templates.hh line 1. */
/* Polyhedron class implementation (non-inline template operators that
   may change the dimension of the vector space).
*/


namespace Parma_Polyhedra_Library {

template <typename Linear_System1, typename Linear_System2>
void
Polyhedron::add_space_dimensions(Linear_System1& sys1,
                                 Linear_System2& sys2,
                                 Bit_Matrix& sat1,
                                 Bit_Matrix& sat2,
                                 dimension_type add_dim) {
  PPL_ASSERT(sys1.topology() == sys2.topology());
  PPL_ASSERT(sys1.space_dimension() == sys2.space_dimension());
  PPL_ASSERT(add_dim != 0);

  sys1.set_space_dimension(sys1.space_dimension() + add_dim);
  sys2.add_universe_rows_and_space_dimensions(add_dim);

  // The resulting saturation matrix will be as follows:
  // from row    0    to      add_dim-1       : only zeroes
  //          add_dim     add_dim+num_rows-1  : old saturation matrix

  // In fact all the old generators saturate all the new constraints
  // because the polyhedron has not been embedded in the new space.
  sat1.resize(sat1.num_rows() + add_dim, sat1.num_columns());
  // The old matrix is moved to the end of the new matrix.
  for (dimension_type i = sat1.num_rows() - add_dim; i-- > 0; ) {
    swap(sat1[i], sat1[i+add_dim]);
  }
  // Computes the "sat_c", too.
  sat2.transpose_assign(sat1);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Polyhedron_conversion_templates.hh line 1. */
/* Polyhedron class implementation: conversion().
*/


/* Automatically generated from PPL source file ../src/Polyhedron_conversion_templates.hh line 34. */

#include <cstddef>
#include <climits>

// This flag turns on the quick non-adjacency test;
// the performance impact of this test was evaluated by H. Le Verge (1994);
// see also Corollary 4.2 in B. Genov's PhD thesis (2014).
#ifndef PPL_QUICK_NON_ADJ_TEST
#define PPL_QUICK_NON_ADJ_TEST 1
#endif

// This flag turns on the quick adjacency test;
// for a justification, see Corollary 4.3 in B. Genov's PhD thesis (2014),
// where it is also said that the test was implemented in cddlib.
#ifndef PPL_QUICK_ADJ_TEST
#define PPL_QUICK_ADJ_TEST 1
#endif

namespace Parma_Polyhedra_Library {

/*!
  \return
  The number of lines of the polyhedron or the number of equality
  constraints in the result of conversion.

  \param source
  The system to use to convert \p dest: it may be modified;

  \param start
  The index of \p source row from which conversion begin;

  \param dest
  The result of the conversion;

  \param sat
  The saturation matrix telling us, for each row in \p source, which
  are the rows of \p dest that satisfy but do not saturate it;

  \param num_lines_or_equalities
  The number of rows in the system \p dest that are either lines of
  the polyhedron (when \p dest is a system of generators) or equality
  constraints (when \p dest is a system of constraints).

  \if Include_Implementation_Details

  For simplicity, all the following comments assume we are converting a
  constraint system \p source to a generator system \p dest;
  the comments for the symmetric case can be obtained by duality.

  If some of the constraints in \p source are redundant, they will be removed.
  This is why the \p source is not declared to be a constant parameter.

  If \p start is 0, then \p source is a sorted system; also, \p dest is
  a generator system corresponding to an empty constraint system.
  If otherwise \p start is greater than 0, then the two sub-systems of
  \p source made by the non-pending rows and the pending rows, respectively,
  are both sorted; also, \p dest is the generator system corresponding to
  the non-pending constraints of \p source.

  Independently from the value of \p start, \p dest has lines from index 0
  to index \p num_lines_or_equalities - 1 and rays/points from index
  \p num_lines_or_equalities to the last of its rows.

  Note that here the rows of \p sat are indexed by rows of \p dest
  and its columns are indexed by rows of \p source.

  We know that polyhedra can be represented by both a system of
  constraints or a system of generators (points, rays and lines)
  (see Section \ref representation).
  When we have both descriptions for a polyhedron \f$P\f$
  we have what is called a <EM>double description</EM>
  (or <EM>DD pair</EM>) for \f$P\f$.

  Here, the <EM>representation system</EM> refers to the system \f$C\f$
  whose rows represent the constraints that characterize \f$P\f$
  and the <EM>generating system</EM>, the system \f$G\f$ whose rows
  represent the generators of \f$P\f$.
  We say that a pair \f$(C, G)\f$ of (real) systems is
  a <EM>double description pair</EM> if
  \f[
    C\vect{x} \geq \vect{0}
      \quad\iff\quad
        \exists \vect{\lambda} \geq \vect{0} \mathrel{.}
        \vect{x} = G\vect{\lambda}.
  \f]

  The term "double description" is quite natural in the sense that
  such a pair contains two different description of the same object.
  In fact, if we refer to the cone representation of a polyhedron \f$P\f$
  and we call \f$C\f$ and \f$G\f$ the systems of constraints and
  rays respectively, we have
  \f[
    P = \{\, \vect{x} \in \Rset^n \mid C\vect{x} \geq \vect{0}\, \}
      = \{\, \vect{x} \in \Rset^n \mid \vect{x} = G\vect{\lambda}
      \text{ for some } \vect{\lambda} \geq \vect{0}\, \}.
  \f]

  Because of the theorem of Minkowski (see Section \ref prelims),
  we can say that, given a \f$m \times n\f$ representation system
  \f$C\f$ such that
  \f$\mathop{\mathrm{rank}}(C) = n = \mathit{dimension of the whole space}\f$
  for a non-empty polyhedron \f$P\f$,
  it is always possible to find a generating system \f$G\f$ for \f$P\f$
  such that \f$(C, G)\f$ is a DD pair.
  Conversely, Weyl's theorem ensures that, for each generating system
  \f$G\f$, it is possible to find a representation system \f$C\f$
  such that \f$(C, G)\f$ is a DD pair.

  For efficiency reasons, our representation of polyhedra makes use
  of a double description.
  We are thus left with two problems:
    -# given \f$C\f$ find \f$G\f$ such that \f$(C, G)\f$ is a DD pair;
    -# given \f$G\f$ find \f$C\f$ such that \f$(C, G)\f$ is a DD pair.

  Using Farkas' Lemma we can prove that these two problems are
  computationally equivalent (i.e., linear-time reducible to each other).
  Farkas' Lemma establishes a fundamental property of vectors in
  \f$\Rset^n\f$ that, in a sense, captures the essence of duality.
  Consider a matrix \f$A \in \Rset^{m \times n}\f$ and let
  \f$\{ \vect{a}_1, \ldots, \vect{a}_m \}\f$ be its set of row vectors.
  Consider also another vector \f$\vect{c} \in \Rset^n\f$ such that,
  whenever a vector \f$\vect{y} \in \Rset^n\f$ has a non-negative projection
  on the \f$\vect{a}_i\f$'s, it also has a non-negative projection
  on \f$\vect{c}\f$.
  The lemma states that \f$\vect{c}\f$ has this property if and only if
  it is in the cone generated by the \f$\vect{a}_i\f$'s.
  Formally, the lemma states the equivalence of the two following
  assertions:
    -# \f$
         \forall \vect{y}
           \mathrel{:} (A\vect{y} \geq 0 \implies
           \langle \vect{y},\vect{c} \rangle \geq 0)
       \f$;
    -# \f$
         \exists \vect{\lambda} \geq \vect{0}
           \mathrel{.} \vect{c}^\mathrm{T} = \vect{\lambda}^\mathrm{T}A
       \f$.

  With this result we can prove that \f$(C, G)\f$ is a DD pair
  if and only if \f$(G^\mathrm{T}, C^\mathrm{T})\f$ is a DD pair.

  Suppose \f$(C, G)\f$ is a DD pair.
  Thus, for each \f$x\f$ of the appropriate dimension,
  \f$C\vect{x} \geq \vect{0}\f$ if and only if
  \f$\exists \lambda \geq 0 \mathrel{.} \vect{x} = G\vect{\lambda}\f$,
  which is of course equivalent to
  \f$
    \exists \vect{\lambda} \geq \vect{0}
      \mathrel{.} \vect{x}^\mathrm{T} = \vect{\lambda}^\mathrm{T}G^\mathrm{T}
  \f$.

  First, we assume that \f$\vect{z}\f$ is such that
  \f$G^\mathrm{T}\vect{z} \geq \vect{0}\f$
  and we will show that
  \f$\exists \vect{\mu} \geq \vect{0} \mathrel{.}
  \vect{z} = C^\mathrm{T}\vect{\mu}\f$.
  Let \f$\vect{x}\f$ be such that \f$C\vect{x} \geq \vect{0}\f$.
  Since \f$(C, G)\f$ is a DD pair, this is equivalent to
  \f$
    \exists \vect{\lambda} \geq \vect{0}
      \mathrel{.} \vect{x}^\mathrm{T} = \vect{\lambda}^\mathrm{T}G^\mathrm{T}
  \f$,
  which, by Farkas' Lemma is equivalent to
  \f$
    \forall \vect{y} \mathrel{:} (G^\mathrm{T}\vect{y} \geq \vect{0} \implies
                                 \langle \vect{y}, \vect{x} \rangle \geq 0)
  \f$.
  Taking \f$\vect{y} = \vect{z}\f$ and recalling our assumption that
  \f$G^\mathrm{T}\vect{z} \geq \vect{0}\f$
  we can conclude that \f$\langle \vect{z}, \vect{x} \rangle \geq 0\f$,
  that is equivalent to \f$\langle \vect{x}, \vect{z} \rangle \geq 0\f$.
  We have thus established that
  \f$
    \forall \vect{x} \mathrel{:} (C\vect{x} \geq \vect{0} \implies
    \langle \vect{x}, \vect{z} \rangle \geq 0)
  \f$.
  By Farkas' Lemma, this is equivalent to
  \f$\exists \vect{\mu} \geq \vect{0} \mathrel{.}
  \vect{z}^\mathrm{T} = \vect{\mu}^\mathrm{T} C\f$,
  which is equivalent to what we wanted to prove, that is,
  \f$\exists \vect{\mu} \geq \vect{0} \mathrel{.}
  \vect{z} = C^\mathrm{T}\vect{\mu}\f$.

  In order to prove the reverse implication, the following observation
  turns out to be useful:
  when \f$(C, G)\f$ is a DD pair, \f$CG \geq 0\f$.
  In fact,
  let \f$\vect{e}_j\f$ be the vector whose components are all \f$0\f$
  apart from the \f$j\f$-th one, which is \f$1\f$.
  Clearly \f$\vect{e}_j \geq \vect{0}\f$ and, taking
  \f$\vect{\lambda} = \vect{e}_j\f$ and
  \f$\vect{x} = G\vect{\lambda} = G \vect{e}_j\f$, we have
  \f$C\vect{x} = C(G \vect{e}_j) = (CG)\vect{e}_j \geq \vect{0}\f$,
  since \f$(C, G)\f$ is a DD pair.
  Thus, as \f$(CG)\vect{e}_j\f$ is the \f$j\f$-th column of \f$CG\f$
  and since the choice of \f$j\f$ was arbitrary, \f$CG \geq \vect{0}\f$.

  We now assume that \f$\vect{z}\f$ is such that
  \f$\exists \vect{\mu} \geq \vect{0} \mathrel{.}
  \vect{z} = C^\mathrm{T}\vect{\mu}\f$
  and we will prove that \f$G^\mathrm{T}\vect{z} \geq \vect{0}\f$.
  By Farkas' Lemma, the assumption
  \f$\exists \vect{\mu} \geq \vect{0} \mathrel{.}
  \vect{z}^\mathrm{T} = \vect{\mu}^\mathrm{T}C\f$,
  is equivalent to
  \f$\forall \vect{y} \mathrel{:} (C\vect{y} \geq \vect{0}
  \implies \langle \vect{y}, \vect{z} \rangle \geq 0)\f$.
  If we take \f$\vect{y} = G\vect{e}_j\f$ then \f$C\vect{y}
                 = CG\vect{e}_j \geq 0\f$,
  since \f$CG \geq \vect{0}\f$.
  So
  \f$
    \langle \vect{y}, \vect{z} \rangle
      = (\vect{e}_j^\mathrm{T}G^\mathrm{T}) \vect{z}
      = \vect{e}_j^\mathrm{T}(G^\mathrm{T} \vect{z})
      \geq 0
  \f$,
  that is, the \f$j\f$-th component of \f$G^\mathrm{T}\vect{z}\f$
  is non-negative. The arbitrary choice of \f$j\f$ allows us to conclude
  that \f$G^\mathrm{T}\vect{z} \geq \vect{0}\f$, as required.

  In view of this result, the following exposition assumes, for clarity,
  that the conversion being performed is from constraints to generators.
  Thus, even if the roles of \p source and \p dest can be interchanged,
  in the sequel we assume the \p source system will contain the constraints
  that represent the polyhedron and the \p dest system will contain
  the generator that generates it.

  There are some observations that are useful to understand this function:

  Observation 1: Let \f$A\f$ be a system of constraints that generate
  the polyhedron \f$P\f$ and \f$\vect{c}\f$ a new constraint that must
  be added. Suppose that there is a line \f$\vect{z}\f$ that does not
  saturate the constraint \f$\vect{c}\f$. If we combine the old lines
  and rays that do not saturate \f$\vect{c}\f$ (except \f$\vect{z}\f$)
  with \f$\vect{z}\f$ such that the new ones saturate \f$\vect{c}\f$,
  the new lines and rays also saturate the constraints  saturated by
  the old lines and rays.

  In fact, if \f$\vect{y}_1\f$ is the old generator that does not saturate
  \f$\vect{c}\f$, \f$\vect{y}_2\f$ is the new one such that
  \f[
    \vect{y}_2 = \lambda \vect{y}_1 + \mu \vect{z}
  \f]
  and \f$\vect{c}_1\f$ is a previous constraint that \f$\vect{y}_1\f$
  and \f$\vect{z}\f$ saturates, we can see
  \f[
    \langle \vect{c}_1, \vect{y}_2 \rangle
    = \langle \vect{c}_1, (\lambda \vect{y}_1 + \mu \vect{z}) \rangle
    = \lambda \langle \vect{c}_1, \vect{y}_1 \rangle
       + \mu \langle \vect{c}_1, \vect{z} \rangle
       = 0 + \mu \langle \vect{c}_1, \vect{z} \rangle
       = \mu \langle \vect{c}_1, \vect{z} \rangle
  \f]
  and
  \f[
    \mu \langle \vect{c}_1, \vect{z} \rangle = 0.
  \f]

  Proposition 1: Let \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ be distinct
  rays of \f$P\f$.
  Then the following statements are equivalent:
  a) \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ are adjacent extreme rays
     (see Section \ref prelims);
  b) \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ are extreme rays and the
     rank of the system composed by the constraints saturated by both
     \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ is equal to
     \f$d - 2\f$, where \f$d\f$ is the rank of the system of constraints.

  In fact, let \f$F\f$ be the system of generators that saturate the
  constraints saturated by both \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$.
  If b) holds, the set \f$F\f$ is 2-dimensional and \f$\vect{r}_1\f$ and
  \f$\vect{r}_2\f$ generate this set. So, every generator
  \f$\vect{x}\f$ of \f$F\f$ can be built as a combination of
  \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$, i.e.
  \f[
    \vect{x} = \lambda \vect{r}_1 + \mu \vect{r}_2.
  \f]
  This combination is non-negative because there exists at least a
  constraint \f$c\f$ saturated by \f$\vect{r}_1\f$ and not
  \f$\vect{r}_2\f$ (or vice versa) (because they are distinct) for which
  \f[
    \langle \vect{c}, \vect{x} \rangle \geq 0
  \f]
  and
  \f[
    \langle \vect{c}, \vect{x} \rangle
    = \lambda \langle \vect{c}, \vect{r}_1 \rangle
                           (or = \mu \langle \vect{c}, \vect{r}_2 \rangle).
  \f]
  So, there is no other extreme ray in \f$F\f$ and a) holds.
  Otherwise, if b) does not hold, the rank of the system generated by
  the constraints saturated by both \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$
  is equal to \f$d - k\f$, with \p k \>= 3, the set \f$F\f$ is
  \p k -dimensional and at least \p k extreme rays are necessary
  to generate \f$F\f$.
  So, \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ are not adjacent and
  a) does not hold.

  Proposition 2: When we build the new system of generators starting from
  a system \f$A\f$ of constraints of \f$P\f$, if \f$\vect{c}\f$ is the
  constraint to add to \f$A\f$ and all lines of \f$P\f$ saturate
  \f$\vect{c}\f$, the new set of rays is the union of those rays that
  saturate, of those that satisfy and of a set \f$\overline Q\f$ of
  rays such that each of them
  -# lies on the hyper-plane represented by the k-th constraint,
  -# is a positive combination of two adjacent rays \f$\vect{r}_1\f$ and
     \f$\vect{r}_2\f$ such that the first one satisfies the constraint and
     the other does not satisfy it.
  If the adjacency property is not taken in account, the new set of
  rays is not irredundant, in general.

  In fact, if \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ are not adjacent,
  the rank of the system composed by the constraints saturated by both
  \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$ is different from \f$d - 2\f$
  (see the previous proposition) or neither \f$\vect{r}_1\f$ nor
  \f$\vect{r}_2\f$ are extreme rays. Since the new ray \f$\vect{r}\f$
  is a combination of \f$\vect{r}_1\f$ and \f$\vect{r}_2\f$,
  it saturates the same constraints saturated by both \f$\vect{r}_1\f$ and
  \f$\vect{r}_2\f$.
  If the rank is less than \f$d - 2\f$, the rank of
  the system composed by \f$\vect{c}\f$ (that is saturated by \f$\vect{r}\f$)
  and by the constraints of \f$A\f$ saturated by \f$\vect{r}\f$  is less
  than \f$d - 1\f$. It means that \f$r\f$ is redundant (see
  Section \ref prelims).
  If neither \f$\vect{r}_1\f$ nor \f$\vect{r}_2\f$ are extreme rays,
  they belong to a 2-dimensional face containing exactly two extreme rays
  of \f$P\f$.
  These two adjacent rays build a ray equal to \f$\vect{r}\f$ and so
  \f$\vect{r}\f$ is redundant.

  \endif
*/
template <typename Source_Linear_System, typename Dest_Linear_System>
dimension_type
Polyhedron::conversion(Source_Linear_System& source,
                       const dimension_type start,
                       Dest_Linear_System& dest,
                       Bit_Matrix& sat,
                       dimension_type num_lines_or_equalities) {
  typedef typename Dest_Linear_System::row_type dest_row_type;
  typedef typename Source_Linear_System::row_type source_row_type;

  // Constraints and generators must have the same dimension,
  // otherwise the scalar products below will bomb.
  PPL_ASSERT(source.space_dimension() == dest.space_dimension());
  const dimension_type source_space_dim = source.space_dimension();
  const dimension_type source_num_rows = source.num_rows();
  const dimension_type source_num_columns = source_space_dim
    + (source.is_necessarily_closed() ? 1U : 2U);


  dimension_type dest_num_rows = dest.num_rows();
  // The rows removed from `dest' will be placed in this vector, so they
  // can be recycled if needed.
  std::vector<dest_row_type> recyclable_dest_rows;

  using std::swap;

  // By construction, the number of columns of `sat' is the same as
  // the number of rows of `source'; also, the number of rows of `sat'
  // is the same as the number of rows of `dest'.
  PPL_ASSERT(source_num_rows == sat.num_columns());
  PPL_ASSERT(dest_num_rows == sat.num_rows());

  // If `start > 0', then we are converting the pending constraints.
  PPL_ASSERT(start == 0 || start == source.first_pending_row());

  PPL_DIRTY_TEMP_COEFFICIENT(normalized_sp_i);
  PPL_DIRTY_TEMP_COEFFICIENT(normalized_sp_o);

  bool dest_sorted = dest.is_sorted();
  const dimension_type dest_first_pending_row = dest.first_pending_row();

  // This will contain the row indexes of the redundant rows of `source'.
  std::vector<dimension_type> redundant_source_rows;

#if PPL_QUICK_ADJ_TEST
  // This will contain the number of ones in each row of `sat'.
  PPL_DIRTY_TEMP(std::vector<dimension_type>, sat_num_ones);
  sat_num_ones.resize(dest_num_rows, 0);
  for (dimension_type i = dest_num_rows; i-- > 0; ) {
    sat_num_ones[i] = sat[i].count_ones();
  }
#endif // PPL_QUICK_ADJ_TEST

  // Converting the sub-system of `source' having rows with indexes
  // from `start' to the last one (i.e., `source_num_rows' - 1).
  for (dimension_type k = start; k < source_num_rows; ++k) {
    const source_row_type& source_k = source[k];

#ifndef NDEBUG
#if PPL_QUICK_ADJ_TEST
    for (dimension_type i = dest_num_rows; i-- > 0; ) {
      PPL_ASSERT(sat_num_ones[i] == sat[i].count_ones());
    }
#endif // PPL_QUICK_ADJ_TEST
#endif // NDEBUG

    // `scalar_prod[i]' will contain the scalar product of the
    // constraint `source_k' and the generator `dest_rows[i]'.  This
    // product is 0 if and only if the generator saturates the
    // constraint.
    PPL_DIRTY_TEMP(std::vector<Coefficient>, scalar_prod);
    if (dest_num_rows > scalar_prod.size()) {
      scalar_prod.insert(scalar_prod.end(),
                         dest_num_rows - scalar_prod.size(),
                         Coefficient_zero());
    }
    // `index_non_zero' will indicate the first generator in `dest_rows'
    // that does not saturate the constraint `source_k'.
    dimension_type index_non_zero = 0;
    for ( ; index_non_zero < dest_num_rows; ++index_non_zero) {
      WEIGHT_BEGIN();
      Scalar_Products::assign(scalar_prod[index_non_zero],
                              source_k,
                              dest.sys.rows[index_non_zero]);
      WEIGHT_ADD_MUL(17, source_space_dim);
      if (scalar_prod[index_non_zero] != 0) {
        // The generator does not saturate the constraint.
        break;
      }
      // Check if the client has requested abandoning all expensive
      // computations.  If so, the exception specified by the client
      // is thrown now.
      maybe_abandon();
    }
    for (dimension_type i = index_non_zero + 1; i < dest_num_rows; ++i) {
      WEIGHT_BEGIN();
      Scalar_Products::assign(scalar_prod[i], source_k, dest.sys.rows[i]);
      WEIGHT_ADD_MUL(25, source_space_dim);
      // Check if the client has requested abandoning all expensive
      // computations.  If so, the exception specified by the client
      // is thrown now.
      maybe_abandon();
    }

    // We first treat the case when `index_non_zero' is less than
    // `num_lines_or_equalities', i.e., when the generator that
    // does not saturate the constraint `source_k' is a line.
    // The other case (described later) is when all the lines
    // in `dest_rows' (i.e., all the rows having indexes less than
    // `num_lines_or_equalities') do saturate the constraint.

    if (index_non_zero < num_lines_or_equalities) {
      // Since the generator `dest_rows[index_non_zero]' does not saturate
      // the constraint `source_k', it can no longer be a line
      // (see saturation rule in Section \ref prelims).
      // Therefore, we first transform it to a ray.
      dest.sys.rows[index_non_zero].set_is_ray_or_point_or_inequality();
      // Of the two possible choices, we select the ray satisfying
      // the constraint (namely, the ray whose scalar product
      // with the constraint gives a positive result).
      if (scalar_prod[index_non_zero] < 0) {
        // The ray `dest_rows[index_non_zero]' lies on the wrong half-space:
        // we change it to have the opposite direction.
        neg_assign(scalar_prod[index_non_zero]);
        neg_assign(dest.sys.rows[index_non_zero].expr);
        // The modified row may still not be OK(), so don't assert OK here.
        // They are all checked at the end of this function.
      }
      // Having changed a line to a ray, we set `dest_rows' to be a
      // non-sorted system, we decrement the number of lines of `dest_rows'
      // and, if necessary, we move the new ray below all the remaining lines.
      dest_sorted = false;
      --num_lines_or_equalities;
      if (index_non_zero != num_lines_or_equalities) {
        swap(dest.sys.rows[index_non_zero],
             dest.sys.rows[num_lines_or_equalities]);
        swap(scalar_prod[index_non_zero],
             scalar_prod[num_lines_or_equalities]);
      }
      const dest_row_type& dest_nle = dest.sys.rows[num_lines_or_equalities];

      // Computing the new lineality space.
      // Since each line must lie on the hyper-plane corresponding to
      // the constraint `source_k', the scalar product between
      // the line and the constraint must be 0.
      // This property already holds for the lines having indexes
      // between 0 and `index_non_zero' - 1.
      // We have to consider the remaining lines, having indexes
      // between `index_non_zero' and `num_lines_or_equalities' - 1.
      // Each line that does not saturate the constraint has to be
      // linearly combined with generator `dest_nle' so that the
      // resulting new line saturates the constraint.
      // Note that, by Observation 1 above, the resulting new line
      // will still saturate all the constraints that were saturated by
      // the old line.

      Coefficient& scalar_prod_nle = scalar_prod[num_lines_or_equalities];
      PPL_ASSERT(scalar_prod_nle != 0);
      for (dimension_type
             i = index_non_zero; i < num_lines_or_equalities; ++i) {
        if (scalar_prod[i] != 0) {
          // The following fragment optimizes the computation of
          //
          // <CODE>
          //   Coefficient scale = scalar_prod[i];
          //   scale.gcd_assign(scalar_prod_nle);
          //   Coefficient normalized_sp_i = scalar_prod[i] / scale;
          //   Coefficient normalized_sp_n = scalar_prod_nle / scale;
          //   for (dimension_type c = dest_num_columns; c-- > 0; ) {
          //     dest[i][c] *= normalized_sp_n;
          //     dest[i][c] -= normalized_sp_i * dest_nle[c];
          //   }
          // </CODE>
          normalize2(scalar_prod[i],
                     scalar_prod_nle,
                     normalized_sp_i,
                     normalized_sp_o);
          dest_row_type& dest_i = dest.sys.rows[i];
          neg_assign(normalized_sp_i);
          dest_i.expr.linear_combine(dest_nle.expr,
                                     normalized_sp_o, normalized_sp_i);
          dest_i.strong_normalize();
          // The modified row may still not be OK(), so don't assert OK here.
          // They are all checked at the end of this function.
          scalar_prod[i] = 0;
          // dest_sorted has already been set to false.
        }
      }

      // Computing the new pointed cone.
      // Similarly to what we have done during the computation of
      // the lineality space, we consider all the remaining rays
      // (having indexes strictly greater than `num_lines_or_equalities')
      // that do not saturate the constraint `source_k'. These rays
      // are positively combined with the ray `dest_nle' so that the
      // resulting new rays saturate the constraint.
      for (dimension_type
             i = num_lines_or_equalities + 1; i < dest_num_rows; ++i) {
        if (scalar_prod[i] != 0) {
          // The following fragment optimizes the computation of
          //
          // <CODE>
          //   Coefficient scale = scalar_prod[i];
          //   scale.gcd_assign(scalar_prod_nle);
          //   Coefficient normalized_sp_i = scalar_prod[i] / scale;
          //   Coefficient normalized_sp_n = scalar_prod_nle / scale;
          //   for (dimension_type c = dest_num_columns; c-- > 0; ) {
          //     dest[i][c] *= normalized_sp_n;
          //     dest[i][c] -= normalized_sp_i * dest_nle[c];
          //   }
          // </CODE>
          normalize2(scalar_prod[i],
                     scalar_prod_nle,
                     normalized_sp_i,
                     normalized_sp_o);
          dest_row_type& dest_i = dest.sys.rows[i];
          WEIGHT_BEGIN();
          neg_assign(normalized_sp_i);
          dest_i.expr.linear_combine(dest_nle.expr,
                                     normalized_sp_o, normalized_sp_i);
          dest_i.strong_normalize();
          // The modified row may still not be OK(), so don't assert OK here.
          // They are all checked at the end of this function.
          scalar_prod[i] = 0;
          // `dest_sorted' has already been set to false.
          WEIGHT_ADD_MUL(41, source_space_dim);
        }
        // Check if the client has requested abandoning all expensive
        // computations.  If so, the exception specified by the client
        // is thrown now.
        maybe_abandon();
      }
      // Since the `scalar_prod_nle' is positive (by construction), it
      // does not saturate the constraint `source_k'.  Therefore, if
      // the constraint is an inequality, we set to 1 the
      // corresponding element of `sat' ...
      Bit_Row& sat_nle = sat[num_lines_or_equalities];
      if (source_k.is_ray_or_point_or_inequality()) {
        sat_nle.set(k - redundant_source_rows.size());
#if PPL_QUICK_ADJ_TEST
        ++sat_num_ones[num_lines_or_equalities];
#endif // PPL_QUICK_ADJ_TEST
      }
      else {
        // ... otherwise, the constraint is an equality which is
        // violated by the generator `dest_nle': the generator has to be
        // removed from `dest_rows'.
        --dest_num_rows;
        swap(dest.sys.rows[num_lines_or_equalities],
             dest.sys.rows[dest_num_rows]);
        recyclable_dest_rows.resize(recyclable_dest_rows.size() + 1);
        swap(dest.sys.rows.back(), recyclable_dest_rows.back());
        dest.sys.rows.pop_back();
        PPL_ASSERT(dest_num_rows == dest.sys.rows.size());

        swap(scalar_prod_nle, scalar_prod[dest_num_rows]);
        swap(sat_nle, sat[dest_num_rows]);
#if PPL_QUICK_ADJ_TEST
        swap(sat_num_ones[num_lines_or_equalities],
             sat_num_ones[dest_num_rows]);
#endif // PPL_QUICK_ADJ_TEST
        // dest_sorted has already been set to false.
      }
      // Finished handling the line or equality case:
      // continue with next `k'.
      continue;
    }

    // Here all the lines in `dest_rows' saturate the constraint `source_k'.
    PPL_ASSERT(index_non_zero >= num_lines_or_equalities);
    // First, we reorder the generators in `dest_rows' as follows:
    // -# all the lines should have indexes between 0 and
    //    `num_lines_or_equalities' - 1 (this already holds);
    // -# all the rays that saturate the constraint should have
    //    indexes between `num_lines_or_equalities' and
    //    `lines_or_equal_bound' - 1; these rays form the set Q=.
    // -# all the rays that have a positive scalar product with the
    //    constraint should have indexes between `lines_or_equal_bound'
    //    and `sup_bound' - 1; these rays form the set Q+.
    // -# all the rays that have a negative scalar product with the
    //    constraint should have indexes between `sup_bound' and
    //    `dest_num_rows' - 1; these rays form the set Q-.
    dimension_type lines_or_equal_bound = num_lines_or_equalities;
    dimension_type inf_bound = dest_num_rows;
    // While we find saturating generators, we simply increment
    // `lines_or_equal_bound'.
    while (inf_bound > lines_or_equal_bound
           && scalar_prod[lines_or_equal_bound] == 0) {
      ++lines_or_equal_bound;
    }
    dimension_type sup_bound = lines_or_equal_bound;
    while (inf_bound > sup_bound) {
      const int sp_sign = sgn(scalar_prod[sup_bound]);
      if (sp_sign == 0) {
        // This generator has to be moved in Q=.
        swap(dest.sys.rows[sup_bound], dest.sys.rows[lines_or_equal_bound]);
        swap(scalar_prod[sup_bound], scalar_prod[lines_or_equal_bound]);
        swap(sat[sup_bound], sat[lines_or_equal_bound]);
#if PPL_QUICK_ADJ_TEST
        swap(sat_num_ones[sup_bound], sat_num_ones[lines_or_equal_bound]);
#endif // PPL_QUICK_ADJ_TEST
        ++lines_or_equal_bound;
        ++sup_bound;
        dest_sorted = false;
      }
      else if (sp_sign < 0) {
        // This generator has to be moved in Q-.
        --inf_bound;
        swap(dest.sys.rows[sup_bound], dest.sys.rows[inf_bound]);
        swap(sat[sup_bound], sat[inf_bound]);
        swap(scalar_prod[sup_bound], scalar_prod[inf_bound]);
#if PPL_QUICK_ADJ_TEST
        swap(sat_num_ones[sup_bound], sat_num_ones[inf_bound]);
#endif // PPL_QUICK_ADJ_TEST
        dest_sorted = false;
      }
      else {
        // sp_sign > 0: this generator has to be moved in Q+.
        ++sup_bound;
      }
    }

    if (sup_bound == dest_num_rows) {
      // Here the set Q- is empty.
      // If the constraint is an inequality, then all the generators
      // in Q= and Q+ satisfy the constraint. The constraint is redundant
      // and it can be safely removed from the constraint system.
      // This is why the `source' parameter is not declared `const'.
      if (source_k.is_ray_or_point_or_inequality()) {
        redundant_source_rows.push_back(k);
      }
      else {
        // The constraint is an equality, so that all the generators
        // in Q+ violate it. Since the set Q- is empty, we can simply
        // remove from `dest_rows' all the generators of Q+.
        PPL_ASSERT(dest_num_rows >= lines_or_equal_bound);
        while (dest_num_rows != lines_or_equal_bound) {
          recyclable_dest_rows.resize(recyclable_dest_rows.size() + 1);
          swap(dest.sys.rows.back(), recyclable_dest_rows.back());
          dest.sys.rows.pop_back();
          --dest_num_rows;
          }
        PPL_ASSERT(dest_num_rows == dest.sys.rows.size());
      }
      // Finished handling the case when Q- is empty:
      // continue with next `k'.
      continue;
    }

    // The set Q- is not empty, i.e., at least one generator
    // violates the constraint `source_k'.
    if (sup_bound == num_lines_or_equalities) {
      // The set Q+ is empty, so that all generators that satisfy
      // the constraint also saturate it.
      // We can simply remove from `dest_rows' all the generators in Q-.
      PPL_ASSERT(dest_num_rows >= sup_bound);
      while (dest_num_rows != sup_bound) {
        recyclable_dest_rows.resize(recyclable_dest_rows.size() + 1);
        swap(dest.sys.rows.back(), recyclable_dest_rows.back());
        dest.sys.rows.pop_back();
        --dest_num_rows;
      }
      PPL_ASSERT(dest_num_rows == dest.sys.rows.size());
      // Finished handling the case when Q+ is empty:
      // continue with next `k'.
      continue;
    }

    // The sets Q+ and Q- are both non-empty.
    // The generators of the new pointed cone are all those satisfying
    // the constraint `source_k' plus a set of new rays enjoying
    // the following properties:
    // -# they lie on the hyper-plane represented by the constraint
    // -# they are obtained as a positive combination of two
    //    adjacent rays, the first taken from Q+ and the second
    //    taken from Q-.

    const dimension_type bound = dest_num_rows;

#if PPL_QUICK_NON_ADJ_TEST
    // For the quick non-adjacency test, we refer to the definition
    // of a minimal proper face (see comments in Polyhedron_defs.hh):
    // an extremal ray saturates at least `n' - `t' - 1 constraints,
    // where `n' is the dimension of the space and `t' is the dimension
    // of the lineality space. Since `n == source_num_columns - 1' and
    // `t == num_lines_or_equalities', we obtain that an extremal ray
    // saturates at least `source_num_columns - num_lines_or_equalities - 2'
    // constraints.
    const dimension_type min_saturators
      = source_num_columns - num_lines_or_equalities - 2;
    // NOTE: we are treating the `k'-th constraint.
    const dimension_type max_saturators = k - redundant_source_rows.size();
#endif // PPL_QUICK_NON_ADJ_TEST

    // In the following loop,
    // `i' runs through the generators in the set Q+ and
    // `j' runs through the generators in the set Q-.
    for (dimension_type i = lines_or_equal_bound; i < sup_bound; ++i) {
      for (dimension_type j = sup_bound; j < bound; ++j) {
        // Checking if generators `dest_rows[i]' and `dest_rows[j]' are
        // adjacent.
        // If there exist another generator that saturates
        // all the constraints saturated by both `dest_rows[i]' and
        // `dest_rows[j]', then they are NOT adjacent.
        PPL_ASSERT(sat[i].last() == C_Integer<unsigned long>::max
                   || sat[i].last() < k);
        PPL_ASSERT(sat[j].last() == C_Integer<unsigned long>::max
                   || sat[j].last() < k);

        // Being the union of `sat[i]' and `sat[j]',
        // `new_satrow' corresponds to a ray that saturates all the
        // constraints saturated by both `dest_rows[i]' and
        // `dest_rows[j]'.
        Bit_Row new_satrow(sat[i], sat[j]);

        // Even before actually creating the new ray as a
        // positive combination of `dest_rows[i]' and `dest_rows[j]',
        // we exploit saturation information to perform:
        //  - a quick non-adjacency test;
        //  - a quick adjacency test.

#if (PPL_QUICK_NON_ADJ_TEST || PPL_QUICK_ADJ_TEST)
        // Compute the number of common saturators.
        dimension_type new_satrow_ones = new_satrow.count_ones();
#endif // (PPL_QUICK_NON_ADJ_TEST || PPL_QUICK_ADJ_TEST)

#if PPL_QUICK_NON_ADJ_TEST
        const dimension_type num_common_satur
          = max_saturators - new_satrow_ones;
        if (num_common_satur < min_saturators) {
          // Quick non-adjacency test succeded: consider next `j'.
          continue;
        }
#endif // PPL_QUICK_NON_ADJ_TEST

#if PPL_QUICK_ADJ_TEST
        // If either `sat[i]' or `sat[j]' has exactly one more zeroes
        // than `new_satrow', then `dest_rows[i]' and `dest_rows[j]'
        // are adjacent. Equivalently, adjacency holds if `new_satrow_ones'
        // is equal to 1 plus the maximum of `sat_num_ones[i]' and
        // `sat_num_ones[j]'.
        const dimension_type max_ones_i_j
          = std::max(sat_num_ones[i], sat_num_ones[j]);
        if (max_ones_i_j + 1 == new_satrow_ones) {
          // Quick adjacency test succeded: skip the full test.
          goto are_adjacent;
        }
#endif // PPL_QUICK_ADJ_TEST

        // Perform the full (combinatorial) adjacency test.
        {
          bool redundant = false;
          WEIGHT_BEGIN();
          for (dimension_type l = num_lines_or_equalities; l < bound; ++l) {
            if (l != i && l != j
                && subset_or_equal(sat[l], new_satrow)) {
              // Found another generator saturating all the constraints
              // saturated by both `dest_rows[i]' and `dest_rows[j]'.
              redundant = true;
              break;
            }
          }
          PPL_ASSERT(bound >= num_lines_or_equalities);
          WEIGHT_ADD_MUL(15, bound - num_lines_or_equalities);
          if (redundant) {
            // Full non-adjacency test succeded: consider next `j'.
            continue;
          }
        }

#if PPL_QUICK_ADJ_TEST
      are_adjacent:
#endif // PPL_QUICK_ADJ_TEST
        // Adding the new ray to `dest_rows' and the corresponding
        // saturation row to `sat'.
        dest_row_type new_row;
        if (recyclable_dest_rows.empty()) {
          sat.add_recycled_row(new_satrow);
#if PPL_QUICK_ADJ_TEST
          sat_num_ones.push_back(new_satrow_ones);
#endif // PPL_QUICK_ADJ_TEST
        }
        else {
          swap(new_row, recyclable_dest_rows.back());
          recyclable_dest_rows.pop_back();
          new_row.set_space_dimension_no_ok(source_space_dim);
          swap(sat[dest_num_rows], new_satrow);
#if PPL_QUICK_ADJ_TEST
          swap(sat_num_ones[dest_num_rows], new_satrow_ones);
#endif // PPL_QUICK_ADJ_TEST
        }

        // The following fragment optimizes the computation of
        //
        // <CODE>
        //   Coefficient scale = scalar_prod[i];
        //   scale.gcd_assign(scalar_prod[j]);
        //   Coefficient normalized_sp_i = scalar_prod[i] / scale;
        //   Coefficient normalized_sp_j = scalar_prod[j] / scale;
        //   for (dimension_type c = dest_num_columns; c-- > 0; ) {
        //     new_row[c] = normalized_sp_i * dest[j][c];
        //     new_row[c] -= normalized_sp_j * dest[i][c];
        //   }
        // </CODE>
        normalize2(scalar_prod[i],
                   scalar_prod[j],
                   normalized_sp_i,
                   normalized_sp_o);
        WEIGHT_BEGIN();

        neg_assign(normalized_sp_o);
        new_row = dest.sys.rows[j];
        // TODO: Check if the following assertions hold.
        PPL_ASSERT(normalized_sp_i != 0);
        PPL_ASSERT(normalized_sp_o != 0);
        new_row.expr.linear_combine(dest.sys.rows[i].expr,
                                    normalized_sp_i, normalized_sp_o);

        WEIGHT_ADD_MUL(86, source_space_dim);
        new_row.strong_normalize();
        // Don't assert new_row.OK() here, because it may fail if
        // the parameter `dest' contained a row that wasn't ok.
        // Since we added a new generator to `dest_rows',
        // we also add a new element to `scalar_prod';
        // by construction, the new ray lies on the hyper-plane
        // represented by the constraint `source_k'.
        // Thus, the added scalar product is 0.
        PPL_ASSERT(scalar_prod.size() >= dest_num_rows);
        if (scalar_prod.size() <= dest_num_rows) {
          scalar_prod.push_back(Coefficient_zero());
        }
        else {
          scalar_prod[dest_num_rows] = Coefficient_zero();
        }
        dest.sys.rows.resize(dest.sys.rows.size() + 1);
        swap(dest.sys.rows.back(), new_row);
        // Increment the number of generators.
        ++dest_num_rows;
      } // End of loop on `j'.
      // Check if the client has requested abandoning all expensive
      // computations.  If so, the exception specified by the client
      // is thrown now.
      maybe_abandon();
    } // End of loop on `i'.
    // Now we substitute the rays in Q- (i.e., the rays violating
    // the constraint) with the newly added rays.
    dimension_type j;
    if (source_k.is_ray_or_point_or_inequality()) {
      // The constraint is an inequality:
      // the violating generators are those in Q-.
      j = sup_bound;
      // For all the generators in Q+, set to 1 the corresponding
      // entry for the constraint `source_k' in the saturation matrix.

      // After the removal of redundant rows in `source', the k-th
      // row will have index `new_k'.
      const dimension_type new_k = k - redundant_source_rows.size();
      for (dimension_type l = lines_or_equal_bound;
           l < sup_bound; ++l) {
        sat[l].set(new_k);
#if PPL_QUICK_ADJ_TEST
        ++sat_num_ones[l];
#endif // PPL_PPL_QUICK_ADJ_TEST
      }
    }
    else {
      // The constraint is an equality:
      // the violating generators are those in the union of Q+ and Q-.
      j = lines_or_equal_bound;
    }
    // Swapping the newly added rays
    // (index `i' running through `dest_num_rows - 1' down-to `bound')
    // with the generators violating the constraint
    // (index `j' running through `j' up-to `bound - 1').
    dimension_type i = dest_num_rows;
    while (j < bound && i > bound) {
      --i;
      swap(dest.sys.rows[i], dest.sys.rows[j]);
      swap(scalar_prod[i], scalar_prod[j]);
      swap(sat[i], sat[j]);
#if PPL_QUICK_ADJ_TEST
      swap(sat_num_ones[i], sat_num_ones[j]);
#endif // PPL_QUICK_ADJ_TEST
      ++j;
      dest_sorted = false;
    }
    // Setting the number of generators in `dest':
    // - if the number of generators violating the constraint
    //   is less than or equal to the number of the newly added
    //   generators, we assign `i' to `dest_num_rows' because
    //   all generators above this index are significant;
    // - otherwise, we assign `j' to `dest_num_rows' because
    //   all generators below index `j-1' violates the constraint.
    const dimension_type new_num_rows = (j == bound) ? i : j;
    PPL_ASSERT(dest_num_rows >= new_num_rows);
    while (dest_num_rows != new_num_rows) {
      recyclable_dest_rows.resize(recyclable_dest_rows.size() + 1);
      swap(dest.sys.rows.back(), recyclable_dest_rows.back());
      dest.sys.rows.pop_back();
      --dest_num_rows;
    }
    PPL_ASSERT(dest_num_rows == dest.sys.rows.size());
  } // End of loop on `k'.

  // We may have identified some redundant constraints in `source',
  // which have been swapped at the end of the system.
  if (redundant_source_rows.size() > 0) {
    source.remove_rows(redundant_source_rows);
    sat.remove_trailing_columns(redundant_source_rows.size());
  }

  // If `start == 0', then `source' was sorted and remained so.
  // If otherwise `start > 0', then the two sub-system made by the
  // non-pending rows and the pending rows, respectively, were both sorted.
  // Thus, the overall system is sorted if and only if either
  // `start == source_num_rows' (i.e., the second sub-system is empty)
  // or the row ordering holds for the two rows at the boundary between
  // the two sub-systems.
  if (start > 0 && start < source.num_rows()) {
    source.set_sorted(compare(source[start - 1], source[start]) <= 0);
  }
  // There are no longer pending constraints in `source'.
  source.unset_pending_rows();

  // We may have identified some redundant rays in `dest_rows',
  // which have been swapped into recyclable_dest_rows.
  if (!recyclable_dest_rows.empty()) {
    const dimension_type num_removed_rows = recyclable_dest_rows.size();
    sat.remove_trailing_rows(num_removed_rows);
  }
  if (dest_sorted) {
    // If the non-pending generators in `dest' are still declared to be
    // sorted, then we have to also check for the sortedness of the
    // pending generators.
    for (dimension_type i = dest_first_pending_row;
         i < dest_num_rows; ++i) {
      if (compare(dest.sys.rows[i - 1], dest.sys.rows[i]) > 0) {
        dest_sorted = false;
        break;
      }
    }
  }
#ifndef NDEBUG
  // The previous code can modify the rows' fields, exploiting the friendness.
  // Check that all rows are OK now.
  for (dimension_type i = dest.num_rows(); i-- > 0; ) {
    PPL_ASSERT(dest.sys.rows[i].OK());
  }
#endif

  dest.sys.index_first_pending = dest.num_rows();
  dest.set_sorted(dest_sorted);
  PPL_ASSERT(dest.sys.OK());

  return num_lines_or_equalities;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Polyhedron_minimize_templates.hh line 1. */
/* Polyhedron class implementation: minimize() and add_and_minimize().
*/


/* Automatically generated from PPL source file ../src/Polyhedron_minimize_templates.hh line 29. */
#include <stdexcept>

namespace Parma_Polyhedra_Library {

/*!
  \return
  <CODE>true</CODE> if the polyhedron is empty, <CODE>false</CODE>
  otherwise.

  \param con_to_gen
  <CODE>true</CODE> if \p source represents the constraints,
  <CODE>false</CODE> otherwise;

  \param source
  The given system, which is not empty;

  \param dest
  The system to build and minimize;

  \param sat
  The saturation matrix.

  \p dest is not <CODE>const</CODE> because it will be built (and then
  modified) during minimize(). Also, \p sat and \p source are
  not <CODE>const</CODE> because the former will be built during
  \p dest creation and the latter will maybe be sorted and modified by
  <CODE>conversion()</CODE> and <CODE>simplify()</CODE>.

  \p sat has the generators on its columns and the constraints on its rows
  if \p con_to_gen is <CODE>true</CODE>, otherwise it has the generators on
  its rows and the constraints on its columns.

  Given \p source, this function builds (by means of
  <CODE>conversion()</CODE>) \p dest and then simplifies (invoking
  <CODE>simplify()</CODE>) \p source, erasing redundant rows.
  For the sequel we assume that \p source is the system of constraints
  and \p dest is the system of generators.
  This will simplify the description of the function; the dual case is
  similar.
*/
template <typename Source_Linear_System, typename Dest_Linear_System>
bool
Polyhedron::minimize(const bool con_to_gen,
                     Source_Linear_System& source,
                     Dest_Linear_System& dest,
                     Bit_Matrix& sat) {

  typedef typename Dest_Linear_System::row_type dest_row_type;

  // Topologies have to agree.
  PPL_ASSERT(source.topology() == dest.topology());
  // `source' cannot be empty: even if it is an empty constraint system,
  // representing the universe polyhedron, homogenization has added
  // the positive constraint. It also cannot be an empty generator system,
  // since this function is always called starting from a non-empty
  // polyhedron.
  PPL_ASSERT(!source.has_no_rows());

  // Sort the source system, if necessary.
  if (!source.is_sorted()) {
    source.sort_rows();
  }
  // Initialization of the system of generators `dest'.
  // The algorithm works incrementally and we haven't seen any
  // constraint yet: as a consequence, `dest' should describe
  // the universe polyhedron of the appropriate dimension.
  // To this end, we initialize it to the identity matrix of dimension
  // `source.num_columns()': the rows represent the lines corresponding
  // to the canonical basis of the vector space.
  dimension_type dest_num_rows
    = source.topology() == NECESSARILY_CLOSED ? source.space_dimension() + 1
                                              : source.space_dimension() + 2;

  dest.clear();
  dest.set_space_dimension(source.space_dimension());

  // Initialize `dest' to the identity matrix.
  for (dimension_type i = 0; i < dest_num_rows; ++i) {
    Linear_Expression expr;
    expr.set_space_dimension(dest_num_rows - 1);
    if (i == 0) {
      expr += 1;
    }
    else {
      expr += Variable(i - 1);
    }
    dest_row_type dest_i(expr, dest_row_type::LINE_OR_EQUALITY, NECESSARILY_CLOSED);
    if (dest.topology() == NOT_NECESSARILY_CLOSED) {
      dest_i.mark_as_not_necessarily_closed();
    }
    dest.sys.insert_no_ok(dest_i, Recycle_Input());
  }
  // The identity matrix `dest' is not sorted (see the sorting rules
  // in Constrant.cc and Generator.cc).
  dest.set_sorted(false);

  // NOTE: the system `dest', as it is now, is not a _legal_ system of
  //       generators, because in the first row we have a line with a
  //       non-zero divisor (which should only happen for
  //       points). However, this is NOT a problem, because `source'
  //       necessarily contains the positivity constraint (or a
  //       combination of it with another constraint) which will
  //       restore things as they should be.


  // Building a saturation matrix and initializing it by setting
  // all of its elements to zero. This matrix will be modified together
  // with `dest' during the conversion.
  // NOTE: since we haven't seen any constraint yet, the relevant
  //       portion of `tmp_sat' is the sub-matrix consisting of
  //       the first 0 columns: thus the relevant portion correctly
  //       characterizes the initial saturation information.
  Bit_Matrix tmp_sat(dest_num_rows, source.num_rows());

  // By invoking the function conversion(), we populate `dest' with
  // the generators characterizing the polyhedron described by all
  // the constraints in `source'.
  // The `start' parameter is zero (we haven't seen any constraint yet)
  // and the 5th parameter (representing the number of lines in `dest'),
  // by construction, is equal to `dest_num_rows'.
  const dimension_type num_lines_or_equalities
    = conversion(source, 0U, dest, tmp_sat, dest_num_rows);
  // conversion() may have modified the number of rows in `dest'.
  dest_num_rows = dest.num_rows();

#ifndef NDEBUG
  for (dimension_type i = dest.num_rows(); i-- > 0; ) {
    PPL_ASSERT(dest[i].OK());
  }
#endif

  // Checking if the generators in `dest' represent an empty polyhedron:
  // the polyhedron is empty if there are no points
  // (because rays, lines and closure points need a supporting point).
  // Points can be detected by looking at:
  // - the divisor, for necessarily closed polyhedra;
  // - the epsilon coordinate, for NNC polyhedra.
  dimension_type first_point;
  if (dest.is_necessarily_closed()) {
    for (first_point = num_lines_or_equalities;
        first_point < dest_num_rows;
        ++first_point) {
      if (dest[first_point].expr.inhomogeneous_term() > 0) {
        break;
      }
    }
  }
  else {
    for (first_point = num_lines_or_equalities;
        first_point < dest_num_rows;
        ++first_point) {
      if (dest[first_point].expr.get(Variable(dest.space_dimension())) > 0) {
        break;
      }
    }
  }

  if (first_point == dest_num_rows) {
    if (con_to_gen) {
      // No point has been found: the polyhedron is empty.
      return true;
    }
    else {
      // Here `con_to_gen' is false: `dest' is a system of constraints.
      // In this case the condition `first_point == dest_num_rows'
      // actually means that all the constraints in `dest' have their
      // inhomogeneous term equal to 0.
      // This is an ILLEGAL situation, because it implies that
      // the constraint system `dest' lacks the positivity constraint
      // and no linear combination of the constraints in `dest'
      // can reintroduce the positivity constraint.
      PPL_UNREACHABLE;
      return false;
    }
  }
  else {
    // A point has been found: the polyhedron is not empty.
    // Now invoking simplify() to remove all the redundant constraints
    // from the system `source'.
    // Since the saturation matrix `tmp_sat' returned by conversion()
    // has rows indexed by generators (the rows of `dest') and columns
    // indexed by constraints (the rows of `source'), we have to
    // transpose it to obtain the saturation matrix needed by simplify().
    sat.transpose_assign(tmp_sat);
    simplify(source, sat);
    return false;
  }
}


/*!
  \return
  <CODE>true</CODE> if the obtained polyhedron is empty,
  <CODE>false</CODE> otherwise.

  \param con_to_gen
  <CODE>true</CODE> if \p source1 and \p source2 are system of
  constraints, <CODE>false</CODE> otherwise;

  \param source1
  The first element of the given DD pair;

  \param dest
  The second element of the given DD pair;

  \param sat
  The saturation matrix that bind \p source1 to \p dest;

  \param source2
  The new system of generators or constraints.

  It is assumed that \p source1 and \p source2 are sorted and have
  no pending rows. It is also assumed that \p dest has no pending rows.
  On entry, the rows of \p sat are indexed by the rows of \p dest
  and its columns are indexed by the rows of \p source1.
  On exit, the rows of \p sat are indexed by the rows of \p dest
  and its columns are indexed by the rows of the system obtained
  by merging \p source1 and \p source2.

  Let us suppose we want to add some constraints to a given system of
  constraints \p source1. This method, given a minimized double description
  pair (\p source1, \p dest) and a system of new constraints \p source2,
  modifies \p source1 by adding to it the constraints of \p source2 that
  are not in \p source1. Then, by invoking
  <CODE>add_and_minimize(bool, Linear_System_Class&, Linear_System_Class&, Bit_Matrix&)</CODE>,
  processes the added constraints obtaining a new DD pair.

  This method treats also the dual case, i.e., adding new generators to
  a previous system of generators. In this case \p source1 contains the
  old generators, \p source2 the new ones and \p dest is the system
  of constraints in the given minimized DD pair.

  Since \p source2 contains the constraints (or the generators) that
  will be added to \p source1, it is constant: it will not be modified.
*/
template <typename Source_Linear_System1, typename Source_Linear_System2,
          typename Dest_Linear_System>
bool
Polyhedron::add_and_minimize(const bool con_to_gen,
                             Source_Linear_System1& source1,
                             Dest_Linear_System& dest,
                             Bit_Matrix& sat,
                             const Source_Linear_System2& source2) {
  // `source1' and `source2' cannot be empty.
  PPL_ASSERT(!source1.has_no_rows() && !source2.has_no_rows());
  // `source1' and `source2' must have the same number of columns
  // to be merged.
  PPL_ASSERT(source1.num_columns() == source2.num_columns());
  // `source1' and `source2' are fully sorted.
  PPL_ASSERT(source1.is_sorted() && source1.num_pending_rows() == 0);
  PPL_ASSERT(source2.is_sorted() && source2.num_pending_rows() == 0);
  PPL_ASSERT(dest.num_pending_rows() == 0);

  const dimension_type old_source1_num_rows = source1.num_rows();
  // `k1' and `k2' run through the rows of `source1' and `source2', resp.
  dimension_type k1 = 0;
  dimension_type k2 = 0;
  dimension_type source2_num_rows = source2.num_rows();
  while (k1 < old_source1_num_rows && k2 < source2_num_rows) {
    // Add to `source1' the constraints from `source2', as pending rows.
    // We exploit the property that initially both `source1' and `source2'
    // are sorted and index `k1' only scans the non-pending rows of `source1',
    // so that it is not influenced by the pending rows appended to it.
    // This way no duplicate (i.e., trivially redundant) constraint
    // is introduced in `source1'.
    const int cmp = compare(source1[k1], source2[k2]);
    if (cmp == 0) {
      // We found the same row: there is no need to add `source2[k2]'.
      ++k2;
      // By sortedness, since `k1 < old_source1_num_rows',
      // we can increment index `k1' too.
      ++k1;
    }
    else if (cmp < 0) {
      // By sortedness, we can increment `k1'.
      ++k1;
    }
    else {
      // Here `cmp > 0'.
      // By sortedness, `source2[k2]' cannot be in `source1'.
      // We add it as a pending row of `source1' (sortedness unaffected).
      source1.add_pending_row(source2[k2]);
      // We can increment `k2'.
      ++k2;
    }
  }
  // Have we scanned all the rows in `source2'?
  if (k2 < source2_num_rows) {
    // By sortedness, all the rows in `source2' having indexes
    // greater than or equal to `k2' were not in `source1'.
    // We add them as pending rows of 'source1' (sortedness not affected).
    for ( ; k2 < source2_num_rows; ++k2) {
      source1.add_pending_row(source2[k2]);
    }
  }

  if (source1.num_pending_rows() == 0) {
    // No row was appended to `source1', because all the constraints
    // in `source2' were already in `source1'.
    // There is nothing left to do ...
    return false;
  }
  return add_and_minimize(con_to_gen, source1, dest, sat);
}

/*!
  \return
  <CODE>true</CODE> if the obtained polyhedron is empty,
  <CODE>false</CODE> otherwise.

  \param con_to_gen
  <CODE>true</CODE> if \p source is a system of constraints,
  <CODE>false</CODE> otherwise;

  \param source
  The first element of the given DD pair. It also contains the pending
  rows to be processed;

  \param dest
  The second element of the given DD pair. It cannot have pending rows;

  \param sat
  The saturation matrix that bind the upper part of \p source to \p dest.

  On entry, the rows of \p sat are indexed by the rows of \p dest
  and its columns are indexed by the non-pending rows of \p source.
  On exit, the rows of \p sat are indexed by the rows of \p dest
  and its columns are indexed by the rows of \p source.

  Let us suppose that \p source is a system of constraints.
  This method assumes that the non-pending part of \p source and
  system \p dest form a double description pair in minimal form and
  will build a new DD pair in minimal form by processing the pending
  constraints in \p source. To this end, it will call
  <CODE>conversion()</CODE>) and <CODE>simplify</CODE>.

  This method treats also the dual case, i.e., processing pending
  generators. In this case \p source contains generators and \p dest
  is the system of constraints corresponding to the non-pending part
  of \p source.
*/
template <typename Source_Linear_System, typename Dest_Linear_System>
bool
Polyhedron::add_and_minimize(const bool con_to_gen,
                             Source_Linear_System& source,
                             Dest_Linear_System& dest,
                             Bit_Matrix& sat) {
  PPL_ASSERT(source.num_pending_rows() > 0);
  PPL_ASSERT(source.space_dimension() == dest.space_dimension());
  PPL_ASSERT(source.is_sorted());

  // First, pad the saturation matrix with new columns (of zeroes)
  // to accommodate for the pending rows of `source'.
  sat.resize(dest.num_rows(), source.num_rows());

  // Incrementally compute the new system of generators.
  // Parameter `start' is set to the index of the first pending constraint.
  const dimension_type num_lines_or_equalities
    = conversion(source, source.first_pending_row(),
                 dest, sat,
                 dest.num_lines_or_equalities());

  // conversion() may have modified the number of rows in `dest'.
  const dimension_type dest_num_rows = dest.num_rows();

  // Checking if the generators in `dest' represent an empty polyhedron:
  // the polyhedron is empty if there are no points
  // (because rays, lines and closure points need a supporting point).
  // Points can be detected by looking at:
  // - the divisor, for necessarily closed polyhedra;
  // - the epsilon coordinate, for NNC polyhedra.
  dimension_type first_point;
  if (dest.is_necessarily_closed()) {
    for (first_point = num_lines_or_equalities;
        first_point < dest_num_rows;
        ++first_point) {
      if (dest[first_point].expr.inhomogeneous_term() > 0) {
        break;
      }
    }
  }
  else {
    for (first_point = num_lines_or_equalities;
        first_point < dest_num_rows;
        ++first_point) {
      if (dest[first_point].expr.get(Variable(dest.space_dimension())) > 0) {
        break;
      }
    }
  }

  if (first_point == dest_num_rows) {
    if (con_to_gen) {
      // No point has been found: the polyhedron is empty.
      return true;
    }
    else {
      // Here `con_to_gen' is false: `dest' is a system of constraints.
      // In this case the condition `first_point == dest_num_rows'
      // actually means that all the constraints in `dest' have their
      // inhomogeneous term equal to 0.
      // This is an ILLEGAL situation, because it implies that
      // the constraint system `dest' lacks the positivity constraint
      // and no linear combination of the constraints in `dest'
      // can reintroduce the positivity constraint.
      PPL_UNREACHABLE;
      return false;
    }
  }
  else {
    // A point has been found: the polyhedron is not empty.
    // Now invoking `simplify()' to remove all the redundant constraints
    // from the system `source'.
    // Since the saturation matrix `sat' returned by `conversion()'
    // has rows indexed by generators (the rows of `dest') and columns
    // indexed by constraints (the rows of `source'), we have to
    // transpose it to obtain the saturation matrix needed by `simplify()'.
    sat.transpose();
    simplify(source, sat);
    // Transposing back.
    sat.transpose();
    return false;
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Polyhedron_simplify_templates.hh line 1. */
/* Polyhedron class implementation: simplify().
*/


/* Automatically generated from PPL source file ../src/Polyhedron_simplify_templates.hh line 29. */
#include <cstddef>
#include <limits>

namespace Parma_Polyhedra_Library {

/*!
  \return
  The rank of \p sys.

  \param sys
  The system to simplify: it will be modified;

  \param sat
  The saturation matrix corresponding to \p sys.

  \p sys may be modified by swapping some of its rows and by possibly
  removing some of them, if they turn out to be redundant.

  If \p sys is a system of constraints, then the rows of \p sat are
  indexed by constraints and its columns are indexed by generators;
  otherwise, if \p sys is a system of generators, then the rows of
  \p sat are indexed by generators and its columns by constraints.

  Given a system of constraints or a system of generators, this function
  simplifies it using Gauss' elimination method (to remove redundant
  equalities/lines), deleting redundant inequalities/rays/points and
  making back-substitution.
  The explanation that follows assumes that \p sys is a system of
  constraints. For the case when \p sys is a system of generators,
  a similar explanation can be obtain by applying duality.

  The explanation relies on the notion of <EM>redundancy</EM>.
  (See the Introduction.)

  First we make some observations that can help the reader
  in understanding the function:

  Proposition: An inequality that is saturated by all the generators
  can be transformed to an equality.

  In fact, by combining any number of generators that saturate the
  constraints, we obtain a generator that saturates the constraints too:
  \f[
    \langle \vect{c}, \vect{r}_1 \rangle = 0 \land
    \langle \vect{c}, \vect{r}_2 \rangle = 0
    \Rightarrow
    \langle \vect{c}, (\lambda_1 \vect{r}_1 + \lambda_2 \vect{r}_2) \rangle =
    \lambda_1 \langle \vect{c}, \vect{r}_1 \rangle
    + \lambda_2 \langle \vect{c}, \vect{r}_2 \rangle
    = 0,
  \f]
  where \f$\lambda_1, \lambda_2\f$ can be any real number.
*/
template <typename Linear_System1>
dimension_type
Polyhedron::simplify(Linear_System1& sys, Bit_Matrix& sat) {
  dimension_type num_rows = sys.num_rows();
  const dimension_type num_cols_sat = sat.num_columns();

  using std::swap;

  // Looking for the first inequality in `sys'.
  dimension_type num_lines_or_equalities = 0;
  while (num_lines_or_equalities < num_rows
         && sys[num_lines_or_equalities].is_line_or_equality()) {
    ++num_lines_or_equalities;
  }

  // `num_saturators[i]' will contain the number of generators
  // that saturate the constraint `sys[i]'.
  if (num_rows > simplify_num_saturators_size) {
    delete [] simplify_num_saturators_p;
    simplify_num_saturators_p = 0;
    simplify_num_saturators_size = 0;
    const size_t max_size
      = std::numeric_limits<size_t>::max() / sizeof(dimension_type);
    const size_t new_size = compute_capacity(num_rows, max_size);
    simplify_num_saturators_p = new dimension_type[new_size];
    simplify_num_saturators_size = new_size;
  }
  dimension_type* const num_saturators = simplify_num_saturators_p;

  bool sys_sorted = sys.is_sorted();

  // Computing the number of saturators for each inequality,
  // possibly identifying and swapping those that happen to be
  // equalities (see Proposition above).
  for (dimension_type i = num_lines_or_equalities; i < num_rows; ++i) {
    if (sat[i].empty()) {
      // The constraint `sys_rows[i]' is saturated by all the generators.
      // Thus, either it is already an equality or it can be transformed
      // to an equality (see Proposition above).
      sys.sys.rows[i].set_is_line_or_equality();
      // Note: simple normalization already holds.
      sys.sys.rows[i].sign_normalize();
      // We also move it just after all the other equalities,
      // so that system `sys_rows' keeps its partial sortedness.
      if (i != num_lines_or_equalities) {
        sys.sys.rows[i].m_swap(sys.sys.rows[num_lines_or_equalities]);
        swap(sat[i], sat[num_lines_or_equalities]);
        swap(num_saturators[i], num_saturators[num_lines_or_equalities]);
      }
      ++num_lines_or_equalities;
      // `sys' is no longer sorted.
      sys_sorted = false;
    }
    else {
      // There exists a generator which does not saturate `sys[i]',
      // so that `sys[i]' is indeed an inequality.
      // We store the number of its saturators.
      num_saturators[i] = num_cols_sat - sat[i].count_ones();
    }
  }

  sys.set_sorted(sys_sorted);
  PPL_ASSERT(sys.OK());

  // At this point, all the equalities of `sys' (included those
  // inequalities that we just transformed to equalities) have
  // indexes between 0 and `num_lines_or_equalities' - 1,
  // which is the property needed by method gauss().
  // We can simplify the system of equalities, obtaining the rank
  // of `sys' as result.
  const dimension_type rank = sys.gauss(num_lines_or_equalities);

  // Now the irredundant equalities of `sys' have indexes from 0
  // to `rank' - 1, whereas the equalities having indexes from `rank'
  // to `num_lines_or_equalities' - 1 are all redundant.
  // (The inequalities in `sys' have been left untouched.)
  // The rows containing equalities are not sorted.

  if (rank < num_lines_or_equalities) {
    // We identified some redundant equalities.
    // Moving them at the bottom of `sys':
    // - index `redundant' runs through the redundant equalities
    // - index `erasing' identifies the first row that should
    //   be erased after this loop.
    // Note that we exit the loop either because we have removed all
    // redundant equalities or because we have moved all the
    // inequalities.
    for (dimension_type redundant = rank,
           erasing = num_rows;
         redundant < num_lines_or_equalities
           && erasing > num_lines_or_equalities;
         ) {
      --erasing;
      sys.remove_row(redundant);
      swap(sat[redundant], sat[erasing]);
      swap(num_saturators[redundant], num_saturators[erasing]);
      ++redundant;
    }
    // Adjusting the value of `num_rows' to the number of meaningful
    // rows of `sys': `num_lines_or_equalities' - `rank' is the number of
    // redundant equalities moved to the bottom of `sys', which are
    // no longer meaningful.
    num_rows -= num_lines_or_equalities - rank;

    // If the above loop exited because it moved all inequalities, it may not
    // have removed all the rendundant rows.
    sys.remove_trailing_rows(sys.num_rows() - num_rows);

    PPL_ASSERT(sys.num_rows() == num_rows);

    sat.remove_trailing_rows(num_lines_or_equalities - rank);

    // Adjusting the value of `num_lines_or_equalities'.
    num_lines_or_equalities = rank;
  }

  const dimension_type old_num_rows = sys.num_rows();

  // Now we use the definition of redundancy (given in the Introduction)
  // to remove redundant inequalities.

  // First we check the saturation rule, which provides a necessary
  // condition for an inequality to be irredundant (i.e., it provides
  // a sufficient condition for identifying redundant inequalities).
  // Let
  //
  //   num_saturators[i] = num_sat_lines[i] + num_sat_rays_or_points[i],
  //   dim_lin_space = num_irredundant_lines,
  //   dim_ray_space
  //     = dim_vector_space - num_irredundant_equalities - dim_lin_space
  //     = num_columns - 1 - num_lines_or_equalities - dim_lin_space,
  //   min_sat_rays_or_points = dim_ray_space.
  //
  // An inequality saturated by less than `dim_ray_space' _rays/points_
  // is redundant. Thus we have the implication
  //
  //   (num_saturators[i] - num_sat_lines[i] < dim_ray_space)
  //      ==>
  //        redundant(sys[i]).
  //
  // Moreover, since every line saturates all inequalities, we also have
  //     dim_lin_space = num_sat_lines[i]
  // so that we can rewrite the condition above as follows:
  //
  //   (num_saturators[i] < num_columns - num_lines_or_equalities - 1)
  //      ==>
  //        redundant(sys[i]).
  //
  const dimension_type sys_num_columns
    = sys.topology() == NECESSARILY_CLOSED ? sys.space_dimension() + 1
                                           : sys.space_dimension() + 2;
  const dimension_type min_saturators
    = sys_num_columns - num_lines_or_equalities - 1;
  for (dimension_type i = num_lines_or_equalities; i < num_rows; ) {
    if (num_saturators[i] < min_saturators) {
      // The inequality `sys[i]' is redundant.
      --num_rows;
      sys.remove_row(i);
      swap(sat[i], sat[num_rows]);
      swap(num_saturators[i], num_saturators[num_rows]);
    }
    else {
      ++i;
    }
  }

  // Now we check the independence rule.
  for (dimension_type i = num_lines_or_equalities; i < num_rows; ) {
    bool redundant = false;
    // NOTE: in the inner loop, index `j' runs through _all_ the
    // inequalities and we do not test if `sat[i]' is strictly
    // contained into `sat[j]'.  Experimentation has shown that this
    // is faster than having `j' only run through the indexes greater
    // than `i' and also doing the test `strict_subset(sat[i],
    // sat[k])'.
    for (dimension_type j = num_lines_or_equalities; j < num_rows; ) {
      if (i == j) {
        // We want to compare different rows of `sys'.
        ++j;
      }
      else {
        // Let us recall that each generator lies on a facet of the
        // polyhedron (see the Introduction).
        // Given two constraints `c_1' and `c_2', if there are `m'
        // generators lying on the hyper-plane corresponding to `c_1',
        // the same `m' generators lie on the hyper-plane
        // corresponding to `c_2', too, and there is another one lying
        // on the latter but not on the former, then `c_2' is more
        // restrictive than `c_1', i.e., `c_1' is redundant.
        bool strict_subset;
        if (subset_or_equal(sat[j], sat[i], strict_subset)) {
          if (strict_subset) {
            // All the saturators of the inequality `sys[i]' are
            // saturators of the inequality `sys[j]' too,
            // and there exists at least one saturator of `sys[j]'
            // which is not a saturator of `sys[i]'.
            // It follows that inequality `sys[i]' is redundant.
            redundant = true;
            break;
          }
          else {
            // We have `sat[j] == sat[i]'.  Hence inequalities
            // `sys[i]' and `sys[j]' are saturated by the same set of
            // generators. Then we can remove either one of the two
            // inequalities: we remove `sys[j]'.
            --num_rows;
            sys.remove_row(j);
            PPL_ASSERT(sys.num_rows() == num_rows);
            swap(sat[j], sat[num_rows]);
            swap(num_saturators[j], num_saturators[num_rows]);
          }
        }
        else {
          // If we reach this point then we know that `sat[i]' does
          // not contain (and is different from) `sat[j]', so that
          // `sys[i]' is not made redundant by inequality `sys[j]'.
          ++j;
        }
      }
    }
    if (redundant) {
      // The inequality `sys[i]' is redundant.
      --num_rows;
      sys.remove_row(i);
      PPL_ASSERT(sys.num_rows() == num_rows);
      swap(sat[i], sat[num_rows]);
      swap(num_saturators[i], num_saturators[num_rows]);
    }
    else {
      // The inequality `sys[i]' is not redundant.
      ++i;
    }
  }

  // Here we physically remove the `sat' rows corresponding to the redundant
  // inequalities previously removed from `sys'.
  sat.remove_trailing_rows(old_num_rows - num_rows);

  // At this point the first `num_lines_or_equalities' rows of 'sys'
  // represent the irredundant equalities, while the remaining rows
  // (i.e., those having indexes from `num_lines_or_equalities' to
  // `num_rows' - 1) represent the irredundant inequalities.
#ifndef NDEBUG
  // Check if the flag is set (that of the equalities is already set).
  for (dimension_type i = num_lines_or_equalities; i < num_rows; ++i) {
    PPL_ASSERT(sys[i].is_ray_or_point_or_inequality());
  }
#endif

  // Finally, since now the sub-system (of `sys') of the irredundant
  // equalities is in triangular form, we back substitute each
  // variables with the expression obtained considering the equalities
  // starting from the last one.
  sys.back_substitute(num_lines_or_equalities);

  // The returned value is the number of irredundant equalities i.e.,
  // the rank of the sub-system of `sys' containing only equalities.
  // (See the Introduction for definition of lineality space dimension.)
  return num_lines_or_equalities;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Polyhedron_defs.hh line 2862. */

/* Automatically generated from PPL source file ../src/Grid_defs.hh line 1. */
/* Grid class declaration.
*/


/* Automatically generated from PPL source file ../src/Grid_Generator_System_defs.hh line 1. */
/* Grid_Generator_System class declaration.
*/


/* Automatically generated from PPL source file ../src/Grid_Generator_System_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/Grid_Generator_System_defs.hh line 33. */
#include <iosfwd>
#include <cstddef>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*!
  \relates Parma_Polyhedra_Library::Grid_Generator_System
  Writes <CODE>false</CODE> if \p gs is empty.  Otherwise, writes on
  \p s the generators of \p gs, all in one row and separated by ", ".
*/
std::ostream& operator<<(std::ostream& s, const Grid_Generator_System& gs);

} // namespace IO_Operators

//! Swaps \p x with \p y.
/*! \relates Grid_Generator_System */
void swap(Grid_Generator_System& x, Grid_Generator_System& y);

//! Returns <CODE>true</CODE> if and only if \p x and \p y are identical.
/*! \relates Grid_Generator_System */
bool operator==(const Grid_Generator_System& x,
                const Grid_Generator_System& y);

} // namespace Parma_Polyhedra_Library

//! A system of grid generators.
/*! \ingroup PPL_CXX_interface
    An object of the class Grid_Generator_System is a system of
    grid generators, i.e., a multiset of objects of the class
    Grid_Generator (lines, parameters and points).
    When inserting generators in a system, space dimensions are
    automatically adjusted so that all the generators in the system
    are defined on the same vector space.
    A system of grid generators which is meant to define a non-empty
    grid must include at least one point: the reason is that
    lines and parameters need a supporting point
    (lines only specify directions while parameters only
    specify direction and distance.

    \par
     In all the examples it is assumed that variables
    <CODE>x</CODE> and <CODE>y</CODE> are defined as follows:
    \code
  Variable x(0);
  Variable y(1);
    \endcode

    \par Example 1
    The following code defines the line having the same direction
    as the \f$x\f$ axis (i.e., the first Cartesian axis)
    in \f$\Rset^2\f$:
    \code
  Grid_Generator_System gs;
  gs.insert(grid_line(x + 0*y));
    \endcode
    As said above, this system of generators corresponds to
    an empty grid, because the line has no supporting point.
    To define a system of generators that does correspond to
    the \f$x\f$ axis, we can add the following code which
    inserts the origin of the space as a point:
    \code
  gs.insert(grid_point(0*x + 0*y));
    \endcode
    Since space dimensions are automatically adjusted, the following
    code obtains the same effect:
    \code
  gs.insert(grid_point(0*x));
    \endcode
    In contrast, if we had added the following code, we would have
    defined a line parallel to the \f$x\f$ axis through
    the point \f$(0, 1)^\transpose \in \Rset^2\f$.
    \code
  gs.insert(grid_point(0*x + 1*y));
    \endcode

    \par Example 2
    The following code builds a system of generators corresponding
    to the grid consisting of all the integral points on the \f$x\f$ axes;
    that is, all points satisfying the congruence relation
    \f[
      \bigl\{\,
        (x, 0)^\transpose \in \Rset^2
      \bigm|
        x \pmod{1}\ 0
      \,\bigr\},
    \f]
    \code
  Grid_Generator_System gs;
  gs.insert(parameter(x + 0*y));
  gs.insert(grid_point(0*x + 0*y));
    \endcode

    \par Example 3
    The following code builds a system of generators having three points
    corresponding to a non-relational grid consisting of all points
    whose coordinates are integer multiple of 3.
    \code
  Grid_Generator_System gs;
  gs.insert(grid_point(0*x + 0*y));
  gs.insert(grid_point(0*x + 3*y));
  gs.insert(grid_point(3*x + 0*y));
    \endcode

    \par Example 4
    By using parameters instead of two of the points we
    can define the same grid as that defined in the previous example.
    Note that there has to be at least one point and, for this purpose,
    any point in the grid could be considered.
    Thus the following code builds two identical grids from the
    grid generator systems \p gs and \p gs1.
    \code
  Grid_Generator_System gs;
  gs.insert(grid_point(0*x + 0*y));
  gs.insert(parameter(0*x + 3*y));
  gs.insert(parameter(3*x + 0*y));
  Grid_Generator_System gs1;
  gs1.insert(grid_point(3*x + 3*y));
  gs1.insert(parameter(0*x + 3*y));
  gs1.insert(parameter(3*x + 0*y));
    \endcode

    \par Example 5
    The following code builds a system of generators having one point and
    a parameter corresponding to all the integral points that
    lie on \f$x + y = 2\f$ in \f$\Rset^2\f$
    \code
  Grid_Generator_System gs;
  gs.insert(grid_point(1*x + 1*y));
  gs.insert(parameter(1*x - 1*y));
    \endcode

    \note
    After inserting a multiset of generators in a grid generator system,
    there are no guarantees that an <EM>exact</EM> copy of them
    can be retrieved:
    in general, only an <EM>equivalent</EM> grid generator system
    will be available, where original generators may have been
    reordered, removed (if they are duplicate or redundant), etc.
*/
class Parma_Polyhedra_Library::Grid_Generator_System {
public:
  typedef Grid_Generator row_type;

  static const Representation default_representation = SPARSE;

  //! Default constructor: builds an empty system of generators.
  explicit Grid_Generator_System(Representation r = default_representation);

  //! Builds the singleton system containing only generator \p g.
  explicit Grid_Generator_System(const Grid_Generator& g,
                                 Representation r = default_representation);

  //! Builds an empty system of generators of dimension \p dim.
  explicit Grid_Generator_System(dimension_type dim,
                                 Representation r = default_representation);

  //! Ordinary copy constructor.
  //! The new Grid_Generator_System will have the same representation as `gs'.
  Grid_Generator_System(const Grid_Generator_System& gs);

  //! Copy constructor with specified representation.
  Grid_Generator_System(const Grid_Generator_System& gs, Representation r);

  //! Destructor.
  ~Grid_Generator_System();

  //! Assignment operator.
  Grid_Generator_System& operator=(const Grid_Generator_System& y);

  //! Returns the current representation of *this.
  Representation representation() const;

  //! Converts *this to the specified representation.
  void set_representation(Representation r);

  //! Returns the maximum space dimension a Grid_Generator_System can handle.
  static dimension_type max_space_dimension();

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  /*! \brief
    Removes all the generators from the generator system and sets its
    space dimension to 0.
  */
  void clear();

  /*! \brief
    Inserts into \p *this a copy of the generator \p g, increasing the
    number of space dimensions if needed.

    If \p g is an all-zero parameter then the only action is to ensure
    that the space dimension of \p *this is at least the space
    dimension of \p g.
  */
  void insert(const Grid_Generator& g);

  /*! \brief
    Inserts into \p *this the generator \p g, increasing the number of
    space dimensions if needed.
  */
  void insert(Grid_Generator& g, Recycle_Input);

  /*! \brief
    Inserts into \p *this the generators in \p gs, increasing the
    number of space dimensions if needed.
  */
  void insert(Grid_Generator_System& gs, Recycle_Input);

  //! Initializes the class.
  static void initialize();

  //! Finalizes the class.
  static void finalize();

  /*! \brief
    Returns the singleton system containing only
    Grid_Generator::zero_dim_point().
  */
  static const Grid_Generator_System& zero_dim_univ();

  //! An iterator over a system of grid generators
  /*! \ingroup PPL_CXX_interface
    A const_iterator is used to provide read-only access
    to each generator contained in an object of Grid_Generator_System.

    \par Example
    The following code prints the system of generators
    of the grid <CODE>gr</CODE>:
    \code
  const Grid_Generator_System& ggs = gr.generators();
  for (Grid_Generator_System::const_iterator i = ggs.begin(),
        ggs_end = ggs.end(); i != ggs_end; ++i)
    cout << *i << endl;
    \endcode
    The same effect can be obtained more concisely by using
    more features of the STL:
    \code
  const Grid_Generator_System& ggs = gr.generators();
  copy(ggs.begin(), ggs.end(), ostream_iterator<Grid_Generator>(cout, "\n"));
    \endcode
  */
  class const_iterator
    : public std::iterator<std::forward_iterator_tag,
                           Grid_Generator,
                           std::ptrdiff_t,
                           const Grid_Generator*,
                           const Grid_Generator&> {
  public:
    //! Default constructor.
    const_iterator();

    //! Ordinary copy constructor.
    const_iterator(const const_iterator& y);

    //! Destructor.
    ~const_iterator();

    //! Assignment operator.
    const_iterator& operator=(const const_iterator& y);

    //! Dereference operator.
    const Grid_Generator& operator*() const;

    //! Indirect member selector.
    const Grid_Generator* operator->() const;

    //! Prefix increment operator.
    const_iterator& operator++();

    //! Postfix increment operator.
    const_iterator operator++(int);

    /*! \brief
      Returns <CODE>true</CODE> if and only if \p *this and \p y are
      identical.
    */
    bool operator==(const const_iterator& y) const;

    /*! \brief
      Returns <CODE>true</CODE> if and only if \p *this and \p y are
      different.
    */
    bool operator!=(const const_iterator& y) const;

  private:
    friend class Grid_Generator_System;

    Linear_System<Grid_Generator>::const_iterator i;

    //! Copy constructor from Linear_System< Grid_Generator>::const_iterator.
    const_iterator(const Linear_System<Grid_Generator>::const_iterator& y);
  };

  //! Returns <CODE>true</CODE> if and only if \p *this has no generators.
  bool empty() const;

  /*! \brief
    Returns the const_iterator pointing to the first generator, if \p
    *this is not empty; otherwise, returns the past-the-end
    const_iterator.
  */
  const_iterator begin() const;

  //! Returns the past-the-end const_iterator.
  const_iterator end() const;

  //! Returns the number of rows (generators) in the system.
  dimension_type num_rows() const;

  //! Returns the number of parameters in the system.
  dimension_type num_parameters() const;

  //! Returns the number of lines in the system.
  dimension_type num_lines() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this contains one or
    more points.
  */
  bool has_points() const;

  //! Returns <CODE>true</CODE> if \p *this is identical to \p y.
  bool is_equal_to(const Grid_Generator_System& y) const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.

    Resizes the matrix of generators using the numbers of rows and columns
    read from \p s, then initializes the coordinates of each generator
    and its type reading the contents from \p s.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Swaps \p *this with \p y.
  void m_swap(Grid_Generator_System& y);

private:
  //! Returns a constant reference to the \p k- th generator of the system.
  const Grid_Generator& operator[](dimension_type k) const;

  //! Assigns to a given variable an affine expression.
  /*!
    \param v
    The variable to which the affine transformation is assigned;

    \param expr
    The numerator of the affine transformation:
    \f$\sum_{i = 0}^{n - 1} a_i x_i + b\f$;

    \param denominator
    The denominator of the affine transformation;

    We allow affine transformations (see the Section \ref
    rational_grid_operations)to have rational
    coefficients. Since the coefficients of linear expressions are
    integers we also provide an integer \p denominator that will
    be used as denominator of the affine transformation.  The
    denominator is required to be a positive integer and its
    default value is 1.

    The affine transformation assigns to every variable \p v, in every
    column, the follow expression:
    \f[
      \frac{\sum_{i = 0}^{n - 1} a_i x_i + b}
           {\mathrm{denominator}}.
    \f]

    \p expr is a constant parameter and unaltered by this computation.
  */
  void affine_image(Variable v,
                    const Linear_Expression& expr,
                    Coefficient_traits::const_reference denominator);

  //! Sets the sortedness flag of the system to \p b.
  void set_sorted(bool b);

  /*! \brief
    Adds \p dims rows and \p dims columns of zeroes to the matrix,
    initializing the added rows as in the universe system.

    \param dims
    The number of rows and columns to be added: must be strictly
    positive.

    Turns the \f$r \times c\f$ matrix \f$A\f$ into the \f$(r+dims)
    \times (c+dims)\f$ matrix
    \f$\bigl(\genfrac{}{}{0pt}{}{A}{0} \genfrac{}{}{0pt}{}{0}{B}\bigr)\f$
    where \f$B\f$ is the \f$dims \times dims\f$ unit matrix of the form
    \f$\bigl(\genfrac{}{}{0pt}{}{1}{0} \genfrac{}{}{0pt}{}{0}{1}\bigr)\f$.
    The matrix is expanded avoiding reallocation whenever possible.
  */
  void add_universe_rows_and_columns(dimension_type dims);

  //! Resizes the system to the specified space dimension.
  void set_space_dimension(dimension_type space_dim);

  //! Removes all the specified dimensions from the generator system.
  /*!
    The space dimension of the variable with the highest space
    dimension in \p vars must be at most the space dimension
    of \p this.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  //! Shift by \p n positions the coefficients of variables, starting from
  //! the coefficient of \p v. This increases the space dimension by \p n.
  void shift_space_dimensions(Variable v, dimension_type n);

  //! Sets the index to indicate that the system has no pending rows.
  void unset_pending_rows();

  //! Permutes the space dimensions of the matrix.
  /*
    \param cycle
    A vector representing a cycle of the permutation according to which the
    columns must be rearranged.

    The \p cycle vector represents a cycle of a permutation of space
    dimensions.
    For example, the permutation
    \f$ \{ x_1 \mapsto x_2, x_2 \mapsto x_3, x_3 \mapsto x_1 \}\f$ can be
    represented by the vector containing \f$ x_1, x_2, x_3 \f$.
  */
  void permute_space_dimensions(const std::vector<Variable>& cycle);

  bool has_no_rows() const;

  //! Makes the system shrink by removing its \p n trailing rows.
  void remove_trailing_rows(dimension_type n);

  void insert_verbatim(const Grid_Generator& g);

  //! Returns the system topology.
  Topology topology() const;

  //! Returns the index of the first pending row.
  dimension_type first_pending_row() const;

  Linear_System<Grid_Generator> sys;

  /*! \brief
    Holds (between class initialization and finalization) a pointer to
    the singleton system containing only Grid_Generator::zero_dim_point().
  */
  static const Grid_Generator_System* zero_dim_univ_p;

  friend bool
  operator==(const Grid_Generator_System& x, const Grid_Generator_System& y);

  //! Sets the index of the first pending row to \p i.
  void set_index_first_pending_row(dimension_type i);

  //! Removes all the invalid lines and parameters.
  /*!
    The invalid lines and parameters are those with all
    the homogeneous terms set to zero.
  */
  void remove_invalid_lines_and_parameters();

  friend class Polyhedron;
  friend class Grid;
};

// Grid_Generator_System_inlines.hh is not included here on purpose.

/* Automatically generated from PPL source file ../src/Grid_Generator_System_inlines.hh line 1. */
/* Grid_Generator_System class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Grid_Generator_System_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

inline void
Grid_Generator_System::set_sorted(bool b) {
  sys.set_sorted(b);
}

inline void
Grid_Generator_System::unset_pending_rows() {
  sys.unset_pending_rows();
}

inline void
Grid_Generator_System::set_index_first_pending_row(const dimension_type i) {
  sys.set_index_first_pending_row(i);
}

inline void
Grid_Generator_System
::permute_space_dimensions(const std::vector<Variable>& cycle) {
  return sys.permute_space_dimensions(cycle);
}

inline bool
Grid_Generator_System::is_equal_to(const Grid_Generator_System& y) const {
  return (sys == y.sys);
}

inline
Grid_Generator_System::Grid_Generator_System(Representation r)
  : sys(NECESSARILY_CLOSED, r) {
  sys.set_sorted(false);
  PPL_ASSERT(space_dimension() == 0);
}

inline
Grid_Generator_System::Grid_Generator_System(const Grid_Generator_System& gs)
  : sys(gs.sys) {
}

inline
Grid_Generator_System::Grid_Generator_System(const Grid_Generator_System& gs,
                                             Representation r)
  : sys(gs.sys, r) {
}

inline
Grid_Generator_System::Grid_Generator_System(dimension_type dim,
                                             Representation r)
  : sys(NECESSARILY_CLOSED, r) {
  sys.set_space_dimension(dim);
  sys.set_sorted(false);
  PPL_ASSERT(space_dimension() == dim);
}

inline
Grid_Generator_System::Grid_Generator_System(const Grid_Generator& g,
                                             Representation r)
  : sys(NECESSARILY_CLOSED, r) {
  sys.insert(g);
  sys.set_sorted(false);
}

inline
Grid_Generator_System::~Grid_Generator_System() {
}

inline Grid_Generator_System&
Grid_Generator_System::operator=(const Grid_Generator_System& y) {
  Grid_Generator_System tmp = y;
  swap(*this, tmp);
  return *this;
}

inline Representation
Grid_Generator_System::representation() const {
  return sys.representation();
}

inline void
Grid_Generator_System::set_representation(Representation r) {
  sys.set_representation(r);
}

inline dimension_type
Grid_Generator_System::max_space_dimension() {
  // Grid generators use an extra column for the parameter divisor.
  return Linear_System<Grid_Generator>::max_space_dimension() - 1;
}

inline dimension_type
Grid_Generator_System::space_dimension() const {
  return sys.space_dimension();
}

inline const Grid_Generator_System&
Grid_Generator_System::zero_dim_univ() {
  PPL_ASSERT(zero_dim_univ_p != 0);
  return *zero_dim_univ_p;
}

inline void
Grid_Generator_System::clear() {
  sys.clear();
  sys.set_sorted(false);
  sys.unset_pending_rows();
  PPL_ASSERT(space_dimension() == 0);
}

inline void
Grid_Generator_System::m_swap(Grid_Generator_System& y) {
  swap(sys, y.sys);
}

inline memory_size_type
Grid_Generator_System::external_memory_in_bytes() const {
  return sys.external_memory_in_bytes();
}

inline memory_size_type
Grid_Generator_System::total_memory_in_bytes() const {
  return external_memory_in_bytes() + sizeof(*this);
}

inline dimension_type
Grid_Generator_System::num_rows() const {
  return sys.num_rows();
}

inline
Grid_Generator_System::const_iterator::const_iterator()
  : i() {
}

inline
Grid_Generator_System::const_iterator::const_iterator(const const_iterator& y)
  : i(y.i) {
}

inline
Grid_Generator_System::const_iterator::~const_iterator() {
}

inline Grid_Generator_System::const_iterator&
Grid_Generator_System::const_iterator::operator=(const const_iterator& y) {
  i = y.i;
  return *this;
}

inline const Grid_Generator&
Grid_Generator_System::const_iterator::operator*() const {
  return *i;
}

inline const Grid_Generator*
Grid_Generator_System::const_iterator::operator->() const {
  return i.operator->();
}

inline Grid_Generator_System::const_iterator&
Grid_Generator_System::const_iterator::operator++() {
  ++i;
  return *this;
}

inline Grid_Generator_System::const_iterator
Grid_Generator_System::const_iterator::operator++(int) {
  const const_iterator tmp = *this;
  operator++();
  return tmp;
}

inline bool
Grid_Generator_System
::const_iterator::operator==(const const_iterator& y) const {
  return i == y.i;
}

inline bool
Grid_Generator_System
::const_iterator::operator!=(const const_iterator& y) const {
  return i != y.i;
}

inline bool
Grid_Generator_System::empty() const {
  return sys.has_no_rows();
}

inline
Grid_Generator_System::const_iterator
::const_iterator(const Linear_System<Grid_Generator>::const_iterator& y)
  : i(y) {
}

inline Grid_Generator_System::const_iterator
Grid_Generator_System::begin() const {
  return static_cast<Grid_Generator_System::const_iterator>(sys.begin());
}

inline Grid_Generator_System::const_iterator
Grid_Generator_System::end() const {
  return static_cast<Grid_Generator_System::const_iterator>(sys.end());
}

inline const Grid_Generator&
Grid_Generator_System::operator[](const dimension_type k) const {
  return sys[k];
}

inline bool
Grid_Generator_System::has_no_rows() const {
  return sys.has_no_rows();
}

inline void
Grid_Generator_System::remove_trailing_rows(dimension_type n) {
  sys.remove_trailing_rows(n);
}

inline void
Grid_Generator_System::insert_verbatim(const Grid_Generator& g) {
  sys.insert(g);
}

inline Topology
Grid_Generator_System::topology() const {
  return sys.topology();
}

inline dimension_type
Grid_Generator_System::first_pending_row() const {
  return sys.first_pending_row();
}

/*! \relates Grid_Generator_System */
inline bool
operator==(const Grid_Generator_System& x,
           const Grid_Generator_System& y) {
  return x.is_equal_to(y);
}

/*! \relates Grid_Generator_System */
inline void
swap(Grid_Generator_System& x, Grid_Generator_System& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Grid_Certificate_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Grid_Certificate;

}

/* Automatically generated from PPL source file ../src/Grid_defs.hh line 47. */
#include <vector>
#include <iosfwd>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*!
  \relates Parma_Polyhedra_Library::Grid
  Writes a textual representation of \p gr on \p s: <CODE>false</CODE>
  is written if \p gr is an empty grid; <CODE>true</CODE> is written
  if \p gr is a universe grid; a minimized system of congruences
  defining \p gr is written otherwise, all congruences in one row
  separated by ", "s.
*/
std::ostream&
operator<<(std::ostream& s, const Grid& gr);

} // namespace IO_Operators

//! Swaps \p x with \p y.
/*! \relates Grid */
void swap(Grid& x, Grid& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if \p x and \p y are the same
  grid.

  \relates Grid
  Note that \p x and \p y may be dimension-incompatible grids: in
  those cases, the value <CODE>false</CODE> is returned.
*/
bool operator==(const Grid& x, const Grid& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if \p x and \p y are different
  grids.

  \relates Grid
  Note that \p x and \p y may be dimension-incompatible grids: in
  those cases, the value <CODE>true</CODE> is returned.
*/
bool operator!=(const Grid& x, const Grid& y);

} // namespace Parma_Polyhedra_Library


//! A grid.
/*! \ingroup PPL_CXX_interface
  An object of the class Grid represents a rational grid.

  The domain of grids <EM>optimally supports</EM>:
    - all (proper and non-proper) congruences;
    - tautological and inconsistent constraints;
    - linear equality constraints (i.e., non-proper congruences).

  Depending on the method, using a constraint that is not optimally
  supported by the domain will either raise an exception or
  result in a (possibly non-optimal) upward approximation.

  The domain of grids support a concept of double description similar
  to the one developed for polyhedra: hence, a grid can be specified
  as either a finite system of congruences or a finite system of
  generators (see Section \ref sect_rational_grids) and it is always
  possible to obtain either representation.
  That is, if we know the system of congruences, we can obtain
  from this a system of generators that define the same grid
  and vice versa.
  These systems can contain redundant members, or they can be in the
  minimal form.

  A key attribute of any grid is its space dimension (the dimension
  \f$n \in \Nset\f$ of the enclosing vector space):

  - all grids, the empty ones included, are endowed with a space
    dimension;
  - most operations working on a grid and another object (another
    grid, a congruence, a generator, a set of variables, etc.) will
    throw an exception if the grid and the object are not
    dimension-compatible (see Section \ref Grid_Space_Dimensions);
  - the only ways in which the space dimension of a grid can be
    changed are with <EM>explicit</EM> calls to operators provided for
    that purpose, and with standard copy, assignment and swap
    operators.

  Note that two different grids can be defined on the zero-dimension
  space: the empty grid and the universe grid \f$R^0\f$.

  \par
  In all the examples it is assumed that variables
  <CODE>x</CODE> and <CODE>y</CODE> are defined (where they are
  used) as follows:
  \code
  Variable x(0);
  Variable y(1);
  \endcode

  \par Example 1
  The following code builds a grid corresponding to the even integer
  pairs in \f$\Rset^2\f$, given as a system of congruences:
  \code
  Congruence_System cgs;
  cgs.insert((x %= 0) / 2);
  cgs.insert((y %= 0) / 2);
  Grid gr(cgs);
  \endcode
  The following code builds the same grid as above, but starting
  from a system of generators specifying three of the points:
  \code
  Grid_Generator_System gs;
  gs.insert(grid_point(0*x + 0*y));
  gs.insert(grid_point(0*x + 2*y));
  gs.insert(grid_point(2*x + 0*y));
  Grid gr(gs);
  \endcode

  \par Example 2
  The following code builds a grid corresponding to a line in
  \f$\Rset^2\f$ by adding a single congruence to the universe grid:
  \code
  Congruence_System cgs;
  cgs.insert(x - y == 0);
  Grid gr(cgs);
  \endcode
  The following code builds the same grid as above, but starting
  from a system of generators specifying a point and a line:
  \code
  Grid_Generator_System gs;
  gs.insert(grid_point(0*x + 0*y));
  gs.insert(grid_line(x + y));
  Grid gr(gs);
  \endcode

  \par Example 3
  The following code builds a grid corresponding to the integral
  points on the line \f$x = y\f$ in \f$\Rset^2\f$ constructed
  by adding an equality and congruence to the universe grid:
  \code
  Congruence_System cgs;
  cgs.insert(x - y == 0);
  cgs.insert(x %= 0);
  Grid gr(cgs);
  \endcode
  The following code builds the same grid as above, but starting
  from a system of generators specifying a point and a parameter:
  \code
  Grid_Generator_System gs;
  gs.insert(grid_point(0*x + 0*y));
  gs.insert(parameter(x + y));
  Grid gr(gs);
  \endcode

  \par Example 4
  The following code builds the grid corresponding to a plane by
  creating the universe grid in \f$\Rset^2\f$:
  \code
  Grid gr(2);
  \endcode
  The following code builds the same grid as above, but starting
  from the empty grid in \f$\Rset^2\f$ and inserting the appropriate
  generators (a point, and two lines).
  \code
  Grid gr(2, EMPTY);
  gr.add_grid_generator(grid_point(0*x + 0*y));
  gr.add_grid_generator(grid_line(x));
  gr.add_grid_generator(grid_line(y));
  \endcode
  Note that a generator system must contain a point when describing
  a grid.  To ensure that this is always the case it is required
  that the first generator inserted in an empty grid is a point
  (otherwise, an exception is thrown).

  \par Example 5
  The following code shows the use of the function
  <CODE>add_space_dimensions_and_embed</CODE>:
  \code
  Grid gr(1);
  gr.add_congruence(x == 2);
  gr.add_space_dimensions_and_embed(1);
  \endcode
  We build the universe grid in the 1-dimension space \f$\Rset\f$.
  Then we add a single equality congruence,
  thus obtaining the grid corresponding to the singleton set
  \f$\{ 2 \} \sseq \Rset\f$.
  After the last line of code, the resulting grid is
  \f[
  \bigl\{\,
  (2, y)^\transpose \in \Rset^2
  \bigm|
  y \in \Rset
  \,\bigr\}.
  \f]

  \par Example 6
  The following code shows the use of the function
  <CODE>add_space_dimensions_and_project</CODE>:
  \code
  Grid gr(1);
  gr.add_congruence(x == 2);
  gr.add_space_dimensions_and_project(1);
  \endcode
  The first two lines of code are the same as in Example 4 for
  <CODE>add_space_dimensions_and_embed</CODE>.
  After the last line of code, the resulting grid is
  the singleton set
  \f$\bigl\{ (2, 0)^\transpose \bigr\} \sseq \Rset^2\f$.

  \par Example 7
  The following code shows the use of the function
  <CODE>affine_image</CODE>:
  \code
  Grid gr(2, EMPTY);
  gr.add_grid_generator(grid_point(0*x + 0*y));
  gr.add_grid_generator(grid_point(4*x + 0*y));
  gr.add_grid_generator(grid_point(0*x + 2*y));
  Linear_Expression expr = x + 3;
  gr.affine_image(x, expr);
  \endcode
  In this example the starting grid is all the pairs of \f$x\f$ and
  \f$y\f$ in \f$\Rset^2\f$ where \f$x\f$ is an integer multiple of 4
  and \f$y\f$ is an integer multiple of 2.  The considered variable
  is \f$x\f$ and the affine expression is \f$x+3\f$.  The resulting
  grid is the given grid translated 3 integers to the right (all the
  pairs \f$(x, y)\f$ where \f$x\f$ is -1 plus an integer multiple of 4
  and \f$y\f$ is an integer multiple of 2).
  Moreover, if the affine transformation for the same variable \p x
  is instead \f$x+y\f$:
  \code
  Linear_Expression expr = x + y;
  \endcode
  the resulting grid is every second integral point along the \f$x=y\f$
  line, with this line of points repeated at every fourth integral value
  along the \f$x\f$ axis.
  Instead, if we do not use an invertible transformation for the
  same variable; for example, the affine expression \f$y\f$:
  \code
  Linear_Expression expr = y;
  \endcode
  the resulting grid is every second point along the \f$x=y\f$ line.

  \par Example 8
  The following code shows the use of the function
  <CODE>affine_preimage</CODE>:
  \code
  Grid gr(2, EMPTY);
  gr.add_grid_generator(grid_point(0*x + 0*y));
  gr.add_grid_generator(grid_point(4*x + 0*y));
  gr.add_grid_generator(grid_point(0*x + 2*y));
  Linear_Expression expr = x + 3;
  gr.affine_preimage(x, expr);
  \endcode
  In this example the starting grid, \p var and the affine
  expression and the denominator are the same as in Example 6, while
  the resulting grid is similar but translated 3 integers to the
  left (all the pairs \f$(x, y)\f$
  where \f$x\f$ is -3 plus an integer multiple of 4 and
  \f$y\f$ is an integer multiple of 2)..
  Moreover, if the affine transformation for \p x is \f$x+y\f$
  \code
  Linear_Expression expr = x + y;
  \endcode
  the resulting grid is a similar grid to the result in Example 6,
  only the grid is slanted along \f$x=-y\f$.
  Instead, if we do not use an invertible transformation for the same
  variable \p x, for example, the affine expression \f$y\f$:
  \code
  Linear_Expression expr = y;
  \endcode
  the resulting grid is every fourth line parallel to the \f$x\f$
  axis.

  \par Example 9
  For this example we also use the variables:
  \code
  Variable z(2);
  Variable w(3);
  \endcode
  The following code shows the use of the function
  <CODE>remove_space_dimensions</CODE>:
  \code
  Grid_Generator_System gs;
  gs.insert(grid_point(3*x + y +0*z + 2*w));
  Grid gr(gs);
  Variables_Set vars;
  vars.insert(y);
  vars.insert(z);
  gr.remove_space_dimensions(vars);
  \endcode
  The starting grid is the singleton set
  \f$\bigl\{ (3, 1, 0, 2)^\transpose \bigr\} \sseq \Rset^4\f$, while
  the resulting grid is
  \f$\bigl\{ (3, 2)^\transpose \bigr\} \sseq \Rset^2\f$.
  Be careful when removing space dimensions <EM>incrementally</EM>:
  since dimensions are automatically renamed after each application
  of the <CODE>remove_space_dimensions</CODE> operator, unexpected
  results can be obtained.
  For instance, by using the following code we would obtain
  a different result:
  \code
  set<Variable> vars1;
  vars1.insert(y);
  gr.remove_space_dimensions(vars1);
  set<Variable> vars2;
  vars2.insert(z);
  gr.remove_space_dimensions(vars2);
  \endcode
  In this case, the result is the grid
  \f$\bigl\{(3, 0)^\transpose \bigr\} \sseq \Rset^2\f$:
  when removing the set of dimensions \p vars2
  we are actually removing variable \f$w\f$ of the original grid.
  For the same reason, the operator \p remove_space_dimensions
  is not idempotent: removing twice the same non-empty set of dimensions
  is never the same as removing them just once.
*/

class Parma_Polyhedra_Library::Grid {
public:
  //! The numeric type of coefficients.
  typedef Coefficient coefficient_type;

  //! Returns the maximum space dimension all kinds of Grid can handle.
  static dimension_type max_space_dimension();

  /*! \brief
    Returns true indicating that this domain has methods that
    can recycle congruences.
  */
  static bool can_recycle_congruence_systems();

  /*! \brief
    Returns true indicating that this domain has methods that
    can recycle constraints.
  */
  static bool can_recycle_constraint_systems();

  //! Builds a grid having the specified properties.
  /*!
    \param num_dimensions
    The number of dimensions of the vector space enclosing the grid;

    \param kind
    Specifies whether the universe or the empty grid has to be built.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space
    dimension.
  */
  explicit Grid(dimension_type num_dimensions = 0,
                Degenerate_Element kind = UNIVERSE);

  //! Builds a grid, copying a system of congruences.
  /*!
    The grid inherits the space dimension of the congruence system.

    \param cgs
    The system of congruences defining the grid.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space
    dimension.
  */
  explicit Grid(const Congruence_System& cgs);

  //! Builds a grid, recycling a system of congruences.
  /*!
    The grid inherits the space dimension of the congruence system.

    \param cgs
    The system of congruences defining the grid.  Its data-structures
    may be recycled to build the grid.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space
    dimension.
  */
  Grid(Congruence_System& cgs, Recycle_Input dummy);

  //! Builds a grid, copying a system of constraints.
  /*!
    The grid inherits the space dimension of the constraint system.

    \param cs
    The system of constraints defining the grid.

    \exception std::invalid_argument
    Thrown if the constraint system \p cs contains inequality constraints.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space
    dimension.
  */
  explicit Grid(const Constraint_System& cs);

  //! Builds a grid, recycling a system of constraints.
  /*!
    The grid inherits the space dimension of the constraint system.

    \param cs
    The system of constraints defining the grid.  Its data-structures
    may be recycled to build the grid.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.

    \exception std::invalid_argument
    Thrown if the constraint system \p cs contains inequality constraints.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space
    dimension.
  */
  Grid(Constraint_System& cs, Recycle_Input dummy);

  //! Builds a grid, copying a system of grid generators.
  /*!
    The grid inherits the space dimension of the generator system.

    \param ggs
    The system of generators defining the grid.

    \exception std::invalid_argument
    Thrown if the system of generators is not empty but has no points.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space
    dimension.
  */
  explicit Grid(const Grid_Generator_System& ggs);

  //! Builds a grid, recycling a system of grid generators.
  /*!
    The grid inherits the space dimension of the generator system.

    \param ggs
    The system of generators defining the grid.  Its data-structures
    may be recycled to build the grid.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.

    \exception std::invalid_argument
    Thrown if the system of generators is not empty but has no points.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space dimension.
  */
  Grid(Grid_Generator_System& ggs, Recycle_Input dummy);

  //! Builds a grid out of a box.
  /*!
    The grid inherits the space dimension of the box.
    The built grid is the most precise grid that includes the box.

    \param box
    The box representing the grid to be built.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.

    \exception std::length_error
    Thrown if the space dimension of \p box exceeds the maximum
    allowed space dimension.
  */
  template <typename Interval>
  explicit Grid(const Box<Interval>& box,
                Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a grid out of a bounded-difference shape.
  /*!
    The grid inherits the space dimension of the BDS.
    The built grid is the most precise grid that includes the BDS.

    \param bd
    The BDS representing the grid to be built.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.

    \exception std::length_error
    Thrown if the space dimension of \p bd exceeds the maximum
    allowed space dimension.
  */
  template <typename U>
  explicit Grid(const BD_Shape<U>& bd,
                Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a grid out of an octagonal shape.
  /*!
    The grid inherits the space dimension of the octagonal shape.
    The built grid is the most precise grid that includes the octagonal shape.

    \param os
    The octagonal shape representing the grid to be built.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.

    \exception std::length_error
    Thrown if the space dimension of \p os exceeds the maximum
    allowed space dimension.
  */
  template <typename U>
  explicit Grid(const Octagonal_Shape<U>& os,
                Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    Builds a grid from a polyhedron using algorithms whose complexity
    does not exceed the one specified by \p complexity.
    If \p complexity is \p ANY_COMPLEXITY, then the grid built is the
    smallest one containing \p ph.

    The grid inherits the space dimension of polyhedron.

    \param ph
    The polyhedron.

    \param complexity
    The complexity class.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space
    dimension.
  */
  explicit Grid(const Polyhedron& ph,
                Complexity_Class complexity = ANY_COMPLEXITY);

  //! Ordinary copy constructor.
  /*!
    The complexity argument is ignored.
  */
  Grid(const Grid& y,
       Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    The assignment operator.  (\p *this and \p y can be
    dimension-incompatible.)
  */
  Grid& operator=(const Grid& y);

  //! \name Member Functions that Do Not Modify the Grid
  //@{

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  /*! \brief
    Returns \f$0\f$, if \p *this is empty; otherwise, returns
    the \ref Grid_Affine_Dimension "affine dimension" of \p *this.
  */
  dimension_type affine_dimension() const;

  /*! \brief
    Returns a system of equality constraints satisfied by \p *this
    with the same affine dimension as \p *this.
  */
  Constraint_System constraints() const;

  /*! \brief
    Returns a minimal system of equality constraints satisfied by
    \p *this with the same affine dimension as \p *this.
  */
  Constraint_System minimized_constraints() const;

  //! Returns the system of congruences.
  const Congruence_System& congruences() const;

  //! Returns the system of congruences in minimal form.
  const Congruence_System& minimized_congruences() const;

  //! Returns the system of generators.
  const Grid_Generator_System& grid_generators() const;

  //! Returns the minimized system of generators.
  const Grid_Generator_System& minimized_grid_generators() const;

  //! Returns the relations holding between \p *this and \p cg.
  /*
    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible.
  */
  // FIXME: Poly_Con_Relation seems to encode exactly what we want
  // here.  We must find a new name for that class.  Temporarily,
  // we keep using it without changing the name.
  Poly_Con_Relation relation_with(const Congruence& cg) const;

  //! Returns the relations holding between \p *this and \p g.
  /*
    \exception std::invalid_argument
    Thrown if \p *this and generator \p g are dimension-incompatible.
  */
  // FIXME: see the comment for Poly_Con_Relation above.
  Poly_Gen_Relation
  relation_with(const Grid_Generator& g) const;

  //! Returns the relations holding between \p *this and \p g.
  /*
    \exception std::invalid_argument
    Thrown if \p *this and generator \p g are dimension-incompatible.
  */
  // FIXME: see the comment for Poly_Con_Relation above.
  Poly_Gen_Relation
  relation_with(const Generator& g) const;

  //! Returns the relations holding between \p *this and \p c.
  /*
    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible.
  */
  // FIXME: Poly_Con_Relation seems to encode exactly what we want
  // here.  We must find a new name for that class.  Temporarily,
  // we keep using it without changing the name.
  Poly_Con_Relation relation_with(const Constraint& c) const;

  //! Returns \c true if and only if \p *this is an empty grid.
  bool is_empty() const;

  //! Returns \c true if and only if \p *this is a universe grid.
  bool is_universe() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is a
    topologically closed subset of the vector space.

    A grid is always topologically closed.
  */
  bool is_topologically_closed() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this and \p y are
    disjoint.

    \exception std::invalid_argument
    Thrown if \p x and \p y are dimension-incompatible.
  */
  bool is_disjoint_from(const Grid& y) const;

  //! Returns <CODE>true</CODE> if and only if \p *this is discrete.
  /*!
    A grid is discrete if it can be defined by a generator system which
    contains only points and parameters.  This includes the empty grid
    and any grid in dimension zero.
  */
  bool is_discrete() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is bounded.
  bool is_bounded() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    contains at least one integer point.
  */
  bool contains_integer_point() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p var is constrained in
    \p *this.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  bool constrains(Variable var) const;

  //! Returns <CODE>true</CODE> if and only if \p expr is bounded in \p *this.
  /*!
    This method is the same as bounds_from_below.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_above(const Linear_Expression& expr) const;

  //! Returns <CODE>true</CODE> if and only if \p expr is bounded in \p *this.
  /*!
    This method is the same as bounds_from_above.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_below(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty and
    \p expr is bounded from above in \p *this, in which case the
    supremum value is computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if the supremum value can be reached in \p this.
    Always <CODE>true</CODE> when \p this bounds \p expr.  Present for
    interface compatibility with class Polyhedron, where closure
    points can result in a value of false.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded by \p *this,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d and \p
    maximum are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty and
    \p expr is bounded from above in \p *this, in which case the
    supremum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if the supremum value can be reached in \p this.
    Always <CODE>true</CODE> when \p this bounds \p expr.  Present for
    interface compatibility with class Polyhedron, where closure
    points can result in a value of false;

    \param point
    When maximization succeeds, will be assigned a point where \p expr
    reaches its supremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded by \p *this,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d, \p maximum
    and \p point are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum,
                Generator& point) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty and
    \p expr is bounded from below in \p *this, in which case the
    infimum value is computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if the is the infimum value can be reached in \p
    this.  Always <CODE>true</CODE> when \p this bounds \p expr.
    Present for interface compatibility with class Polyhedron, where
    closure points can result in a value of false.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d
    and \p minimum are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty and
    \p expr is bounded from below in \p *this, in which case the
    infimum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if the is the infimum value can be reached in \p
    this.  Always <CODE>true</CODE> when \p this bounds \p expr.
    Present for interface compatibility with class Polyhedron, where
    closure points can result in a value of false;

    \param point
    When minimization succeeds, will be assigned a point where \p expr
    reaches its infimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d, \p minimum
    and \p point are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum,
                Generator& point) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty and
    \ref Grid_Frequency "frequency" for \p *this with respect to \p expr
    is defined, in which case the frequency and the value for \p expr
    that is closest to zero are computed.

    \param expr
    The linear expression for which the frequency is needed;

    \param freq_n
    The numerator of the maximum frequency of \p expr;

    \param freq_d
    The denominator of the maximum frequency of \p expr;

    \param val_n
    The numerator of them value of \p expr at a point in the grid
    that is closest to zero;

    \param val_d
    The denominator of a value of \p expr at a point in the grid
    that is closest to zero;

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or frequency is undefined with respect to \p expr,
    then <CODE>false</CODE> is returned and \p freq_n, \p freq_d,
    \p val_n and \p val_d are left untouched.
  */
  bool frequency(const Linear_Expression& expr,
                 Coefficient& freq_n, Coefficient& freq_d,
                 Coefficient& val_n, Coefficient& val_d) const;

  //! Returns <CODE>true</CODE> if and only if \p *this contains \p y.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool contains(const Grid& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this strictly
    contains \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool strictly_contains(const Grid& y) const;

  //! Checks if all the invariants are satisfied.
  /*!
    \return
    <CODE>true</CODE> if and only if \p *this satisfies all the
    invariants and either \p check_not_empty is <CODE>false</CODE> or
    \p *this is not empty.

    \param check_not_empty
    <CODE>true</CODE> if and only if, in addition to checking the
    invariants, \p *this must be checked to be not empty.

    The check is performed so as to intrude as little as possible.  If
    the library has been compiled with run-time assertions enabled,
    error messages are written on <CODE>std::cerr</CODE> in case
    invariants are violated. This is useful for the purpose of
    debugging the library.
  */
  bool OK(bool check_not_empty = false) const;

  //@} // Member Functions that Do Not Modify the Grid

  //! \name Space Dimension Preserving Member Functions that May Modify the Grid
  //@{

  //! Adds a copy of congruence \p cg to \p *this.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are
    dimension-incompatible.
  */
  void add_congruence(const Congruence& cg);

  /*! \brief
    Adds a copy of grid generator \p g to the system of generators of
    \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and generator \p g are dimension-incompatible,
    or if \p *this is an empty grid and \p g is not a point.
  */
  void add_grid_generator(const Grid_Generator& g);

  //! Adds a copy of each congruence in \p cgs to \p *this.
  /*!
    \param cgs
    Contains the congruences that will be added to the system of
    congruences of \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible.
  */
  void add_congruences(const Congruence_System& cgs);

  //! Adds the congruences in \p cgs to *this.
  /*!
    \param cgs
    The congruence system to be added to \p *this.  The congruences in
    \p cgs may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible.

    \warning
    The only assumption that can be made about \p cgs upon successful
    or exceptional return is that it can be safely destroyed.
  */
  void add_recycled_congruences(Congruence_System& cgs);

  /*! \brief
    Adds to \p *this a congruence equivalent to constraint \p c.

    \param c
    The constraint to be added.

    \exception std::invalid_argument
    Thrown if \p *this and \p c are dimension-incompatible
    or if constraint \p c is not optimally supported by the grid domain.
  */
  void add_constraint(const Constraint& c);

  /*! \brief
    Adds to \p *this congruences equivalent to the constraints in \p cs.

    \param cs
    The constraints to be added.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible
    or if \p cs contains a constraint which is not optimally supported
    by the grid domain.
  */
  void add_constraints(const Constraint_System& cs);

  /*! \brief
    Adds to \p *this congruences equivalent to the constraints in \p cs.

    \param cs
    The constraints to be added. They may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible
    or if \p cs contains a constraint which is not optimally supported
    by the grid domain.

    \warning
    The only assumption that can be made about \p cs upon successful
    or exceptional return is that it can be safely destroyed.
  */
  void add_recycled_constraints(Constraint_System& cs);

  //! Uses a copy of the congruence \p cg to refine \p *this.
  /*!
    \param cg
    The congruence used.

    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible.
  */
  void refine_with_congruence(const Congruence& cg);

 //! Uses a copy of the congruences in \p cgs to refine \p *this.
  /*!
    \param cgs
    The congruences used.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible.
  */
  void refine_with_congruences(const Congruence_System& cgs);

  //! Uses a copy of the constraint \p c to refine \p *this.
  /*!

    \param c
    The constraint used. If it is not an equality, it will be ignored

    \exception std::invalid_argument
    Thrown if \p *this and \p c are dimension-incompatible.
  */
  void refine_with_constraint(const Constraint& c);

  //! Uses a copy of the constraints in \p cs to refine \p *this.
  /*!
    \param cs
    The constraints used. Constraints that are not equalities are ignored.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible.
  */
  void refine_with_constraints(const Constraint_System& cs);

  /*! \brief
    Adds a copy of the generators in \p gs to the system of generators
    of \p *this.

    \param gs
    Contains the generators that will be added to the system of
    generators of \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p gs are dimension-incompatible, or if
    \p *this is empty and the system of generators \p gs is not empty,
    but has no points.
  */
  void add_grid_generators(const Grid_Generator_System& gs);

  /*! \brief
    Adds the generators in \p gs to the system of generators of \p
    *this.

    \param gs
    The generator system to be added to \p *this.  The generators in
    \p gs may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p gs are dimension-incompatible.

    \warning
    The only assumption that can be made about \p gs upon successful
    or exceptional return is that it can be safely destroyed.
  */
  void add_recycled_grid_generators(Grid_Generator_System& gs);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to space dimension \p var, assigning the result to \p *this.

    \param var
    The space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  void unconstrain(Variable var);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to the set of space dimensions \p vars,
    assigning the result to \p *this.

    \param vars
    The set of space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars.
  */
  void unconstrain(const Variables_Set& vars);

  /*! \brief
    Assigns to \p *this the intersection of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void intersection_assign(const Grid& y);

  /*! \brief
    Assigns to \p *this the least upper bound of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void upper_bound_assign(const Grid& y);

  /*! \brief
    If the upper bound of \p *this and \p y is exact it is assigned to \p
    *this and <CODE>true</CODE> is returned, otherwise
    <CODE>false</CODE> is returned.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool upper_bound_assign_if_exact(const Grid& y);

  /*! \brief
    Assigns to \p *this the \ref Convex_Polyhedral_Difference "grid-difference"
    of \p *this and \p y.

    The grid difference between grids x and y is the smallest grid
    containing all the points from x and y that are only in x.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void difference_assign(const Grid& y);

  /*! \brief
    Assigns to \p *this a \ref Meet_Preserving_Simplification
    "meet-preserving simplification" of \p *this with respect to \p y.
    If \c false is returned, then the intersection is empty.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool simplify_using_context_assign(const Grid& y);

  /*! \brief
    Assigns to \p *this the \ref Grid_Affine_Transformation
    "affine image" of \p
    *this under the function mapping variable \p var to the affine
    expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is assigned;

    \param expr
    The numerator of the affine expression;

    \param denominator
    The denominator of the affine expression (optional argument with
    default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of
    \p *this.

    \if Include_Implementation_Details

    When considering the generators of a grid, the
    affine transformation
    \f[
      \frac{\sum_{i=0}^{n-1} a_i x_i + b}{\mathrm{denominator}}
    \f]
    is assigned to \p var where \p expr is
    \f$\sum_{i=0}^{n-1} a_i x_i + b\f$
    (\f$b\f$ is the inhomogeneous term).

    If congruences are up-to-date, it uses the specialized function
    affine_preimage() (for the system of congruences)
    and inverse transformation to reach the same result.
    To obtain the inverse transformation we use the following observation.

    Observation:
    -# The affine transformation is invertible if the coefficient
       of \p var in this transformation (i.e., \f$a_\mathrm{var}\f$)
       is different from zero.
    -# If the transformation is invertible, then we can write
       \f[
         \mathrm{denominator} * {x'}_\mathrm{var}
           = \sum_{i = 0}^{n - 1} a_i x_i + b
           = a_\mathrm{var} x_\mathrm{var}
             + \sum_{i \neq var} a_i x_i + b,
       \f]
       so that the inverse transformation is
       \f[
         a_\mathrm{var} x_\mathrm{var}
           = \mathrm{denominator} * {x'}_\mathrm{var}
             - \sum_{i \neq j} a_i x_i - b.
       \f]

    Then, if the transformation is invertible, all the entities that
    were up-to-date remain up-to-date. Otherwise only generators remain
    up-to-date.

    \endif
  */
  void affine_image(Variable var,
                    const Linear_Expression& expr,
                    Coefficient_traits::const_reference denominator
                    = Coefficient_one());

  /*! \brief
    Assigns to \p *this the \ref Grid_Affine_Transformation
    "affine preimage" of
    \p *this under the function mapping variable \p var to the affine
    expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is substituted;

    \param expr
    The numerator of the affine expression;

    \param denominator
    The denominator of the affine expression (optional argument with
    default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this.

    \if Include_Implementation_Details

    When considering congruences of a grid, the affine transformation
    \f[
      \frac{\sum_{i=0}^{n-1} a_i x_i + b}{denominator},
    \f]
    is assigned to \p var where \p expr is
    \f$\sum_{i=0}^{n-1} a_i x_i + b\f$
    (\f$b\f$ is the inhomogeneous term).

    If generators are up-to-date, then the specialized function
    affine_image() is used (for the system of generators)
    and inverse transformation to reach the same result.
    To obtain the inverse transformation, we use the following observation.

    Observation:
    -# The affine transformation is invertible if the coefficient
       of \p var in this transformation (i.e. \f$a_\mathrm{var}\f$)
       is different from zero.
    -# If the transformation is invertible, then we can write
       \f[
         \mathrm{denominator} * {x'}_\mathrm{var}
           = \sum_{i = 0}^{n - 1} a_i x_i + b
           = a_\mathrm{var} x_\mathrm{var}
               + \sum_{i \neq \mathrm{var}} a_i x_i + b,
       \f],
       the inverse transformation is
       \f[
         a_\mathrm{var} x_\mathrm{var}
           = \mathrm{denominator} * {x'}_\mathrm{var}
               - \sum_{i \neq j} a_i x_i - b.
       \f].

    Then, if the transformation is invertible, all the entities that
    were up-to-date remain up-to-date. Otherwise only congruences remain
    up-to-date.

    \endif
  */
  void affine_preimage(Variable var,
                       const Linear_Expression& expr,
                       Coefficient_traits::const_reference denominator
                         = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to
    the \ref Grid_Generalized_Image "generalized affine relation"
    \f$\mathrm{var}' = \frac{\mathrm{expr}}{\mathrm{denominator}}
    \pmod{\mathrm{modulus}}\f$.

    \param var
    The left hand side variable of the generalized affine relation;

    \param relsym
    The relation symbol where EQUAL is the symbol for a congruence
    relation;

    \param expr
    The numerator of the right hand side affine expression;

    \param denominator
    The denominator of the right hand side affine expression.
    Optional argument with an automatic value of one;

    \param modulus
    The modulus of the congruence lhs %= rhs.  A modulus of zero
    indicates lhs == rhs.  Optional argument with an automatic value
    of zero.

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p
    *this.
  */
  void
  generalized_affine_image(Variable var,
                           Relation_Symbol relsym,
                           const Linear_Expression& expr,
                           Coefficient_traits::const_reference denominator
                           = Coefficient_one(),
                           Coefficient_traits::const_reference modulus
                           = Coefficient_zero());

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Grid_Generalized_Image "generalized affine relation"
    \f$\mathrm{var}' = \frac{\mathrm{expr}}{\mathrm{denominator}}
    \pmod{\mathrm{modulus}}\f$.

    \param var
    The left hand side variable of the generalized affine relation;

    \param relsym
    The relation symbol where EQUAL is the symbol for a congruence
    relation;

    \param expr
    The numerator of the right hand side affine expression;

    \param denominator
    The denominator of the right hand side affine expression.
    Optional argument with an automatic value of one;

    \param modulus
    The modulus of the congruence lhs %= rhs.  A modulus of zero
    indicates lhs == rhs.  Optional argument with an automatic value
    of zero.

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p
    *this.
  */
  void
  generalized_affine_preimage(Variable var,
                              Relation_Symbol relsym,
                              const Linear_Expression& expr,
                              Coefficient_traits::const_reference denominator
                              = Coefficient_one(),
                              Coefficient_traits::const_reference modulus
                              = Coefficient_zero());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to
    the \ref Grid_Generalized_Image "generalized affine relation"
    \f$\mathrm{lhs}' = \mathrm{rhs} \pmod{\mathrm{modulus}}\f$.

    \param lhs
    The left hand side affine expression.

    \param relsym
    The relation symbol where EQUAL is the symbol for a congruence
    relation;

    \param rhs
    The right hand side affine expression.

    \param modulus
    The modulus of the congruence lhs %= rhs.  A modulus of zero
    indicates lhs == rhs.  Optional argument with an automatic value
    of zero.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p
    rhs.
  */
  void
  generalized_affine_image(const Linear_Expression& lhs,
                           Relation_Symbol relsym,
                           const Linear_Expression& rhs,
                           Coefficient_traits::const_reference modulus
                           = Coefficient_zero());

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Grid_Generalized_Image "generalized affine relation"
    \f$\mathrm{lhs}' = \mathrm{rhs} \pmod{\mathrm{modulus}}\f$.

    \param lhs
    The left hand side affine expression;

    \param relsym
    The relation symbol where EQUAL is the symbol for a congruence
    relation;

    \param rhs
    The right hand side affine expression;

    \param modulus
    The modulus of the congruence lhs %= rhs.  A modulus of zero
    indicates lhs == rhs.  Optional argument with an automatic value
    of zero.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p
    rhs.
  */
  void
  generalized_affine_preimage(const Linear_Expression& lhs,
                              Relation_Symbol relsym,
                              const Linear_Expression& rhs,
                              Coefficient_traits::const_reference modulus
                              = Coefficient_zero());

  /*!
    \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_image(Variable var,
                            const Linear_Expression& lb_expr,
                            const Linear_Expression& ub_expr,
                            Coefficient_traits::const_reference denominator
                            = Coefficient_one());

  /*!
    \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_preimage(Variable var,
                               const Linear_Expression& lb_expr,
                               const Linear_Expression& ub_expr,
                               Coefficient_traits::const_reference denominator
                               = Coefficient_one());

  /*! \brief
    Assigns to \p *this the result of computing the \ref Grid_Time_Elapse
    "time-elapse" between \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void time_elapse_assign(const Grid& y);

  /*! \brief
    \ref Wrapping_Operator "Wraps" the specified dimensions of the
    vector space.

    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be wrapped.

    \param w
    The width of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param r
    The representation of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param o
    The overflow behavior of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param cs_p
    Possibly null pointer to a constraint system.
    This argument is for compatibility with wrap_assign()
    for the other domains and only checked for dimension-compatibility.

    \param complexity_threshold
    A precision parameter of the \ref Wrapping_Operator "wrapping operator".
    This argument is for compatibility with wrap_assign()
    for the other domains and is ignored.

    \param wrap_individually
    <CODE>true</CODE> if the dimensions should be wrapped individually.
    As wrapping dimensions collectively does not improve the precision,
    this argument is ignored.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars or with <CODE>*cs_p</CODE>.

    \warning
    It is assumed that variables in \p Vars represent integers.  Thus,
    where the extra cost is negligible, the integrality of these
    variables is enforced; possibly causing a non-integral grid to
    become empty.
  */
  void wrap_assign(const Variables_Set& vars,
                   Bounded_Integer_Type_Width w,
                   Bounded_Integer_Type_Representation r,
                   Bounded_Integer_Type_Overflow o,
                   const Constraint_System* cs_p = 0,
                   unsigned complexity_threshold = 16,
                   bool wrap_individually = true);

  /*! \brief
    Possibly tightens \p *this by dropping all points with non-integer
    coordinates.

    \param complexity
    This argument is ignored as the algorithm used has polynomial
    complexity.
  */
  void drop_some_non_integer_points(Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  /*! \brief
    Possibly tightens \p *this by dropping all points with non-integer
    coordinates for the space dimensions corresponding to \p vars.

    \param vars
    Points with non-integer coordinates for these variables/space-dimensions
    can be discarded.

    \param complexity
    This argument is ignored as the algorithm used has polynomial
    complexity.
  */
  void drop_some_non_integer_points(const Variables_Set& vars,
                                    Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  //! Assigns to \p *this its topological closure.
  void topological_closure_assign();

  /*! \brief
    Assigns to \p *this the result of computing the \ref Grid_Widening
    "Grid widening" between \p *this and \p y using congruence systems.

    \param y
    A grid that <EM>must</EM> be contained in \p *this;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Grid_Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void congruence_widening_assign(const Grid& y, unsigned* tp = NULL);

  /*! \brief
    Assigns to \p *this the result of computing the \ref Grid_Widening
    "Grid widening" between \p *this and \p y using generator systems.

    \param y
    A grid that <EM>must</EM> be contained in \p *this;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Grid_Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void generator_widening_assign(const Grid& y, unsigned* tp = NULL);

  /*! \brief
    Assigns to \p *this the result of computing the \ref Grid_Widening
    "Grid widening" between \p *this and \p y.

    This widening uses either the congruence or generator systems
    depending on which of the systems describing x and y
    are up to date and minimized.

    \param y
    A grid that <EM>must</EM> be contained in \p *this;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Grid_Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void widening_assign(const Grid& y, unsigned* tp = NULL);

  /*! \brief
    Improves the result of the congruence variant of
    \ref Grid_Widening "Grid widening" computation by also enforcing
    those congruences in \p cgs that are satisfied by all the points
    of \p *this.

    \param y
    A grid that <EM>must</EM> be contained in \p *this;

    \param cgs
    The system of congruences used to improve the widened grid;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Grid_Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cgs are dimension-incompatible.
  */
  void limited_congruence_extrapolation_assign(const Grid& y,
                                               const Congruence_System& cgs,
                                               unsigned* tp = NULL);

  /*! \brief
    Improves the result of the generator variant of the
    \ref Grid_Widening "Grid widening"
    computation by also enforcing those congruences in \p cgs that are
    satisfied by all the points of \p *this.

    \param y
    A grid that <EM>must</EM> be contained in \p *this;

    \param cgs
    The system of congruences used to improve the widened grid;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Grid_Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cgs are dimension-incompatible.
  */
  void limited_generator_extrapolation_assign(const Grid& y,
                                              const Congruence_System& cgs,
                                              unsigned* tp = NULL);

  /*! \brief
    Improves the result of the \ref Grid_Widening "Grid widening"
    computation by also enforcing those congruences in \p cgs that are
    satisfied by all the points of \p *this.

    \param y
    A grid that <EM>must</EM> be contained in \p *this;

    \param cgs
    The system of congruences used to improve the widened grid;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Grid_Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cgs are dimension-incompatible.
  */
  void limited_extrapolation_assign(const Grid& y,
                                    const Congruence_System& cgs,
                                    unsigned* tp = NULL);

  //@} // Space Dimension Preserving Member Functions that May Modify [...]

  //! \name Member Functions that May Modify the Dimension of the Vector Space
  //@{

  /*! \brief
    \ref Adding_New_Dimensions_to_the_Vector_Space "Adds"
    \p m new space dimensions and embeds the old grid in the new
    vector space.

    \param m
    The number of dimensions to add.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the vector
    space to exceed dimension <CODE>max_space_dimension()</CODE>.

    The new space dimensions will be those having the highest indexes
    in the new grid, which is characterized by a system of congruences
    in which the variables which are the new dimensions can have any
    value.  For instance, when starting from the grid \f$\cL \sseq
    \Rset^2\f$ and adding a third space dimension, the result will be
    the grid
    \f[
      \bigl\{\,
        (x, y, z)^\transpose \in \Rset^3
      \bigm|
        (x, y)^\transpose \in \cL
      \,\bigr\}.
    \f]
  */
  void add_space_dimensions_and_embed(dimension_type m);

  /*! \brief
    \ref Adding_New_Dimensions_to_the_Vector_Space "Adds"
    \p m new space dimensions to the grid and does not embed it
    in the new vector space.

    \param m
    The number of space dimensions to add.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the
    vector space to exceed dimension <CODE>max_space_dimension()</CODE>.

    The new space dimensions will be those having the highest indexes
    in the new grid, which is characterized by a system of congruences
    in which the variables running through the new dimensions are all
    constrained to be equal to 0.  For instance, when starting from
    the grid \f$\cL \sseq \Rset^2\f$ and adding a third space
    dimension, the result will be the grid
    \f[
      \bigl\{\,
        (x, y, 0)^\transpose \in \Rset^3
      \bigm|
        (x, y)^\transpose \in \cL
      \,\bigr\}.
    \f]
  */
  void add_space_dimensions_and_project(dimension_type m);

  /*! \brief
    Assigns to \p *this the \ref Concatenating_Polyhedra "concatenation" of
    \p *this and \p y, taken in this order.

    \exception std::length_error
    Thrown if the concatenation would cause the vector space
    to exceed dimension <CODE>max_space_dimension()</CODE>.
  */
  void concatenate_assign(const Grid& y);

  //! Removes all the specified dimensions from the vector space.
  /*!
    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be removed.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  /*! \brief
    Removes the higher dimensions of the vector space so that the
    resulting space will have \ref Removing_Dimensions_from_the_Vector_Space
    "dimension \p new_dimension."

    \exception std::invalid_argument
    Thrown if \p new_dimensions is greater than the space dimension of
    \p *this.
  */
  void remove_higher_space_dimensions(dimension_type new_dimension);

  /*! \brief
    Remaps the dimensions of the vector space according to
    a \ref Mapping_the_Dimensions_of_the_Vector_Space "partial function".

    If \p pfunc maps only some of the dimensions of \p *this then the
    rest will be projected away.

    If the highest dimension mapped to by \p pfunc is higher than the
    highest dimension in \p *this then the number of dimensions in \p
    *this will be increased to the highest dimension mapped to by \p
    pfunc.

    \param pfunc
    The partial function specifying the destiny of each space
    dimension.

    The template type parameter Partial_Function must provide
    the following methods.
    \code
      bool has_empty_codomain() const
    \endcode
    returns <CODE>true</CODE> if and only if the represented partial
    function has an empty codomain (i.e., it is always undefined).
    The <CODE>has_empty_codomain()</CODE> method will always be called
    before the methods below.  However, if
    <CODE>has_empty_codomain()</CODE> returns <CODE>true</CODE>, none
    of the functions below will be called.
    \code
      dimension_type max_in_codomain() const
    \endcode
    returns the maximum value that belongs to the codomain of the
    partial function.
    The <CODE>max_in_codomain()</CODE> method is called at most once.
    \code
      bool maps(dimension_type i, dimension_type& j) const
    \endcode
    Let \f$f\f$ be the represented function and \f$k\f$ be the value
    of \p i.  If \f$f\f$ is defined in \f$k\f$, then \f$f(k)\f$ is
    assigned to \p j and <CODE>true</CODE> is returned.  If \f$f\f$ is
    undefined in \f$k\f$, then <CODE>false</CODE> is returned.
    This method is called at most \f$n\f$ times, where \f$n\f$ is the
    dimension of the vector space enclosing the grid.

    The result is undefined if \p pfunc does not encode a partial
    function with the properties described in the
    \ref Mapping_the_Dimensions_of_the_Vector_Space "specification of the mapping operator".
  */
  template <typename Partial_Function>
  void map_space_dimensions(const Partial_Function& pfunc);

  //! Creates \p m copies of the space dimension corresponding to \p var.
  /*!
    \param var
    The variable corresponding to the space dimension to be replicated;

    \param m
    The number of replicas to be created.

    \exception std::invalid_argument
    Thrown if \p var does not correspond to a dimension of the vector
    space.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the vector
    space to exceed dimension <CODE>max_space_dimension()</CODE>.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    and <CODE>var</CODE> has space dimension \f$k \leq n\f$,
    then the \f$k\f$-th space dimension is
    \ref Expanding_One_Dimension_of_the_Vector_Space_to_Multiple_Dimensions
    "expanded" to \p m new space dimensions
    \f$n\f$, \f$n+1\f$, \f$\dots\f$, \f$n+m-1\f$.
  */
  void expand_space_dimension(Variable var, dimension_type m);

  //! Folds the space dimensions in \p vars into \p dest.
  /*!
    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be folded;

    \param dest
    The variable corresponding to the space dimension that is the
    destination of the folding operation.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p dest or with
    one of the Variable objects contained in \p vars.  Also
    thrown if \p dest is contained in \p vars.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    <CODE>dest</CODE> has space dimension \f$k \leq n\f$,
    \p vars is a set of variables whose maximum space dimension
    is also less than or equal to \f$n\f$, and \p dest is not a member
    of \p vars, then the space dimensions corresponding to
    variables in \p vars are
    \ref Folding_Multiple_Dimensions_of_the_Vector_Space_into_One_Dimension "folded"
    into the \f$k\f$-th space dimension.
  */
  void fold_space_dimensions(const Variables_Set& vars, Variable dest);

  //@} // Member Functions that May Modify the Dimension of the Vector Space

  friend bool operator==(const Grid& x, const Grid& y);

  friend class Parma_Polyhedra_Library::Grid_Certificate;

  template <typename Interval> friend class Parma_Polyhedra_Library::Box;

  //! \name Miscellaneous Member Functions
  //@{

  //! Destructor.
  ~Grid();

  /*! \brief
    Swaps \p *this with grid \p y.  (\p *this and \p y can be
    dimension-incompatible.)
  */
  void m_swap(Grid& y);

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns a 32-bit hash code for \p *this.

    If \p x and \p y are such that <CODE>x == y</CODE>,
    then <CODE>x.hash_code() == y.hash_code()</CODE>.
  */
  int32_t hash_code() const;

  //@} // Miscellaneous Member Functions

private:

  //! The system of congruences.
  Congruence_System con_sys;

  //! The system of generators.
  Grid_Generator_System gen_sys;

#define PPL_IN_Grid_CLASS
/* Automatically generated from PPL source file ../src/Grid_Status_idefs.hh line 1. */
/* Grid::Status class declaration.
*/


#ifndef PPL_IN_Grid_CLASS
#error "Do not include Grid_Status_idefs.hh directly; use Grid_defs.hh instead"
#endif

//! A conjunctive assertion about a grid.
/*!
  The assertions supported that are in use are:
  - <EM>zero-dim universe</EM>: the grid is the zero-dimension
    vector space \f$\Rset^0 = \{\cdot\}\f$;
  - <EM>empty</EM>: the grid is the empty set;
  - <EM>congruences up-to-date</EM>: the grid is correctly
    characterized by the attached system of congruences, modulo the
    processing of pending generators;
  - <EM>generators up-to-date</EM>: the grid is correctly
    characterized by the attached system of generators, modulo the
    processing of pending congruences;
  - <EM>congruences minimized</EM>: the non-pending part of the system
    of congruences attached to the grid is in minimal form;
  - <EM>generators minimized</EM>: the non-pending part of the system
    of generators attached to the grid is in minimal form.

  Other supported assertions are:
  - <EM>congruences pending</EM>
  - <EM>generators pending</EM>
  - <EM>congruences' saturation matrix up-to-date</EM>
  - <EM>generators' saturation matrix up-to-date</EM>.

  Not all the conjunctions of these elementary assertions constitute
  a legal Status.  In fact:
  - <EM>zero-dim universe</EM> excludes any other assertion;
  - <EM>empty</EM>: excludes any other assertion;
  - <EM>congruences pending</EM> and <EM>generators pending</EM>
    are mutually exclusive;
  - <EM>congruences pending</EM> implies both <EM>congruences minimized</EM>
    and <EM>generators minimized</EM>;
  - <EM>generators pending</EM> implies both <EM>congruences minimized</EM>
    and <EM>generators minimized</EM>;
  - <EM>congruences minimized</EM> implies <EM>congruences up-to-date</EM>;
  - <EM>generators minimized</EM> implies <EM>generators up-to-date</EM>;
  - <EM>congruences' saturation matrix up-to-date</EM> implies both
    <EM>congruences up-to-date</EM> and <EM>generators up-to-date</EM>;
  - <EM>generators' saturation matrix up-to-date</EM> implies both
    <EM>congruences up-to-date</EM> and <EM>generators up-to-date</EM>.
*/
class Status {
public:
  //! By default Status is the <EM>zero-dim universe</EM> assertion.
  Status();

  //! \name Test, remove or add an individual assertion from the conjunction
  //@{
  bool test_zero_dim_univ() const;
  void reset_zero_dim_univ();
  void set_zero_dim_univ();

  bool test_empty() const;
  void reset_empty();
  void set_empty();

  bool test_c_up_to_date() const;
  void reset_c_up_to_date();
  void set_c_up_to_date();

  bool test_g_up_to_date() const;
  void reset_g_up_to_date();
  void set_g_up_to_date();

  bool test_c_minimized() const;
  void reset_c_minimized();
  void set_c_minimized();

  bool test_g_minimized() const;
  void reset_g_minimized();
  void set_g_minimized();

  bool test_sat_c_up_to_date() const;
  void reset_sat_c_up_to_date();
  void set_sat_c_up_to_date();

  bool test_sat_g_up_to_date() const;
  void reset_sat_g_up_to_date();
  void set_sat_g_up_to_date();

  bool test_c_pending() const;
  void reset_c_pending();
  void set_c_pending();

  bool test_g_pending() const;
  void reset_g_pending();
  void set_g_pending();
  //@} // Test, remove or add an individual assertion from the conjunction

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

private:
  //! Status is implemented by means of a finite bitset.
  typedef unsigned int flags_t;

  //! \name Bitmasks for the individual assertions
  //@{
  static const flags_t ZERO_DIM_UNIV    = 0U;
  static const flags_t EMPTY            = 1U << 0;
  static const flags_t C_UP_TO_DATE     = 1U << 1;
  static const flags_t G_UP_TO_DATE     = 1U << 2;
  static const flags_t C_MINIMIZED      = 1U << 3;
  static const flags_t G_MINIMIZED      = 1U << 4;
  static const flags_t SAT_C_UP_TO_DATE = 1U << 5;
  static const flags_t SAT_G_UP_TO_DATE = 1U << 6;
  static const flags_t CS_PENDING       = 1U << 7;
  static const flags_t GS_PENDING       = 1U << 8;
  //@} // Bitmasks for the individual assertions

  //! This holds the current bitset.
  flags_t flags;

  //! Construct from a bitmask.
  Status(flags_t mask);

  //! Check whether <EM>all</EM> bits in \p mask are set.
  bool test_all(flags_t mask) const;

  //! Check whether <EM>at least one</EM> bit in \p mask is set.
  bool test_any(flags_t mask) const;

  //! Set the bits in \p mask.
  void set(flags_t mask);

  //! Reset the bits in \p mask.
  void reset(flags_t mask);
};

/* Automatically generated from PPL source file ../src/Grid_defs.hh line 1977. */
#undef PPL_IN_Grid_CLASS

  //! The status flags to keep track of the grid's internal state.
  Status status;

  //! The number of dimensions of the enclosing vector space.
  dimension_type space_dim;

  enum Dimension_Kind {
    PARAMETER = 0,
    LINE = 1,
    GEN_VIRTUAL = 2,
    PROPER_CONGRUENCE = PARAMETER,
    CON_VIRTUAL = LINE,
    EQUALITY = GEN_VIRTUAL
  };

  typedef std::vector<Dimension_Kind> Dimension_Kinds;

  // The type of row associated with each dimension.  If the virtual
  // rows existed then the reduced systems would be square and upper
  // or lower triangular, and the rows in each would have the types
  // given in this vector.  As the congruence system is reduced to an
  // upside-down lower triangular form the ordering of the congruence
  // types is last to first.
  Dimension_Kinds dim_kinds;

  //! Builds a grid universe or empty grid.
  /*!
    \param num_dimensions
    The number of dimensions of the vector space enclosing the grid;

    \param kind
    specifies whether the universe or the empty grid has to be built.
  */
  void construct(dimension_type num_dimensions, Degenerate_Element kind);

  //! Builds a grid from a system of congruences.
  /*!
    The grid inherits the space dimension of the congruence system.

    \param cgs
    The system of congruences defining the grid. Its data-structures
    may be recycled to build the grid.
  */
  void construct(Congruence_System& cgs);

  //! Builds a grid from a system of grid generators.
  /*!
    The grid inherits the space dimension of the generator system.

    \param ggs
    The system of grid generators defining the grid.  Its data-structures
    may be recycled to build the grid.
  */
  void construct(Grid_Generator_System& ggs);

  //! \name Private Verifiers: Verify if Individual Flags are Set
  //@{

  //! Returns <CODE>true</CODE> if the grid is known to be empty.
  /*!
    The return value <CODE>false</CODE> does not necessarily
    implies that \p *this is non-empty.
  */
  bool marked_empty() const;

  //! Returns <CODE>true</CODE> if the system of congruences is up-to-date.
  bool congruences_are_up_to_date() const;

  //! Returns <CODE>true</CODE> if the system of generators is up-to-date.
  bool generators_are_up_to_date() const;

  //! Returns <CODE>true</CODE> if the system of congruences is minimized.
  bool congruences_are_minimized() const;

  //! Returns <CODE>true</CODE> if the system of generators is minimized.
  bool generators_are_minimized() const;

  //@} // Private Verifiers: Verify if Individual Flags are Set

  //! \name State Flag Setters: Set Only the Specified Flags
  //@{

  /*! \brief
    Sets \p status to express that the grid is the universe
    0-dimension vector space, clearing all corresponding matrices.
  */
  void set_zero_dim_univ();

  /*! \brief
    Sets \p status to express that the grid is empty, clearing all
    corresponding matrices.
  */
  void set_empty();

  //! Sets \p status to express that congruences are up-to-date.
  void set_congruences_up_to_date();

  //! Sets \p status to express that generators are up-to-date.
  void set_generators_up_to_date();

  //! Sets \p status to express that congruences are minimized.
  void set_congruences_minimized();

  //! Sets \p status to express that generators are minimized.
  void set_generators_minimized();

  //@} // State Flag Setters: Set Only the Specified Flags

  //! \name State Flag Cleaners: Clear Only the Specified Flag
  //@{

  //! Clears the \p status flag indicating that the grid is empty.
  void clear_empty();

  //! Sets \p status to express that congruences are out of date.
  void clear_congruences_up_to_date();

  //! Sets \p status to express that generators are out of date.
  void clear_generators_up_to_date();

  //! Sets \p status to express that congruences are no longer minimized.
  void clear_congruences_minimized();

  //! Sets \p status to express that generators are no longer minimized.
  void clear_generators_minimized();

  //@} // State Flag Cleaners: Clear Only the Specified Flag

  //! \name Updating Matrices
  //@{

  //! Updates and minimizes the congruences from the generators.
  void update_congruences() const;

  //! Updates and minimizes the generators from the congruences.
  /*!
    \return
    <CODE>false</CODE> if and only if \p *this turns out to be an
    empty grid.

    It is illegal to call this method when the Status field already
    declares the grid to be empty.
  */
  bool update_generators() const;

  //@} // Updating Matrices

  //! \name Minimization of Descriptions
  //@{

  //! Minimizes both the congruences and the generators.
  /*!
    \return
    <CODE>false</CODE> if and only if \p *this turns out to be an
    empty grid.

    Minimization is performed on each system only if the minimized
    Status field is clear.
  */
  bool minimize() const;

  //@} // Minimization of Descriptions

  enum Three_Valued_Boolean {
    TVB_TRUE,
    TVB_FALSE,
    TVB_DONT_KNOW
  };

  //! Polynomial but incomplete equivalence test between grids.
  Three_Valued_Boolean quick_equivalence_test(const Grid& y) const;

  //! Returns <CODE>true</CODE> if and only if \p *this is included in \p y.
  bool is_included_in(const Grid& y) const;

  //! Checks if and how \p expr is bounded in \p *this.
  /*!
    Returns <CODE>true</CODE> if and only if \p from_above is
    <CODE>true</CODE> and \p expr is bounded from above in \p *this,
    or \p from_above is <CODE>false</CODE> and \p expr is bounded
    from below in \p *this.

    \param expr
    The linear expression to test;

    \param method_call
    The call description of the public parent method, for example
    "bounded_from_above(e)".  Passed to throw_dimension_incompatible,
    as the first argument.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds(const Linear_Expression& expr, const char* method_call) const;

  //! Maximizes or minimizes \p expr subject to \p *this.
  /*!
    \param expr
    The linear expression to be maximized or minimized subject to \p
    *this;

    \param method_call
    The call description of the public parent method, for example
    "maximize(e)".  Passed to throw_dimension_incompatible, as the
    first argument;

    \param ext_n
    The numerator of the extremum value;

    \param ext_d
    The denominator of the extremum value;

    \param included
    <CODE>true</CODE> if and only if the extremum of \p expr in \p
    *this can actually be reached (which is always the case);

    \param point
    When maximization or minimization succeeds, will be assigned the
    point where \p expr reaches the extremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded in the appropriate
    direction, <CODE>false</CODE> is returned and \p ext_n, \p ext_d,
    \p included and \p point are left untouched.
  */
  bool max_min(const Linear_Expression& expr,
               const char* method_call,
               Coefficient& ext_n, Coefficient& ext_d, bool& included,
               Generator* point = NULL) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty and
    \ref Grid_Frequency "frequency" for \p *this with respect to \p expr
    is defined, in which case the frequency and the value for \p expr
    that is closest to zero are computed.

    \param expr
    The linear expression for which the frequency is needed;

    \param freq_n
    The numerator of the maximum frequency of \p expr;

    \param freq_d
    The denominator of the maximum frequency of \p expr;

    \param val_n
    The numerator of a value of \p expr at a point in the grid
    that is closest to zero;

    \param val_d
    The denominator of a value of \p expr at a point in the grid
    that is closest to zero;

    If \p *this is empty or frequency is undefined with respect to \p expr,
    then <CODE>false</CODE> is returned and \p freq_n, \p freq_d,
    \p val_n and \p val_d are left untouched.

    \warning
    If \p expr and \p *this are dimension-incompatible,
    the grid generator system is not minimized or \p *this is
    empty, then the behavior is undefined.
  */
  bool frequency_no_check(const Linear_Expression& expr,
                Coefficient& freq_n, Coefficient& freq_d,
                Coefficient& val_n, Coefficient& val_d) const;

  //! Checks if and how \p expr is bounded in \p *this.
  /*!
    Returns <CODE>true</CODE> if and only if \p from_above is
    <CODE>true</CODE> and \p expr is bounded from above in \p *this,
    or \p from_above is <CODE>false</CODE> and \p expr is bounded
    from below in \p *this.

    \param expr
    The linear expression to test;
  */
  bool bounds_no_check(const Linear_Expression& expr) const;

  /*! \brief
    Adds the congruence \p cg to \p *this.

    \warning
    If \p cg and \p *this are dimension-incompatible,
    the grid generator system is not minimized or \p *this is
    empty, then the behavior is undefined.
  */
  void add_congruence_no_check(const Congruence& cg);

  /*! \brief
    Uses the constraint \p c to refine \p *this.

    \param c
    The constraint to be added.

    \exception std::invalid_argument
    Thrown if c is a non-trivial inequality constraint.

    \warning
    If \p c and \p *this are dimension-incompatible,
    the behavior is undefined.
  */
  void add_constraint_no_check(const Constraint& c);

  /*! \brief
    Uses the constraint \p c to refine \p *this.

    \param c
    The constraint to be added.
    Non-trivial inequalities are ignored.

    \warning
    If \p c and \p *this are dimension-incompatible,
    the behavior is undefined.
  */
  void refine_no_check(const Constraint& c);

  //! \name Widening- and Extrapolation-Related Functions
  //@{

  //! Copies a widened selection of congruences from \p y to \p selected_cgs.
  void select_wider_congruences(const Grid& y,
                                Congruence_System& selected_cgs) const;

  //! Copies widened generators from \p y to \p widened_ggs.
  void select_wider_generators(const Grid& y,
                               Grid_Generator_System& widened_ggs) const;

  //@} // Widening- and Extrapolation-Related Functions

  //! Adds new space dimensions to the given systems.
  /*!
    \param cgs
    A congruence system, to which columns are added;

    \param gs
    A generator system, to which rows and columns are added;

    \param dims
    The number of space dimensions to add.

    This method is invoked only by
    <CODE>add_space_dimensions_and_embed()</CODE>.
  */
  void add_space_dimensions(Congruence_System& cgs,
                            Grid_Generator_System& gs,
                            dimension_type dims);

  //! Adds new space dimensions to the given systems.
  /*!
    \param gs
    A generator system, to which columns are added;

    \param cgs
    A congruence system, to which rows and columns are added;

    \param dims
    The number of space dimensions to add.

    This method is invoked only by
    <CODE>add_space_dimensions_and_project()</CODE>.
  */
  void add_space_dimensions(Grid_Generator_System& gs,
                            Congruence_System& cgs,
                            dimension_type dims);

  //! \name Minimization-related Static Member Functions
  //@{

  //! Normalizes the divisors in \p sys.
  /*!
    Converts \p sys to an equivalent system in which the divisors are
    of equal value.

    \param sys
    The generator system to be normalized.  It must have at least one
    row.

    \param divisor
    A reference to the initial value of the divisor.  The resulting
    value of this object is the new system divisor.

    \param first_point
    If \p first_point has a value other than NULL then it is taken as
    the first point in \p sys, and it is assumed that any following
    points have the same divisor as \p first_point.
  */
  static void
  normalize_divisors(Grid_Generator_System& sys,
                     Coefficient& divisor,
                     const Grid_Generator* first_point = NULL);

  //! Normalizes the divisors in \p sys.
  /*!
    Converts \p sys to an equivalent system in which the divisors are
    of equal value.

    \param sys
    The generator system to be normalized.  It must have at least one
    row.
  */
  static void
  normalize_divisors(Grid_Generator_System& sys);

  //! Normalize all the divisors in \p sys and \p gen_sys.
  /*!
    Modify \p sys and \p gen_sys to use the same single divisor value
    for all generators, leaving each system representing the grid it
    represented originally.

    \param sys
    The first of the generator systems to be normalized.

    \param gen_sys
    The second of the generator systems to be normalized.  This system
    must have at least one row and the divisors of the generators in
    this system must be equal.

    \exception std::runtime_error
    Thrown if all rows in \p gen_sys are lines and/or parameters.
  */
  static void normalize_divisors(Grid_Generator_System& sys,
                                 Grid_Generator_System& gen_sys);

  /*! \brief
    Converts generator system \p dest to be equivalent to congruence
    system \p source.
  */
  static void conversion(Congruence_System& source,
                         Grid_Generator_System& dest,
                         Dimension_Kinds& dim_kinds);

  /*! \brief
    Converts congruence system \p dest to be equivalent to generator
    system \p source.
  */
  static void conversion(Grid_Generator_System& source,
                         Congruence_System& dest,
                         Dimension_Kinds& dim_kinds);

  //! Converts \p cgs to upper triangular (i.e. minimized) form.
  /*!
    Returns <CODE>true</CODE> if \p cgs represents the empty set,
    otherwise returns <CODE>false</CODE>.
  */
  static bool simplify(Congruence_System& cgs,
                       Dimension_Kinds& dim_kinds);

  //! Converts \p gs to lower triangular (i.e. minimized) form.
  /*!
    Expects \p gs to contain at least one point.
  */
  static void simplify(Grid_Generator_System& ggs,
                       Dimension_Kinds& dim_kinds);

  //! Reduces the line \p row using the line \p pivot.
  /*!
    Uses the line \p pivot to change the representation of the line
    \p row so that the element at index \p column of \p row is zero.
  */
  // A member of Grid for access to Matrix<Dense_Row>::rows.
  static void reduce_line_with_line(Grid_Generator& row,
                                    Grid_Generator& pivot,
                                    dimension_type column);

  //! Reduces the equality \p row using the equality \p pivot.
  /*!
    Uses the equality \p pivot to change the representation of the
    equality \p row so that the element at index \p column of \p row
    is zero.
  */
  // A member of Grid for access to Matrix<Dense_Row>::rows.
  static void reduce_equality_with_equality(Congruence& row,
                                            const Congruence& pivot,
                                            dimension_type column);

  //! Reduces \p row using \p pivot.
  /*!
    Uses the point, parameter or proper congruence at \p pivot to
    change the representation of the point, parameter or proper
    congruence at \p row so that the element at index \p column of \p row
    is zero.  Only elements from index \p start to index \p end are
    modified (i.e. it is assumed that all other elements are zero).
    This means that \p col must be in [start,end).

    NOTE: This may invalidate the rows, since it messes with the divisors.
    Client code has to fix that (if needed) and assert OK().
  */
  // Part of Grid for access to Matrix<Dense_Row>::rows.
  template <typename R>
  static void reduce_pc_with_pc(R& row,
                                R& pivot,
                                dimension_type column,
                                dimension_type start,
                                dimension_type end);

  //! Reduce \p row using \p pivot.
  /*!
    Use the line \p pivot to change the representation of the
    parameter \p row such that the element at index \p column of \p row
    is zero.
  */
  // This takes a parameter with type Swapping_Vector<Grid_Generator> (instead
  // of Grid_Generator_System) to simplify the implementation of `simplify()'.
  // NOTE: This may invalidate `row' and the rows in `sys'. Client code must
  // fix/check this.
  static void reduce_parameter_with_line(Grid_Generator& row,
                                         const Grid_Generator& pivot,
                                         dimension_type column,
                                         Swapping_Vector<Grid_Generator>& sys,
                                         dimension_type num_columns);

  //! Reduce \p row using \p pivot.
  /*!
    Use the equality \p pivot to change the representation of the
    congruence \p row such that element at index \p column of \p row
    is zero.
  */
  // A member of Grid for access to Matrix<Dense_Row>::rows.
  // This takes a parameter with type Swapping_Vector<Congruence> (instead of
  // Congruence_System) to simplify the implementation of `conversion()'.
  static void reduce_congruence_with_equality(Congruence& row,
                                              const Congruence& pivot,
                                              dimension_type column,
                                              Swapping_Vector<Congruence>& sys);

  //! Reduce column \p dim in rows preceding \p pivot_index in \p sys.
  /*!
    Required when converting (or simplifying) a congruence or generator
    system to "strong minimal form"; informally, strong minimal form means
    that, not only is the system in minimal form (ie a triangular matrix),
    but also the absolute values of the coefficients of the proper congruences
    and parameters are minimal. As a simple example, the set of congruences
    \f$\{3x \equiv_3 0, 4x + y \equiv_3 1\}\f$,
    (which is in minimal form) is equivalent to the set
    \f$\{3x \equiv_3 0, x + y \equiv_3 1\}\f$
    (which is in strong minimal form).

    \param sys
    The generator or congruence system to be reduced to strong minimal form.

    \param dim
    Column to be reduced.

    \param pivot_index
    Index of last row to be reduced.

    \param start
    Index of first column to be changed.

    \param end
    Index of last column to be changed.

    \param sys_dim_kinds
    Dimension kinds of the elements of \p sys.

    \param generators
    Flag indicating whether \p sys is a congruence or generator system
  */
  template <typename M>
  // This takes a parameter with type `Swapping_Vector<M::row_type>'
  // instead of `M' to simplify the implementation of simplify().
  // NOTE: This may invalidate the rows in `sys'. Client code must
  // fix/check this.
  static void reduce_reduced(Swapping_Vector<typename M::row_type>& sys,
                             dimension_type dim,
                             dimension_type pivot_index,
                             dimension_type start, dimension_type end,
                             const Dimension_Kinds& sys_dim_kinds,
                             bool generators = true);

  //! Multiply the elements of \p dest by \p multiplier.
  // A member of Grid for access to Matrix<Dense_Row>::rows and cgs::operator[].
  // The type of `dest' is Swapping_Vector<Congruence> instead of
  // Congruence_System to simplify the implementation of conversion().
  static void multiply_grid(const Coefficient& multiplier,
                            Congruence& cg,
                            Swapping_Vector<Congruence>& dest,
                            dimension_type num_rows);

  //! Multiply the elements of \p dest by \p multiplier.
  // A member of Grid for access to Grid_Generator::operator[].
  // The type of `dest' is Swapping_Vector<Grid_Generator> instead of
  // Grid_Generator_System to simplify the implementation of conversion().
  // NOTE: This does not check whether the rows are OK(). Client code
  // should do that.
  static void multiply_grid(const Coefficient& multiplier,
                            Grid_Generator& gen,
                            Swapping_Vector<Grid_Generator>& dest,
                            dimension_type num_rows);

  /*! \brief
    If \p sys is lower triangular return <CODE>true</CODE>, else
    return <CODE>false</CODE>.
  */
  static bool lower_triangular(const Congruence_System& sys,
                               const Dimension_Kinds& dim_kinds);

  /*! \brief
    If \p sys is upper triangular return <CODE>true</CODE>, else
    return <CODE>false</CODE>.
  */
  static bool upper_triangular(const Grid_Generator_System& sys,
                               const Dimension_Kinds& dim_kinds);

#ifndef NDEBUG
  //! Checks that trailing rows contain only zero terms.
  /*!
    If all columns contain zero in the rows of \p system from row
    index \p first to row index \p last then return <code>true</code>,
    else return <code>false</code>.  \p row_size gives the number of
    columns in each row.

    This method is only used in assertions in the simplify methods.
  */
  template <typename M, typename R>
  static bool rows_are_zero(M& system,
                            dimension_type first,
                            dimension_type last,
                            dimension_type row_size);
#endif

  //@} // Minimization-Related Static Member Functions

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! \name Exception Throwers
  //@{
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
protected:
  void throw_dimension_incompatible(const char* method,
                                    const char* other_name,
                                    dimension_type other_dim) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* gr_name,
                                    const Grid& gr) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* le_name,
                                    const Linear_Expression& le) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* cg_name,
                                    const Congruence& cg) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* c_name,
                                    const Constraint& c) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* g_name,
                                    const Grid_Generator& g) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* g_name,
                                    const Generator& g) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* cgs_name,
                                    const Congruence_System& cgs) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* cs_name,
                                    const Constraint_System& cs) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* gs_name,
                                    const Grid_Generator_System& gs) const;
  void throw_dimension_incompatible(const char* method,
                                    const char* var_name,
                                    Variable var) const;
  void throw_dimension_incompatible(const char* method,
                                    dimension_type required_space_dim) const;

  static void throw_invalid_argument(const char* method,
                                     const char* reason);
  static void throw_invalid_constraint(const char* method,
                                       const char* c_name);
  static void throw_invalid_constraints(const char* method,
                                        const char* cs_name);
  static void throw_invalid_generator(const char* method,
                                      const char* g_name);
  static void throw_invalid_generators(const char* method,
                                       const char* gs_name);
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //@} // Exception Throwers
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)

};

/* Automatically generated from PPL source file ../src/Grid_Status_inlines.hh line 1. */
/* Grid::Status class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline
Grid::Status::Status(flags_t mask)
  : flags(mask) {
}

inline
Grid::Status::Status()
  : flags(ZERO_DIM_UNIV) {
}

inline bool
Grid::Status::test_all(flags_t mask) const {
  return (flags & mask) == mask;
}

inline bool
Grid::Status::test_any(flags_t mask) const {
  return (flags & mask) != 0;
}

inline void
Grid::Status::set(flags_t mask) {
  flags |= mask;
}

inline void
Grid::Status::reset(flags_t mask) {
  flags &= ~mask;
}

inline bool
Grid::Status::test_zero_dim_univ() const {
  return flags == ZERO_DIM_UNIV;
}

inline void
Grid::Status::reset_zero_dim_univ() {
  // This is a no-op if the current status is not zero-dim.
  if (flags == ZERO_DIM_UNIV) {
    // In the zero-dim space, if it is not the universe it is empty.
    flags = EMPTY;
  }
}

inline void
Grid::Status::set_zero_dim_univ() {
  // Zero-dim universe is incompatible with anything else.
  flags = ZERO_DIM_UNIV;
}

inline bool
Grid::Status::test_empty() const {
  return test_any(EMPTY);
}

inline void
Grid::Status::reset_empty() {
  reset(EMPTY);
}

inline void
Grid::Status::set_empty() {
  flags = EMPTY;
}

inline bool
Grid::Status::test_c_up_to_date() const {
  return test_any(C_UP_TO_DATE);
}

inline void
Grid::Status::reset_c_up_to_date() {
  reset(C_UP_TO_DATE);
}

inline void
Grid::Status::set_c_up_to_date() {
  set(C_UP_TO_DATE);
}

inline bool
Grid::Status::test_g_up_to_date() const {
  return test_any(G_UP_TO_DATE);
}

inline void
Grid::Status::reset_g_up_to_date() {
  reset(G_UP_TO_DATE);
}

inline void
Grid::Status::set_g_up_to_date() {
  set(G_UP_TO_DATE);
}

inline bool
Grid::Status::test_c_minimized() const {
  return test_any(C_MINIMIZED);
}

inline void
Grid::Status::reset_c_minimized() {
  reset(C_MINIMIZED);
}

inline void
Grid::Status::set_c_minimized() {
  set(C_MINIMIZED);
}

inline bool
Grid::Status::test_g_minimized() const {
  return test_any(G_MINIMIZED);
}

inline void
Grid::Status::reset_g_minimized() {
  reset(G_MINIMIZED);
}

inline void
Grid::Status::set_g_minimized() {
  set(G_MINIMIZED);
}


inline bool
Grid::Status::test_c_pending() const {
  return test_any(CS_PENDING);
}

inline void
Grid::Status::reset_c_pending() {
  reset(CS_PENDING);
}

inline void
Grid::Status::set_c_pending() {
  set(CS_PENDING);
}

inline bool
Grid::Status::test_g_pending() const {
  return test_any(GS_PENDING);
}

inline void
Grid::Status::reset_g_pending() {
  reset(GS_PENDING);
}

inline void
Grid::Status::set_g_pending() {
  set(GS_PENDING);
}


inline bool
Grid::Status::test_sat_c_up_to_date() const {
  return test_any(SAT_C_UP_TO_DATE);
}

inline void
Grid::Status::reset_sat_c_up_to_date() {
  reset(SAT_C_UP_TO_DATE);
}

inline void
Grid::Status::set_sat_c_up_to_date() {
  set(SAT_C_UP_TO_DATE);
}

inline bool
Grid::Status::test_sat_g_up_to_date() const {
  return test_any(SAT_G_UP_TO_DATE);
}

inline void
Grid::Status::reset_sat_g_up_to_date() {
  reset(SAT_G_UP_TO_DATE);
}

inline void
Grid::Status::set_sat_g_up_to_date() {
  set(SAT_G_UP_TO_DATE);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Grid_inlines.hh line 1. */
/* Grid class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Grid_inlines.hh line 30. */
#include <algorithm>

namespace Parma_Polyhedra_Library {

inline bool
Grid::marked_empty() const {
  return status.test_empty();
}

inline bool
Grid::congruences_are_up_to_date() const {
  return status.test_c_up_to_date();
}

inline bool
Grid::generators_are_up_to_date() const {
  return status.test_g_up_to_date();
}

inline bool
Grid::congruences_are_minimized() const {
  return status.test_c_minimized();
}

inline bool
Grid::generators_are_minimized() const {
  return status.test_g_minimized();
}

inline void
Grid::set_generators_up_to_date() {
  status.set_g_up_to_date();
}

inline void
Grid::set_congruences_up_to_date() {
  status.set_c_up_to_date();
}

inline void
Grid::set_congruences_minimized() {
  set_congruences_up_to_date();
  status.set_c_minimized();
}

inline void
Grid::set_generators_minimized() {
  set_generators_up_to_date();
  status.set_g_minimized();
}

inline void
Grid::clear_empty() {
  status.reset_empty();
}

inline void
Grid::clear_congruences_minimized() {
  status.reset_c_minimized();
}

inline void
Grid::clear_generators_minimized() {
  status.reset_g_minimized();
}

inline void
Grid::clear_congruences_up_to_date() {
  clear_congruences_minimized();
  status.reset_c_up_to_date();
  // Can get rid of con_sys here.
}

inline void
Grid::clear_generators_up_to_date() {
  clear_generators_minimized();
  status.reset_g_up_to_date();
  // Can get rid of gen_sys here.
}

inline dimension_type
Grid::max_space_dimension() {
  // One dimension is reserved to have a value of type dimension_type
  // that does not represent a legal dimension.
  return std::min(std::numeric_limits<dimension_type>::max() - 1,
                  std::min(Congruence_System::max_space_dimension(),
                           Grid_Generator_System::max_space_dimension()
                           )
                  );
}

inline
Grid::Grid(dimension_type num_dimensions,
           const Degenerate_Element kind)
  : con_sys(),
    gen_sys(check_space_dimension_overflow(num_dimensions,
                                           max_space_dimension(),
                                           "PPL::Grid::",
                                           "Grid(n, k)",
                                           "n exceeds the maximum "
                                           "allowed space dimension")) {
  construct(num_dimensions, kind);
  PPL_ASSERT(OK());
}

inline
Grid::Grid(const Congruence_System& cgs)
  : con_sys(check_space_dimension_overflow(cgs.space_dimension(),
                                           max_space_dimension(),
                                           "PPL::Grid::",
                                           "Grid(cgs)",
                                           "the space dimension of cgs "
                                           "exceeds the maximum allowed "
                                           "space dimension")),
    gen_sys(cgs.space_dimension()) {
  Congruence_System cgs_copy(cgs);
  construct(cgs_copy);
}

inline
Grid::Grid(Congruence_System& cgs, Recycle_Input)
  : con_sys(check_space_dimension_overflow(cgs.space_dimension(),
                                           max_space_dimension(),
                                           "PPL::Grid::",
                                           "Grid(cgs, recycle)",
                                           "the space dimension of cgs "
                                           "exceeds the maximum allowed "
                                           "space dimension")),
    gen_sys(cgs.space_dimension()) {
  construct(cgs);
}

inline
Grid::Grid(const Grid_Generator_System& ggs)
  : con_sys(check_space_dimension_overflow(ggs.space_dimension(),
                                           max_space_dimension(),
                                           "PPL::Grid::",
                                           "Grid(ggs)",
                                           "the space dimension of ggs "
                                           "exceeds the maximum allowed "
                                           "space dimension")),
    gen_sys(ggs.space_dimension()) {
  Grid_Generator_System ggs_copy(ggs);
  construct(ggs_copy);
}

inline
Grid::Grid(Grid_Generator_System& ggs, Recycle_Input)
  : con_sys(check_space_dimension_overflow(ggs.space_dimension(),
                                           max_space_dimension(),
                                           "PPL::Grid::",
                                           "Grid(ggs, recycle)",
                                           "the space dimension of ggs "
                                           "exceeds the maximum allowed "
                                           "space dimension")),
    gen_sys(ggs.space_dimension()) {
  construct(ggs);
}

template <typename U>
inline
Grid::Grid(const BD_Shape<U>& bd, Complexity_Class)
  : con_sys(check_space_dimension_overflow(bd.space_dimension(),
                                           max_space_dimension(),
                                           "PPL::Grid::",
                                           "Grid(bd)",
                                           "the space dimension of bd "
                                           "exceeds the maximum allowed "
                                           "space dimension")),
    gen_sys(bd.space_dimension()) {
  Congruence_System cgs = bd.congruences();
  construct(cgs);
}

template <typename U>
inline
Grid::Grid(const Octagonal_Shape<U>& os, Complexity_Class)
  : con_sys(check_space_dimension_overflow(os.space_dimension(),
                                           max_space_dimension(),
                                           "PPL::Grid::",
                                           "Grid(os)",
                                           "the space dimension of os "
                                           "exceeds the maximum allowed "
                                           "space dimension")),
    gen_sys(os.space_dimension()) {
  Congruence_System cgs = os.congruences();
  construct(cgs);
}

inline
Grid::~Grid() {
}

inline dimension_type
Grid::space_dimension() const {
  return space_dim;
}

inline memory_size_type
Grid::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

inline int32_t
Grid::hash_code() const {
  return hash_code_from_dimension(space_dimension());
}

inline Constraint_System
Grid::constraints() const {
  return Constraint_System(congruences());
}

inline Constraint_System
Grid::minimized_constraints() const {
  return Constraint_System(minimized_congruences());
}

inline void
Grid::m_swap(Grid& y) {
  using std::swap;
  swap(con_sys, y.con_sys);
  swap(gen_sys, y.gen_sys);
  swap(status, y.status);
  swap(space_dim, y.space_dim);
  swap(dim_kinds, y.dim_kinds);
}

inline void
Grid::add_congruence(const Congruence& cg) {
  // Dimension-compatibility check.
  if (space_dim < cg.space_dimension()) {
    throw_dimension_incompatible("add_congruence(cg)", "cg", cg);
  }

  if (!marked_empty()) {
    add_congruence_no_check(cg);
  }
}

inline void
Grid::add_congruences(const Congruence_System& cgs) {
  // TODO: this is just an executable specification.
  // Space dimension compatibility check.
  if (space_dim < cgs.space_dimension()) {
    throw_dimension_incompatible("add_congruences(cgs)", "cgs", cgs);
  }

  if (!marked_empty()) {
    Congruence_System cgs_copy = cgs;
    add_recycled_congruences(cgs_copy);
  }
}

inline void
Grid::refine_with_congruence(const Congruence& cg) {
  add_congruence(cg);
}

inline void
Grid::refine_with_congruences(const Congruence_System& cgs) {
  add_congruences(cgs);
}

inline bool
Grid::can_recycle_constraint_systems() {
  return true;
}

inline bool
Grid::can_recycle_congruence_systems() {
  return true;
}

inline void
Grid::add_constraint(const Constraint& c) {
  // Space dimension compatibility check.
  if (space_dim < c.space_dimension()) {
    throw_dimension_incompatible("add_constraint(c)", "c", c);
  }
  if (!marked_empty()) {
    add_constraint_no_check(c);
  }
}

inline void
Grid::add_recycled_constraints(Constraint_System& cs) {
  // TODO: really recycle the constraints.
  add_constraints(cs);
}

inline bool
Grid::bounds_from_above(const Linear_Expression& expr) const {
  return bounds(expr, "bounds_from_above(e)");
}

inline bool
Grid::bounds_from_below(const Linear_Expression& expr) const {
  return bounds(expr, "bounds_from_below(e)");
}

inline bool
Grid::maximize(const Linear_Expression& expr,
               Coefficient& sup_n, Coefficient& sup_d, bool& maximum) const {
  return max_min(expr, "maximize(e, ...)", sup_n, sup_d, maximum);
}

inline bool
Grid::maximize(const Linear_Expression& expr,
               Coefficient& sup_n, Coefficient& sup_d, bool& maximum,
               Generator& point) const {
  return max_min(expr, "maximize(e, ...)", sup_n, sup_d, maximum, &point);
}

inline bool
Grid::minimize(const Linear_Expression& expr,
               Coefficient& inf_n, Coefficient& inf_d, bool& minimum) const {
  return max_min(expr, "minimize(e, ...)", inf_n, inf_d, minimum);
}

inline bool
Grid::minimize(const Linear_Expression& expr,
               Coefficient& inf_n, Coefficient& inf_d, bool& minimum,
               Generator& point) const {
  return max_min(expr, "minimize(e, ...)", inf_n, inf_d, minimum, &point);
}

inline void
Grid::normalize_divisors(Grid_Generator_System& sys) {
  PPL_DIRTY_TEMP_COEFFICIENT(divisor);
  divisor = 1;
  normalize_divisors(sys, divisor);
}

/*! \relates Grid */
inline bool
operator!=(const Grid& x, const Grid& y) {
  return !(x == y);
}

inline bool
Grid::strictly_contains(const Grid& y) const {
  const Grid& x = *this;
  return x.contains(y) && !y.contains(x);
}

inline void
Grid::topological_closure_assign() {
}

/*! \relates Grid */
inline void
swap(Grid& x, Grid& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Grid_templates.hh line 1. */
/* Grid class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Grid_templates.hh line 30. */
#include <algorithm>
#include <deque>

namespace Parma_Polyhedra_Library {

template <typename Interval>
Grid::Grid(const Box<Interval>& box, Complexity_Class)
  : con_sys(),
    gen_sys() {
  space_dim = check_space_dimension_overflow(box.space_dimension(),
                                             max_space_dimension(),
                                             "PPL::Grid::",
                                             "Grid(box, from_bounding_box)",
                                             "the space dimension of box "
                                             "exceeds the maximum allowed "
                                             "space dimension");

  if (box.is_empty()) {
    // Empty grid.
    set_empty();
    PPL_ASSERT(OK());
    return;
  }

  if (space_dim == 0) {
    set_zero_dim_univ();
  }
  else {
    // Initialize the space dimension as indicated by the box.
    con_sys.set_space_dimension(space_dim);
    gen_sys.set_space_dimension(space_dim);
    // Add congruences and generators according to `box'.
    PPL_DIRTY_TEMP_COEFFICIENT(l_n);
    PPL_DIRTY_TEMP_COEFFICIENT(l_d);
    PPL_DIRTY_TEMP_COEFFICIENT(u_n);
    PPL_DIRTY_TEMP_COEFFICIENT(u_d);
    gen_sys.insert(grid_point());
    for (dimension_type k = space_dim; k-- > 0; ) {
      const Variable v_k = Variable(k);
      bool closed = false;
      // TODO: Consider producing the system(s) in minimized form.
      if (box.has_lower_bound(v_k, l_n, l_d, closed)) {
        if (box.has_upper_bound(v_k, u_n, u_d, closed)) {
          if (l_n * u_d == u_n * l_d) {
            // A point interval sets dimension k of every point to a
            // single value.
            con_sys.insert(l_d * v_k == l_n);

            // This is declared here because it may be invalidated
            // by the call to gen_sys.insert() at the end of the loop.
            Grid_Generator& point = gen_sys.sys.rows[0];

            // Scale the point to use as divisor the lcm of the
            // divisors of the existing point and the lower bound.
            const Coefficient& point_divisor = point.divisor();
            gcd_assign(u_n, l_d, point_divisor);
            // `u_n' now holds the gcd.
            exact_div_assign(u_n, point_divisor, u_n);
            if (l_d < 0) {
              neg_assign(u_n);
            }
            // l_d * u_n == abs(l_d * (point_divisor / gcd(l_d, point_divisor)))
            point.scale_to_divisor(l_d * u_n);
            // Set dimension k of the point to the lower bound.
            if (l_d < 0) {
              neg_assign(u_n);
            }
            // point[k + 1] = l_n * point_divisor / gcd(l_d, point_divisor)
            point.expr.set(Variable(k), l_n * u_n);
            PPL_ASSERT(point.OK());

            PPL_ASSERT(gen_sys.sys.OK());

            continue;
          }
        }
      }
      // A universe interval allows any value in dimension k.
      gen_sys.insert(grid_line(v_k));
    }
    set_congruences_up_to_date();
    set_generators_up_to_date();
  }

  PPL_ASSERT(OK());
}

template <typename Partial_Function>
void
Grid::map_space_dimensions(const Partial_Function& pfunc) {
  if (space_dim == 0) {
    return;
  }

  if (pfunc.has_empty_codomain()) {
    // All dimensions vanish: the grid becomes zero_dimensional.
    if (marked_empty()
        || (!generators_are_up_to_date() && !update_generators())) {
      // Removing all dimensions from the empty grid.
      space_dim = 0;
      set_empty();
    }
    else {
      // Removing all dimensions from a non-empty grid.
      set_zero_dim_univ();
    }

    PPL_ASSERT(OK());
    return;
  }

  dimension_type new_space_dimension = pfunc.max_in_codomain() + 1;

  if (new_space_dimension == space_dim) {
    // The partial function `pfunc' is indeed total and thus specifies
    // a permutation, that is, a renaming of the dimensions.  For
    // maximum efficiency, we will simply permute the columns of the
    // constraint system and/or the generator system.

    std::vector<Variable> cycle;
    cycle.reserve(space_dim);

    // Used to mark elements as soon as they are inserted in a cycle.
    std::deque<bool> visited(space_dim);

    for (dimension_type i = space_dim; i-- > 0; ) {
      if (!visited[i]) {
        dimension_type j = i;
        do {
          visited[j] = true;
          // The following initialization is only to make the compiler happy.
          dimension_type k = 0;
          if (!pfunc.maps(j, k)) {
            throw_invalid_argument("map_space_dimensions(pfunc)",
                                   " pfunc is inconsistent");
          }
          if (k == j) {
            break;
          }

          cycle.push_back(Variable(j));
          // Go along the cycle.
          j = k;
        } while (!visited[j]);

        // End of cycle.

        // Avoid calling clear_*_minimized() if cycle.size() is less than 2,
        // to improve efficiency.
        if (cycle.size() >= 2) {
          // Permute all that is up-to-date.
          if (congruences_are_up_to_date()) {
            con_sys.permute_space_dimensions(cycle);
            clear_congruences_minimized();
          }

          if (generators_are_up_to_date()) {
            gen_sys.permute_space_dimensions(cycle);
            clear_generators_minimized();
          }
        }

        cycle.clear();
      }
    }

    PPL_ASSERT(OK());
    return;
  }

  // If control gets here, then `pfunc' is not a permutation and some
  // dimensions must be projected away.

  const Grid_Generator_System& old_gensys = grid_generators();

  if (old_gensys.has_no_rows()) {
    // The grid is empty.
    Grid new_grid(new_space_dimension, EMPTY);
    m_swap(new_grid);
    PPL_ASSERT(OK());
    return;
  }

  // Make a local copy of the partial function.
  std::vector<dimension_type> pfunc_maps(space_dim, not_a_dimension());
  for (dimension_type j = space_dim; j-- > 0; ) {
    dimension_type pfunc_j;
    if (pfunc.maps(j, pfunc_j)) {
      pfunc_maps[j] = pfunc_j;
    }
  }

  Grid_Generator_System new_gensys;
  // Set sortedness, for the assertion met via gs::insert.
  new_gensys.set_sorted(false);
  // Get the divisor of the first point.
  Grid_Generator_System::const_iterator i;
  Grid_Generator_System::const_iterator old_gensys_end = old_gensys.end();
  for (i = old_gensys.begin(); i != old_gensys_end; ++i) {
    if (i->is_point()) {
      break;
    }
  }
  PPL_ASSERT(i != old_gensys_end);
  const Coefficient& system_divisor = i->divisor();
  for (i = old_gensys.begin(); i != old_gensys_end; ++i) {
    const Grid_Generator& old_g = *i;
    const Grid_Generator::expr_type old_g_e = old_g.expression();
    Linear_Expression expr;
    expr.set_space_dimension(new_space_dimension);
    bool all_zeroes = true;
    for (Grid_Generator::expr_type::const_iterator j = old_g_e.begin(),
          j_end = old_g_e.end(); j != j_end; ++j) {
      const dimension_type mapped_id = pfunc_maps[j.variable().id()];
      if (mapped_id != not_a_dimension()) {
        add_mul_assign(expr, *j, Variable(mapped_id));
        all_zeroes = false;
      }
    }
    switch (old_g.type()) {
    case Grid_Generator::LINE:
      if (!all_zeroes) {
        new_gensys.insert(grid_line(expr));
      }
      break;
    case Grid_Generator::PARAMETER:
      if (!all_zeroes) {
        new_gensys.insert(parameter(expr, system_divisor));
      }
      break;
    case Grid_Generator::POINT:
      new_gensys.insert(grid_point(expr, old_g.divisor()));
      break;
    }
  }

  Grid new_grid(new_gensys);
  m_swap(new_grid);

  PPL_ASSERT(OK(true));
}

// Needed for converting the congruence or grid_generator system
// to "strong minimal form".
template <typename M>
void
Grid::reduce_reduced(Swapping_Vector<typename M::row_type>& rows,
                     const dimension_type dim,
                     const dimension_type pivot_index,
                     const dimension_type start,
                     const dimension_type end,
                     const Dimension_Kinds& sys_dim_kinds,
                     const bool generators) {
  // TODO: Remove this.
  typedef typename M::row_type M_row_type;

  const M_row_type& pivot = rows[pivot_index];
  const Coefficient& pivot_dim = pivot.expr.get(dim);

  if (pivot_dim == 0) {
    return;
  }

  PPL_DIRTY_TEMP_COEFFICIENT(pivot_dim_half);
  pivot_dim_half = (pivot_dim + 1) / 2;
  const Dimension_Kind row_kind = sys_dim_kinds[dim];
  const bool row_is_line_or_equality
    = (row_kind == (generators ? LINE : EQUALITY));

  PPL_DIRTY_TEMP_COEFFICIENT(num_rows_to_subtract);
  PPL_DIRTY_TEMP_COEFFICIENT(row_dim_remainder);
  for (dimension_type kinds_index = dim,
         row_index = pivot_index; row_index-- > 0; ) {
    if (generators) {
      --kinds_index;
      // Move over any virtual rows.
      while (sys_dim_kinds[kinds_index] == GEN_VIRTUAL) {
        --kinds_index;
      }
    }
    else {
      ++kinds_index;
      // Move over any virtual rows.
      while (sys_dim_kinds[kinds_index] == CON_VIRTUAL) {
        ++kinds_index;
      }
    }

    // row_kind CONGRUENCE is included as PARAMETER
    if (row_is_line_or_equality
        || (row_kind == PARAMETER
            && sys_dim_kinds[kinds_index] == PARAMETER)) {
      M_row_type& row = rows[row_index];

      const Coefficient& row_dim = row.expr.get(dim);
      // num_rows_to_subtract may be positive or negative.
      num_rows_to_subtract = row_dim / pivot_dim;

      // Ensure that after subtracting num_rows_to_subtract * r_dim
      // from row_dim, -pivot_dim_half < row_dim <= pivot_dim_half.
      // E.g., if pivot[dim] = 9, then after this reduction
      // -5 < row_dim <= 5.
      row_dim_remainder = row_dim % pivot_dim;
      if (row_dim_remainder < 0) {
        if (row_dim_remainder <= -pivot_dim_half) {
          --num_rows_to_subtract;
        }
      }
      else if (row_dim_remainder > 0 && row_dim_remainder > pivot_dim_half) {
        ++num_rows_to_subtract;
      }
      // Subtract num_rows_to_subtract copies of pivot from row i.  Only the
      // entries from dim need to be subtracted, as the preceding
      // entries are all zero.
      // If num_rows_to_subtract is negative, these copies of pivot are
      // added to row i.
      if (num_rows_to_subtract != 0) {
        row.expr.linear_combine(pivot.expr,
                                Coefficient_one(), -num_rows_to_subtract,
                                start, end + 1);
      }
    }
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Grid_defs.hh line 2664. */

/* Automatically generated from PPL source file ../src/BD_Shape_defs.hh line 1. */
/* BD_Shape class declaration.
*/


/* Automatically generated from PPL source file ../src/DB_Matrix_defs.hh line 1. */
/* DB_Matrix class declaration.
*/


/* Automatically generated from PPL source file ../src/DB_Matrix_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename T>
class DB_Matrix;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/DB_Row_defs.hh line 1. */
/* DB_Row class declaration.
*/


/* Automatically generated from PPL source file ../src/DB_Row_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename T>
class DB_Row_Impl_Handler;

template <typename T>
class DB_Row;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Ptr_Iterator_defs.hh line 1. */
/* Ptr_Iterator class declaration.
*/


/* Automatically generated from PPL source file ../src/Ptr_Iterator_types.hh line 1. */


namespace Parma_Polyhedra_Library {

namespace Implementation {

template <typename P>
class Ptr_Iterator;

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Ptr_Iterator_defs.hh line 28. */
#include <iterator>

namespace Parma_Polyhedra_Library {

namespace Implementation {

template<typename P, typename Q>
bool operator==(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y);

template<typename P, typename Q>
bool operator!=(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y);

template<typename P, typename Q>
bool operator<(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y);

template<typename P, typename Q>
bool operator<=(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y);

template<typename P, typename Q>
bool operator>(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y);

template<typename P, typename Q>
bool operator>=(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y);

template<typename P, typename Q>
typename Ptr_Iterator<P>::difference_type
operator-(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y);

template<typename P>
Ptr_Iterator<P> operator+(typename Ptr_Iterator<P>::difference_type m,
                          const Ptr_Iterator<P>& y);

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A class to define STL const and non-const iterators from pointer types.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename P>
class Parma_Polyhedra_Library::Implementation::Ptr_Iterator
  : public std::iterator<typename std::iterator_traits<P>::iterator_category,
                         typename std::iterator_traits<P>::value_type,
                         typename std::iterator_traits<P>::difference_type,
                         typename std::iterator_traits<P>::pointer,
                         typename std::iterator_traits<P>::reference> {
public:
  typedef typename std::iterator_traits<P>::difference_type difference_type;
  typedef typename std::iterator_traits<P>::reference reference;
  typedef typename std::iterator_traits<P>::pointer pointer;

  //! Default constructor: no guarantees.
  Ptr_Iterator();

  //! Construct an iterator pointing at \p q.
  explicit Ptr_Iterator(const P& q);

  /*! \brief
    Copy constructor allowing the construction of a const_iterator
    from a non-const iterator.
  */
  template<typename Q>
  Ptr_Iterator(const Ptr_Iterator<Q>& q);

  //! Dereference operator.
  reference operator*() const;

  //! Indirect member selector.
  pointer operator->() const;

  //! Subscript operator.
  reference operator[](const difference_type m) const;

  //! Prefix increment operator.
  Ptr_Iterator& operator++();

  //! Postfix increment operator.
  Ptr_Iterator operator++(int);

  //! Prefix decrement operator
  Ptr_Iterator& operator--();

  //! Postfix decrement operator.
  Ptr_Iterator operator--(int);

  //! Assignment-increment operator.
  Ptr_Iterator& operator+=(const difference_type m);

  //! Assignment-decrement operator.
  Ptr_Iterator& operator-=(const difference_type m);

  //! Returns the difference between \p *this and \p y.
  difference_type operator-(const Ptr_Iterator& y) const;

  //! Returns the sum of \p *this and \p m.
  Ptr_Iterator operator+(const difference_type m) const;

  //! Returns the difference of \p *this and \p m.
  Ptr_Iterator operator-(const difference_type m) const;

private:
  //! The base pointer implementing the iterator.
  P p;

  //! Returns the hidden pointer.
  const P& base() const;

  template <typename Q, typename R>
  friend bool Parma_Polyhedra_Library::Implementation::
  operator==(const Ptr_Iterator<Q>& x, const Ptr_Iterator<R>& y);

  template <typename Q, typename R>
  friend bool Parma_Polyhedra_Library::Implementation::
  operator!=(const Ptr_Iterator<Q>& x, const Ptr_Iterator<R>& y);

  template<typename Q, typename R>
  friend bool Parma_Polyhedra_Library::Implementation::
  operator<(const Ptr_Iterator<Q>& x, const Ptr_Iterator<R>& y);

  template<typename Q, typename R>
  friend bool Parma_Polyhedra_Library::Implementation::
  operator<=(const Ptr_Iterator<Q>& x, const Ptr_Iterator<R>& y);

  template<typename Q, typename R>
  friend bool Parma_Polyhedra_Library::Implementation::
  operator>(const Ptr_Iterator<Q>& x, const Ptr_Iterator<R>& y);

  template<typename Q, typename R>
  friend bool Parma_Polyhedra_Library::Implementation::
  operator>=(const Ptr_Iterator<Q>& x, const Ptr_Iterator<R>& y);

  template<typename Q, typename R>
  friend typename Ptr_Iterator<Q>::difference_type
  Parma_Polyhedra_Library::Implementation::
  operator-(const Ptr_Iterator<Q>& x, const Ptr_Iterator<R>& y);

  friend Ptr_Iterator<P>
  Parma_Polyhedra_Library::Implementation::
  operator+<>(typename Ptr_Iterator<P>::difference_type m,
              const Ptr_Iterator<P>& y);
};

/* Automatically generated from PPL source file ../src/Ptr_Iterator_inlines.hh line 1. */
/* Ptr_Iterator class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

namespace Implementation {

template <typename P>
inline const P&
Ptr_Iterator<P>::base() const {
  return p;
}

template <typename P>
inline
Ptr_Iterator<P>::Ptr_Iterator()
  : p(P()) {
}

template <typename P>
inline
Ptr_Iterator<P>::Ptr_Iterator(const P& q)
  : p(q) {
}

template <typename P>
template <typename Q>
inline
Ptr_Iterator<P>::Ptr_Iterator(const Ptr_Iterator<Q>& q)
  : p(q.base()) {
}

template <typename P>
inline typename Ptr_Iterator<P>::reference
Ptr_Iterator<P>::operator*() const {
  return *p;
}

template <typename P>
inline typename Ptr_Iterator<P>::pointer
Ptr_Iterator<P>::operator->() const {
  return p;
}

template <typename P>
inline typename Ptr_Iterator<P>::reference
Ptr_Iterator<P>::operator[](const difference_type m) const {
  return p[m];
}

template <typename P>
inline Ptr_Iterator<P>&
Ptr_Iterator<P>::operator++() {
  ++p;
  return *this;
}

template <typename P>
inline Ptr_Iterator<P>
Ptr_Iterator<P>::operator++(int) {
  return Ptr_Iterator(p++);
}

template <typename P>
inline Ptr_Iterator<P>&
Ptr_Iterator<P>::operator--() {
  --p;
  return *this;
}

template <typename P>
inline Ptr_Iterator<P>
Ptr_Iterator<P>::operator--(int) {
  return Ptr_Iterator(p--);
}


template <typename P>
inline Ptr_Iterator<P>&
Ptr_Iterator<P>::operator+=(const difference_type m) {
  p += m;
  return *this;
}

template <typename P>
inline Ptr_Iterator<P>&
Ptr_Iterator<P>::operator-=(const difference_type m) {
  p -= m;
  return *this;
}

template <typename P>
inline typename Ptr_Iterator<P>::difference_type
Ptr_Iterator<P>::operator-(const Ptr_Iterator& y) const {
  return p - y.p;
}

template <typename P>
inline Ptr_Iterator<P>
Ptr_Iterator<P>::operator+(const difference_type m) const {
  return Ptr_Iterator(p + m);
}

template <typename P>
inline Ptr_Iterator<P>
Ptr_Iterator<P>::operator-(const difference_type m) const {
  return Ptr_Iterator(p - m);
}

template<typename P, typename Q>
inline bool
operator==(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y) {
  return x.base() == y.base();
}

template<typename P, typename Q>
inline bool
operator!=(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y) {
  return x.base() != y.base();
}

template<typename P, typename Q>
inline bool
operator<(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y) {
  return x.base() < y.base();
}

template<typename P, typename Q>
inline bool
operator<=(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y) {
  return x.base() <= y.base();
}

template<typename P, typename Q>
inline bool
operator>(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y) {
  return x.base() > y.base();
}

template<typename P, typename Q>
inline bool
operator>=(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y) {
  return x.base() >= y.base();
}

template<typename P, typename Q>
inline typename Ptr_Iterator<P>::difference_type
operator-(const Ptr_Iterator<P>& x, const Ptr_Iterator<Q>& y) {
  return x.base() - y.base();
}

template<typename P>
inline Ptr_Iterator<P>
operator+(typename Ptr_Iterator<P>::difference_type m,
          const Ptr_Iterator<P>& y) {
  return Ptr_Iterator<P>(m + y.base());
}

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Ptr_Iterator_defs.hh line 171. */

/* Automatically generated from PPL source file ../src/DB_Row_defs.hh line 30. */
#include <cstddef>
#include <vector>

#ifndef PPL_DB_ROW_EXTRA_DEBUG
#ifdef PPL_ABI_BREAKING_EXTRA_DEBUG
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  When PPL_DB_ROW_EXTRA_DEBUG evaluates to <CODE>true</CODE>, each instance
  of the class DB_Row carries its own capacity; this enables extra
  consistency checks to be performed.
  \ingroup PPL_CXX_interface
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
#define PPL_DB_ROW_EXTRA_DEBUG 1
#else // !defined(PPL_ABI_BREAKING_EXTRA_DEBUG)
#define PPL_DB_ROW_EXTRA_DEBUG 0
#endif // !defined(PPL_ABI_BREAKING_EXTRA_DEBUG)
#endif // !defined(PPL_DB_ROW_EXTRA_DEBUG)


#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! The handler of the actual DB_Row implementation.
/*! \ingroup PPL_CXX_interface
  Exception-safety is the only responsibility of this class: it has
  to ensure that its \p impl member is correctly deallocated.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Parma_Polyhedra_Library::DB_Row_Impl_Handler {
public:
  //! Default constructor.
  DB_Row_Impl_Handler();

  //! Destructor.
  ~DB_Row_Impl_Handler();

  class Impl;

  //! A pointer to the actual implementation.
  Impl* impl;

#if PPL_DB_ROW_EXTRA_DEBUG
  //! The capacity of \p impl (only available during debugging).
  dimension_type capacity_;
#endif // PPL_DB_ROW_EXTRA_DEBUG

private:
  //! Private and unimplemented: copy construction is not allowed.
  DB_Row_Impl_Handler(const DB_Row_Impl_Handler&);

  //! Private and unimplemented: copy assignment is not allowed.
  DB_Row_Impl_Handler& operator=(const DB_Row_Impl_Handler&);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! The base class for the single rows of matrices.
/*! \ingroup PPL_CXX_interface
  The class template DB_Row<T> allows for the efficient representation of
  the single rows of a DB_Matrix. It contains elements of type T stored
  as a vector. The class T is a family of extended numbers that
  must provide representation for
  \f$ -\infty \f$, \f$0\f$,\f$ +\infty \f$ (and, consequently for <EM>nan</EM>,
  <EM>not a number</EM>, since this arises as the ``result'' of
  undefined sums like \f$ +\infty + (-\infty) \f$).

  The class T must provide the following methods:

  \code
    T()
  \endcode
  is the default constructor: no assumption is made on the particular
  object constructed, provided <CODE>T().OK()</CODE> gives <CODE>true</CODE>
  (see below).
  \code
    ~T()
  \endcode
  is the destructor.
  \code
    bool is_nan() const
  \endcode
  returns <CODE>true</CODE> if and only \p *this represents
  the  <EM>not a number</EM> value.
  \code
    bool OK() const
  \endcode
  returns <CODE>true</CODE> if and only if \p *this satisfies all
  its invariants.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Parma_Polyhedra_Library::DB_Row : private DB_Row_Impl_Handler<T> {
public:
  //! Pre-constructs a row: construction must be completed by construct().
  DB_Row();

  //! \name Post-constructors.
  //@{
  //! Constructs properly a default-constructed element.
  /*!
    Builds a row with size \p sz and minimum capacity.
  */
  void construct(dimension_type sz);

  //! Constructs properly a default-constructed element.
  /*!
    \param sz
    The size of the row that will be constructed.

    \param capacity
    The minimum capacity of the row that will be constructed.

    The row that we are constructing has a minimum capacity of
    (i.e., it can contain at least) \p elements, \p sz of which
    will be constructed now.
  */
  void construct(dimension_type sz, dimension_type capacity);

  //! Constructs properly a conservative approximation of \p y.
  /*!
    \param y
    A row containing the elements whose upward approximations will
    be used to properly construct \p *this.

    \param capacity
    The capacity of the constructed row.

    It is assumed that \p capacity is greater than or equal to the
    size of \p y.
  */
  template <typename U>
  void construct_upward_approximation(const DB_Row<U>& y,
                                      dimension_type capacity);

  //@}

  //! Tight constructor: resizing will require reallocation.
  DB_Row(dimension_type sz);

  //! Sizing constructor with capacity.
  DB_Row(dimension_type sz, dimension_type capacity);

  //! Ordinary copy constructor.
  DB_Row(const DB_Row& y);

  //! Copy constructor with specified capacity.
  /*!
    It is assumed that \p capacity is greater than or equal to \p y size.
  */
  DB_Row(const DB_Row& y, dimension_type capacity);

  //! Copy constructor with specified size and capacity.
  /*!
    It is assumed that \p sz is greater than or equal to the size of \p y
    and, of course, that \p sz is less than or equal to \p capacity.
    Any new position is initialized to \f$+\infty\f$.
  */
  DB_Row(const DB_Row& y, dimension_type sz, dimension_type capacity);

  //! Destructor.
  ~DB_Row();

  //! Assignment operator.
  DB_Row& operator=(const DB_Row& y);

  //! Swaps \p *this with \p y.
  void m_swap(DB_Row& y);

  //! Assigns the implementation of \p y to \p *this.
  void assign(DB_Row& y);

  /*! \brief
    Allocates memory for a default constructed DB_Row object,
    allowing for \p capacity coefficients at most.

    It is assumed that no allocation has been performed before
    (otherwise, a memory leak will occur).
    After execution, the size of the DB_Row object is zero.
  */
  void allocate(dimension_type capacity);

  //! Expands the row to size \p new_size.
  /*!
    Adds new positions to the implementation of the row
    obtaining a new row with size \p new_size.
    It is assumed that \p new_size is between the current size
    and capacity of the row. The new positions are initialized
    to \f$+\infty\f$.
  */
  void expand_within_capacity(dimension_type new_size);

  //! Shrinks the row by erasing elements at the end.
  /*!
    Destroys elements of the row implementation
    from position \p new_size to the end.
    It is assumed that \p new_size is not greater than the current size.
  */
  void shrink(dimension_type new_size);

  //! Returns the size() of the largest possible DB_Row.
  static dimension_type max_size();

  //! Gives the number of coefficients currently in use.
  dimension_type size() const;

  //! \name Subscript operators.
  //@{
  //! Returns a reference to the element of the row indexed by \p k.
  T& operator[](dimension_type k);

  //! Returns a constant reference to the element of the row indexed by \p k.
  const T& operator[](dimension_type k) const;
  //@}

  //! A (non const) random access iterator to access the row's elements.
  typedef Implementation::Ptr_Iterator<T*> iterator;

  //! A const random access iterator to access the row's elements.
  typedef Implementation::Ptr_Iterator<const T*> const_iterator;

  /*! \brief
    Returns the const iterator pointing to the first element,
    if \p *this is not empty;
    otherwise, returns the past-the-end const iterator.
  */
  iterator begin();

  //! Returns the past-the-end iterator.
  iterator end();

  /*! \brief
    Returns the const iterator pointing to the first element,
    if \p *this is not empty;
    otherwise, returns the past-the-end const iterator.
  */
  const_iterator begin() const;

  //! Returns the past-the-end const iterator.
  const_iterator end() const;

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  /*! \brief
    Returns a lower bound to the size in bytes of the memory
    managed by \p *this.
  */
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns the total size in bytes of the memory occupied by \p *this,
    provided the capacity of \p *this is given by \p capacity.
  */
  memory_size_type total_memory_in_bytes(dimension_type capacity) const;

  /*! \brief
    Returns the size in bytes of the memory managed by \p *this,
    provided the capacity of \p *this is given by \p capacity.
  */
  memory_size_type external_memory_in_bytes(dimension_type capacity) const;

  //! Checks if all the invariants are satisfied.
  bool OK(dimension_type row_size, dimension_type row_capacity) const;

private:
  template <typename U> friend class Parma_Polyhedra_Library::DB_Row;

  //! Exception-safe copy construction mechanism for coefficients.
  void copy_construct_coefficients(const DB_Row& y);

#if PPL_DB_ROW_EXTRA_DEBUG
  //! Returns the capacity of the row (only available during debugging).
  dimension_type capacity() const;
#endif // PPL_DB_ROW_EXTRA_DEBUG
};

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
/*! \relates DB_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
void swap(DB_Row<T>& x, DB_Row<T>& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps objects referred by \p x and \p y.
/*! \relates DB_Row */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
void iter_swap(typename std::vector<DB_Row<T> >::iterator x,
               typename std::vector<DB_Row<T> >::iterator y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! \name Classical comparison operators.
//@{
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
/*! \relates DB_Row */
template <typename T>
bool operator==(const DB_Row<T>& x, const DB_Row<T>& y);

/*! \relates DB_Row */
template <typename T>
bool operator!=(const DB_Row<T>& x, const DB_Row<T>& y);
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//@}
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)

} // namespace Parma_Polyhedra_Library


#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! The real implementation of a DB_Row object.
/*! \ingroup PPL_CXX_interface
  The class DB_Row_Impl_Handler::Impl provides the implementation of
  DB_Row objects and, in particular, of the corresponding memory
  allocation functions.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Parma_Polyhedra_Library::DB_Row_Impl_Handler<T>::Impl {
public:
  //! \name Custom allocator and deallocator.
  //@{

  /*! \brief
    Allocates a chunk of memory able to contain \p capacity T objects
    beyond the specified \p fixed_size and returns a pointer to the new
    allocated memory.
  */
  static void* operator new(size_t fixed_size, dimension_type capacity);

  //! Uses the standard delete operator to free the memory \p p points to.
  static void operator delete(void* p);

  /*! \brief
    Placement version: uses the standard operator delete to free
    the memory \p p points to.
  */
  static void operator delete(void* p, dimension_type capacity);
  //@}

  //! Default constructor.
  Impl();

  //! Destructor.
  /*!
    Uses <CODE>shrink()</CODE> method with argument \f$0\f$
    to delete all the row elements.
  */
  ~Impl();

  //! Expands the row to size \p new_size.
  /*!
    It is assumed that \p new_size is between the current size and capacity.
  */
  void expand_within_capacity(dimension_type new_size);

  //! Shrinks the row by erasing elements at the end.
  /*!
    It is assumed that \p new_size is not greater than the current size.
  */
  void shrink(dimension_type new_size);

  //! Exception-safe copy construction mechanism for coefficients.
  void copy_construct_coefficients(const Impl& y);

  /*! \brief
    Exception-safe upward approximation construction mechanism
    for coefficients.
  */
  template <typename U>
  void construct_upward_approximation(const U& y);

  //! Returns the size() of the largest possible Impl.
  static dimension_type max_size();

  //! \name Size accessors.
  //@{
  //! Returns the actual size of \p this.
  dimension_type size() const;

  //! Sets to \p new_sz the actual size of \p *this.
  void set_size(dimension_type new_sz);

  //! Increments the size of \p *this by 1.
  void bump_size();
  //@}

  //! \name Subscript operators.
  //@{
  //! Returns a reference to the element of \p *this indexed by \p k.
  T& operator[](dimension_type k);

  //! Returns a constant reference to the element of \p *this indexed by \p k.
  const T& operator[](dimension_type k) const;
  //@}

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes(dimension_type capacity) const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

private:
  friend class DB_Row<T>;

  //! The number of coefficients in the row.
  dimension_type size_;

  //! The vector of coefficients.
  T vec_[
#if PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
          0
#else
          1
#endif
  ];

  //! Private and unimplemented: copy construction is not allowed.
  Impl(const Impl& y);

  //! Private and unimplemented: assignment is not allowed.
  Impl& operator=(const Impl&);

  //! Exception-safe copy construction mechanism.
  void copy_construct(const Impl& y);
};

/* Automatically generated from PPL source file ../src/DB_Row_inlines.hh line 1. */
/* DB_Row class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/DB_Row_inlines.hh line 29. */
#include <cstddef>
#include <limits>
#include <algorithm>
#include <iostream>

namespace Parma_Polyhedra_Library {

template <typename T>
inline void*
DB_Row_Impl_Handler<T>::Impl::operator new(const size_t fixed_size,
                                           const dimension_type capacity) {
#if PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
  return ::operator new(fixed_size + capacity*sizeof(T));
#else
  PPL_ASSERT(capacity >= 1);
  return ::operator new(fixed_size + (capacity-1)*sizeof(T));
#endif
}

template <typename T>
inline void
DB_Row_Impl_Handler<T>::Impl::operator delete(void* p) {
  ::operator delete(p);
}

template <typename T>
inline void
DB_Row_Impl_Handler<T>::Impl::operator delete(void* p, dimension_type) {
  ::operator delete(p);
}

template <typename T>
inline memory_size_type
DB_Row_Impl_Handler<T>::Impl
::total_memory_in_bytes(dimension_type capacity) const {
  return
    sizeof(*this)
    + capacity*sizeof(T)
#if !PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
    - 1*sizeof(T)
#endif
    + external_memory_in_bytes();
}

template <typename T>
inline memory_size_type
DB_Row_Impl_Handler<T>::Impl::total_memory_in_bytes() const {
  // In general, this is a lower bound, as the capacity of *this
  // may be strictly greater than `size_'
  return total_memory_in_bytes(size_);
}

template <typename T>
inline dimension_type
DB_Row_Impl_Handler<T>::Impl::max_size() {
  return std::numeric_limits<size_t>::max() / sizeof(T);
}

template <typename T>
inline dimension_type
DB_Row_Impl_Handler<T>::Impl::size() const {
  return size_;
}

template <typename T>
inline void
DB_Row_Impl_Handler<T>::Impl::set_size(const dimension_type new_sz) {
  size_ = new_sz;
}

template <typename T>
inline void
DB_Row_Impl_Handler<T>::Impl::bump_size() {
  ++size_;
}

template <typename T>
inline
DB_Row_Impl_Handler<T>::Impl::Impl()
  : size_(0) {
}

template <typename T>
inline
DB_Row_Impl_Handler<T>::Impl::~Impl() {
  shrink(0);
}

template <typename T>
inline
DB_Row_Impl_Handler<T>::DB_Row_Impl_Handler()
  : impl(0) {
#if PPL_DB_ROW_EXTRA_DEBUG
  capacity_ = 0;
#endif
}

template <typename T>
inline
DB_Row_Impl_Handler<T>::~DB_Row_Impl_Handler() {
  delete impl;
}

template <typename T>
inline T&
DB_Row_Impl_Handler<T>::Impl::operator[](const dimension_type k) {
  PPL_ASSERT(k < size());
  return vec_[k];
}

template <typename T>
inline const T&
DB_Row_Impl_Handler<T>::Impl::operator[](const dimension_type k) const {
  PPL_ASSERT(k < size());
  return vec_[k];
}

template <typename T>
inline dimension_type
DB_Row<T>::max_size() {
  return DB_Row_Impl_Handler<T>::Impl::max_size();
}

template <typename T>
inline dimension_type
DB_Row<T>::size() const {
  return this->impl->size();
}

#if PPL_DB_ROW_EXTRA_DEBUG
template <typename T>
inline dimension_type
DB_Row<T>::capacity() const {
  return this->capacity_;
}
#endif // PPL_DB_ROW_EXTRA_DEBUG

template <typename T>
inline
DB_Row<T>::DB_Row()
  : DB_Row_Impl_Handler<T>() {
}

template <typename T>
inline void
DB_Row<T>::allocate(
#if PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
               const
#endif
               dimension_type capacity) {
  DB_Row<T>& x = *this;
  PPL_ASSERT(capacity <= max_size());
#if !PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
  if (capacity == 0) {
    ++capacity;
  }
#endif
  PPL_ASSERT(x.impl == 0);
  x.impl = new(capacity) typename DB_Row_Impl_Handler<T>::Impl();
#if PPL_DB_ROW_EXTRA_DEBUG
  PPL_ASSERT(x.capacity_ == 0);
  x.capacity_ = capacity;
#endif
}

template <typename T>
inline void
DB_Row<T>::expand_within_capacity(const dimension_type new_size) {
  DB_Row<T>& x = *this;
  PPL_ASSERT(x.impl);
#if PPL_DB_ROW_EXTRA_DEBUG
  PPL_ASSERT(new_size <= x.capacity_);
#endif
  x.impl->expand_within_capacity(new_size);
}

template <typename T>
inline void
DB_Row<T>::copy_construct_coefficients(const DB_Row& y) {
  DB_Row<T>& x = *this;
  PPL_ASSERT(x.impl && y.impl);
#if PPL_DB_ROW_EXTRA_DEBUG
  PPL_ASSERT(y.size() <= x.capacity_);
#endif
  x.impl->copy_construct_coefficients(*(y.impl));
}

template <typename T>
template <typename U>
inline void
DB_Row<T>::construct_upward_approximation(const DB_Row<U>& y,
                                          const dimension_type capacity) {
  DB_Row<T>& x = *this;
  PPL_ASSERT(y.size() <= capacity && capacity <= max_size());
  allocate(capacity);
  PPL_ASSERT(y.impl);
  x.impl->construct_upward_approximation(*(y.impl));
}

template <typename T>
inline void
DB_Row<T>::construct(const dimension_type sz,
                     const dimension_type capacity) {
  PPL_ASSERT(sz <= capacity && capacity <= max_size());
  allocate(capacity);
  expand_within_capacity(sz);
}

template <typename T>
inline void
DB_Row<T>::construct(const dimension_type sz) {
  construct(sz, sz);
}

template <typename T>
inline
DB_Row<T>::DB_Row(const dimension_type sz,
                  const dimension_type capacity)
  : DB_Row_Impl_Handler<T>() {
  construct(sz, capacity);
}

template <typename T>
inline
DB_Row<T>::DB_Row(const dimension_type sz) {
  construct(sz);
}

template <typename T>
inline
DB_Row<T>::DB_Row(const DB_Row& y)
  : DB_Row_Impl_Handler<T>() {
  if (y.impl != 0) {
    allocate(compute_capacity(y.size(), max_size()));
    copy_construct_coefficients(y);
  }
}

template <typename T>
inline
DB_Row<T>::DB_Row(const DB_Row& y,
                  const dimension_type capacity)
  : DB_Row_Impl_Handler<T>() {
  PPL_ASSERT(y.impl);
  PPL_ASSERT(y.size() <= capacity && capacity <= max_size());
  allocate(capacity);
  copy_construct_coefficients(y);
}

template <typename T>
inline
DB_Row<T>::DB_Row(const DB_Row& y,
                  const dimension_type sz,
                  const dimension_type capacity)
  : DB_Row_Impl_Handler<T>() {
  PPL_ASSERT(y.impl);
  PPL_ASSERT(y.size() <= sz && sz <= capacity && capacity <= max_size());
  allocate(capacity);
  copy_construct_coefficients(y);
  expand_within_capacity(sz);
}

template <typename T>
inline
DB_Row<T>::~DB_Row() {
}

template <typename T>
inline void
DB_Row<T>::shrink(const dimension_type new_size) {
  DB_Row<T>& x = *this;
  PPL_ASSERT(x.impl);
  x.impl->shrink(new_size);
}

template <typename T>
inline void
DB_Row<T>::m_swap(DB_Row& y) {
  using std::swap;
  DB_Row<T>& x = *this;
  swap(x.impl, y.impl);
#if PPL_DB_ROW_EXTRA_DEBUG
  swap(x.capacity_, y.capacity_);
#endif
}

template <typename T>
inline void
DB_Row<T>::assign(DB_Row& y) {
  DB_Row<T>& x = *this;
  x.impl = y.impl;
#if PPL_DB_ROW_EXTRA_DEBUG
  x.capacity_ = y.capacity_;
#endif
}

template <typename T>
inline DB_Row<T>&
DB_Row<T>::operator=(const DB_Row& y) {
  DB_Row tmp(y);
  m_swap(tmp);
  return *this;
}

template <typename T>
inline T&
DB_Row<T>::operator[](const dimension_type k) {
  DB_Row<T>& x = *this;
  return (*x.impl)[k];
}

template <typename T>
inline const T&
DB_Row<T>::operator[](const dimension_type k) const {
  const DB_Row<T>& x = *this;
  return (*x.impl)[k];
}

template <typename T>
inline typename DB_Row<T>::iterator
DB_Row<T>::begin() {
  DB_Row<T>& x = *this;
  return iterator(x.impl->vec_);
}

template <typename T>
inline typename DB_Row<T>::iterator
DB_Row<T>::end() {
  DB_Row<T>& x = *this;
  return iterator(x.impl->vec_ + x.impl->size_);
}

template <typename T>
inline typename DB_Row<T>::const_iterator
DB_Row<T>::begin() const {
  const DB_Row<T>& x = *this;
  return const_iterator(x.impl->vec_);
}

template <typename T>
inline typename DB_Row<T>::const_iterator
DB_Row<T>::end() const {
  const DB_Row<T>& x = *this;
  return const_iterator(x.impl->vec_ + x.impl->size_);
}

template <typename T>
inline memory_size_type
DB_Row<T>::external_memory_in_bytes(dimension_type capacity) const {
  const DB_Row<T>& x = *this;
  return x.impl->total_memory_in_bytes(capacity);
}

template <typename T>
inline memory_size_type
DB_Row<T>::total_memory_in_bytes(dimension_type capacity) const {
  return sizeof(*this) + external_memory_in_bytes(capacity);
}

template <typename T>
inline memory_size_type
DB_Row<T>::external_memory_in_bytes() const {
  const DB_Row<T>& x = *this;
#if PPL_DB_ROW_EXTRA_DEBUG
  return x.impl->total_memory_in_bytes(x.capacity_);
#else
  return x.impl->total_memory_in_bytes();
#endif
}

template <typename T>
inline memory_size_type
DB_Row<T>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

/*! \relates DB_Row */
template <typename T>
inline bool
operator!=(const DB_Row<T>& x, const DB_Row<T>& y) {
  return !(x == y);
}

/*! \relates DB_Row */
template <typename T>
inline void
swap(DB_Row<T>& x, DB_Row<T>& y) {
  x.m_swap(y);
}

/*! \relates DB_Row */
template <typename T>
inline void
iter_swap(typename std::vector<DB_Row<T> >::iterator x,
          typename std::vector<DB_Row<T> >::iterator y) {
  swap(*x, *y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/DB_Row_templates.hh line 1. */
/* DB_Row class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/DB_Row_templates.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename T>
template <typename U>
void
DB_Row_Impl_Handler<T>::Impl::construct_upward_approximation(const U& y) {
  const dimension_type y_size = y.size();
#if PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
  // Construct in direct order: will destroy in reverse order.
  for (dimension_type i = 0; i < y_size; ++i) {
    construct(vec_[i], y[i], ROUND_UP);
    bump_size();
  }
#else // PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
  if (y_size > 0) {
    assign_r(vec_[0], y[0], ROUND_UP);
    bump_size();
    // Construct in direct order: will destroy in reverse order.
    for (dimension_type i = 1; i < y_size; ++i) {
      construct(vec_[i], y[i], ROUND_UP);
      bump_size();
    }
  }
#endif // PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
}

template <typename T>
void
DB_Row_Impl_Handler<T>::
Impl::expand_within_capacity(const dimension_type new_size) {
  PPL_ASSERT(size() <= new_size && new_size <= max_size());
#if !PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
  if (size() == 0 && new_size > 0) {
    // vec_[0] is already constructed: we just need to assign +infinity.
    assign_r(vec_[0], PLUS_INFINITY, ROUND_NOT_NEEDED);
    bump_size();
  }
#endif
  // Construct in direct order: will destroy in reverse order.
  for (dimension_type i = size(); i < new_size; ++i) {
    new(&vec_[i]) T(PLUS_INFINITY, ROUND_NOT_NEEDED);
    bump_size();
  }
}

template <typename T>
void
DB_Row_Impl_Handler<T>::Impl::shrink(dimension_type new_size) {
  const dimension_type old_size = size();
  PPL_ASSERT(new_size <= old_size);
  // Since ~T() does not throw exceptions, nothing here does.
  set_size(new_size);
#if !PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
  // Make sure we do not try to destroy vec_[0].
  if (new_size == 0) {
    ++new_size;
  }
#endif
  // We assume construction was done "forward".
  // We thus perform destruction "backward".
  for (dimension_type i = old_size; i-- > new_size; ) {
    vec_[i].~T();
  }
}

template <typename T>
void
DB_Row_Impl_Handler<T>::Impl::copy_construct_coefficients(const Impl& y) {
  const dimension_type y_size = y.size();
#if PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
  // Construct in direct order: will destroy in reverse order.
  for (dimension_type i = 0; i < y_size; ++i) {
    new(&vec_[i]) T(y.vec_[i]);
    bump_size();
  }
#else // PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
  if (y_size > 0) {
    vec_[0] = y.vec_[0];
    bump_size();
    // Construct in direct order: will destroy in reverse order.
    for (dimension_type i = 1; i < y_size; ++i) {
      new(&vec_[i]) T(y.vec_[i]);
      bump_size();
    }
  }
#endif // PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
}

template <typename T>
memory_size_type
DB_Row_Impl_Handler<T>::Impl::external_memory_in_bytes() const {
  memory_size_type n = 0;
  for (dimension_type i = size(); i-- > 0; ) {
    n += Parma_Polyhedra_Library::external_memory_in_bytes(vec_[i]);
  }
  return n;
}

template <typename T>
bool
DB_Row<T>::OK(const dimension_type row_size,
              const dimension_type
#if PPL_DB_ROW_EXTRA_DEBUG
              row_capacity
#endif
              ) const {
#ifndef NDEBUG
  using std::endl;
  using std::cerr;
#endif

  const DB_Row<T>& x = *this;
  bool is_broken = false;

#if PPL_DB_ROW_EXTRA_DEBUG
# if !PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
  if (x.capacity_ == 0) {
    cerr << "Illegal row capacity: is 0, should be at least 1"
         << endl;
    is_broken = true;
  }
  else if (x.capacity_ == 1 && row_capacity == 0)
    // This is fine.
    ;
  else
# endif // !PPL_CXX_SUPPORTS_ZERO_LENGTH_ARRAYS
  if (x.capacity_ != row_capacity) {
    cerr << "DB_Row capacity mismatch: is " << x.capacity_
         << ", should be " << row_capacity << "."
         << endl;
    is_broken = true;
  }
#endif // PPL_DB_ROW_EXTRA_DEBUG

  if (x.size() != row_size) {
#ifndef NDEBUG
    cerr << "DB_Row size mismatch: is " << x.size()
         << ", should be " << row_size << "."
         << endl;
#endif
    is_broken = true;
  }

#if PPL_DB_ROW_EXTRA_DEBUG
  if (x.capacity_ < x.size()) {
#ifndef NDEBUG
    cerr << "DB_Row is completely broken: capacity is " << x.capacity_
         << ", size is " << x.size() << "."
         << endl;
#endif
    is_broken = true;
  }
#endif // PPL_DB_ROW_EXTRA_DEBUG

  for (dimension_type i = x.size(); i-- > 0; ) {
    const T& element = x[i];
    // Not OK is bad.
    if (!element.OK()) {
      is_broken = true;
      break;
    }
    // In addition, nans should never occur.
    if (is_not_a_number(element)) {
#ifndef NDEBUG
      cerr << "Not-a-number found in DB_Row."
           << endl;
#endif
      is_broken = true;
      break;
    }
  }

  return !is_broken;
}

/*! \relates DB_Row */
template <typename T>
bool
operator==(const DB_Row<T>& x, const DB_Row<T>& y) {
  if (x.size() != y.size()) {
    return false;
  }
  for (dimension_type i = x.size(); i-- > 0; ) {
    if (x[i] != y[i]) {
      return false;
    }
  }
  return true;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/DB_Row_defs.hh line 469. */

/* Automatically generated from PPL source file ../src/DB_Matrix_defs.hh line 32. */
#include <vector>
#include <cstddef>
#include <iosfwd>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Output operator.
/*! \relates Parma_Polyhedra_Library::DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
std::ostream&
operator<<(std::ostream& s, const DB_Matrix<T>& c);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library


#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! The base class for the square matrices.
/*! \ingroup PPL_CXX_interface
  The template class DB_Matrix<T> allows for the representation of
  a square matrix of T objects.
  Each DB_Matrix<T> object can be viewed as a multiset of DB_Row<T>.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
class Parma_Polyhedra_Library::DB_Matrix {
public:
  //! Returns the maximum number of rows a DB_Matrix can handle.
  static dimension_type max_num_rows();

  //! Returns the maximum number of columns a DB_Matrix can handle.
  static dimension_type max_num_columns();

  //! Builds an empty matrix.
  /*!
    DB_Rows' size and capacity are initialized to \f$0\f$.
  */
  DB_Matrix();

  //! Builds a square matrix having the specified dimension.
  explicit DB_Matrix(dimension_type n_rows);

  //! Copy constructor.
  DB_Matrix(const DB_Matrix& y);

  //! Constructs a conservative approximation of \p y.
  template <typename U>
  explicit DB_Matrix(const DB_Matrix<U>& y);

  //! Destructor.
  ~DB_Matrix();

  //! Assignment operator.
  DB_Matrix& operator=(const DB_Matrix& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! A read-only iterator over the rows of the matrix.
  /*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  class const_iterator {
  private:
    typedef typename std::vector<DB_Row<T> >::const_iterator Iter;
    //! The const iterator on the rows' vector \p rows.
    Iter i;

  public:
    typedef std::forward_iterator_tag iterator_category;
    typedef typename std::iterator_traits<Iter>::value_type value_type;
    typedef typename std::iterator_traits<Iter>::difference_type
    difference_type;
    typedef typename std::iterator_traits<Iter>::pointer pointer;
    typedef typename std::iterator_traits<Iter>::reference reference;

    //! Default constructor.
    const_iterator();

    /*! \brief
      Builds a const iterator on the matrix starting from
      an iterator \p b on the elements of the vector \p rows.
    */
    explicit const_iterator(const Iter& b);

    //! Ordinary copy constructor.
    const_iterator(const const_iterator& y);

    //! Assignment operator.
    const_iterator& operator=(const const_iterator& y);

    //! Dereference operator.
    reference operator*() const;

    //! Indirect member selector.
    pointer operator->() const;

    //! Prefix increment operator.
    const_iterator& operator++();

    //! Postfix increment operator.
    const_iterator operator++(int);

    /*! \brief
      Returns <CODE>true</CODE> if and only if
      \p *this and \p y are identical.
    */
    bool operator==(const const_iterator& y) const;

    /*! \brief
      Returns <CODE>true</CODE> if and only if
      \p *this and \p y are different.
    */
    bool operator!=(const const_iterator& y) const;
  };

  /*! \brief
    Returns the const_iterator pointing to the first row,
    if \p *this is not empty;
    otherwise, returns the past-the-end const_iterator.
  */
  const_iterator begin() const;

  //! Returns the past-the-end const_iterator.
  const_iterator end() const;

private:
  template <typename U> friend class DB_Matrix;

  //! The rows of the matrix.
  std::vector<DB_Row<T> > rows;

  //! Size of the initialized part of each row.
  dimension_type row_size;

  /*! \brief
    Capacity allocated for each row, i.e., number of
    <CODE>long</CODE> objects that each row can contain.
  */
  dimension_type row_capacity;

public:
  //! Swaps \p *this with \p y.
  void m_swap(DB_Matrix& y);

  //! Makes the matrix grow by adding more rows and more columns.
  /*!
    \param new_n_rows
    The number of rows and columns of the resized matrix.

    A new matrix, with the specified dimension, is created.
    The contents of the old matrix are copied in the upper, left-hand
    corner of the new matrix, which is then assigned to \p *this.
  */
  void grow(dimension_type new_n_rows);

  //! Resizes the matrix without worrying about the old contents.
  /*!
    \param new_n_rows
    The number of rows and columns of the resized matrix.

    A new matrix, with the specified dimension, is created without copying
    the content of the old matrix and assigned to \p *this.
  */
  void resize_no_copy(dimension_type new_n_rows);

  //! Returns the number of rows in the matrix.
  dimension_type num_rows() const;

  //! \name Subscript operators.
  //@{
  //! Returns a reference to the \p k-th row of the matrix.
  DB_Row<T>& operator[](dimension_type k);

  //! Returns a constant reference to the \p k-th row of the matrix.
  const DB_Row<T>& operator[](dimension_type k) const;
  //@}

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;
};

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
/*! \relates DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
void swap(DB_Matrix<T>& x, DB_Matrix<T>& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are identical.
/*! \relates DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
bool operator==(const DB_Matrix<T>& x, const DB_Matrix<T>& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are different.
/*! \relates DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
bool operator!=(const DB_Matrix<T>& x, const DB_Matrix<T>& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates DB_Matrix
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into to \p r
  and returns <CODE>true</CODE>;  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const DB_Matrix<T>& x,
                                 const DB_Matrix<T>& y,
                                 Rounding_Dir dir,
                                 Temp& tmp0,
                                 Temp& tmp1,
                                 Temp& tmp2);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Computes the euclidean distance between \p x and \p y.
/*! \relates DB_Matrix
  If the Euclidean distance between \p x and \p y is defined,
  stores an approximation of it into to \p r
  and returns <CODE>true</CODE>;  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
bool euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const DB_Matrix<T>& x,
                               const DB_Matrix<T>& y,
                               Rounding_Dir dir,
                               Temp& tmp0,
                               Temp& tmp1,
                               Temp& tmp2);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates DB_Matrix
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into to \p r
  and returns <CODE>true</CODE>;  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
bool l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const DB_Matrix<T>& x,
                                const DB_Matrix<T>& y,
                                Rounding_Dir dir,
                                Temp& tmp0,
                                Temp& tmp1,
                                Temp& tmp2);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/DB_Matrix_inlines.hh line 1. */
/* DB_Matrix class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/DB_Matrix_inlines.hh line 31. */
#include <iostream>

namespace Parma_Polyhedra_Library {

template <typename T>
inline void
DB_Matrix<T>::m_swap(DB_Matrix& y) {
  using std::swap;
  swap(rows, y.rows);
  swap(row_size, y.row_size);
  swap(row_capacity, y.row_capacity);
}

template <typename T>
inline dimension_type
DB_Matrix<T>::max_num_rows() {
  return std::vector<DB_Row<T> >().max_size();
}

template <typename T>
inline dimension_type
DB_Matrix<T>::max_num_columns() {
  return DB_Row<T>::max_size();
}

template <typename T>
inline memory_size_type
DB_Matrix<T>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename T>
inline
DB_Matrix<T>::const_iterator::const_iterator()
  : i(Iter()) {
}

template <typename T>
inline
DB_Matrix<T>::const_iterator::const_iterator(const Iter& b)
  : i(b) {
}

template <typename T>
inline
DB_Matrix<T>::const_iterator::const_iterator(const const_iterator& y)
  : i(y.i) {
}

template <typename T>
inline typename DB_Matrix<T>::const_iterator&
DB_Matrix<T>::const_iterator::operator=(const const_iterator& y) {
  i = y.i;
  return *this;
}

template <typename T>
inline typename DB_Matrix<T>::const_iterator::reference
DB_Matrix<T>::const_iterator::operator*() const {
  return *i;
}

template <typename T>
inline typename DB_Matrix<T>::const_iterator::pointer
DB_Matrix<T>::const_iterator::operator->() const {
  return &*i;
}

template <typename T>
inline typename DB_Matrix<T>::const_iterator&
DB_Matrix<T>::const_iterator::operator++() {
  ++i;
  return *this;
}

template <typename T>
inline typename DB_Matrix<T>::const_iterator
DB_Matrix<T>::const_iterator::operator++(int) {
  return const_iterator(i++);
}

template <typename T>
inline bool
DB_Matrix<T>::const_iterator::operator==(const const_iterator& y) const {
  return i == y.i;
}

template <typename T>
inline bool
DB_Matrix<T>::const_iterator::operator!=(const const_iterator& y) const {
  return !operator==(y);
}

template <typename T>
inline typename DB_Matrix<T>::const_iterator
DB_Matrix<T>::begin() const {
  return const_iterator(rows.begin());
}

template <typename T>
inline typename DB_Matrix<T>::const_iterator
DB_Matrix<T>::end() const {
  return const_iterator(rows.end());
}

template <typename T>
inline
DB_Matrix<T>::DB_Matrix()
  : rows(),
    row_size(0),
    row_capacity(0) {
}

template <typename T>
inline
DB_Matrix<T>::~DB_Matrix() {
}

template <typename T>
inline DB_Row<T>&
DB_Matrix<T>::operator[](const dimension_type k) {
  PPL_ASSERT(k < rows.size());
  return rows[k];
}

template <typename T>
inline const DB_Row<T>&
DB_Matrix<T>::operator[](const dimension_type k) const {
  PPL_ASSERT(k < rows.size());
  return rows[k];
}

template <typename T>
inline dimension_type
DB_Matrix<T>::num_rows() const {
  return rows.size();
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
inline bool
operator!=(const DB_Matrix<T>& x, const DB_Matrix<T>& y) {
  return !(x == y);
}

template <typename T>
inline
DB_Matrix<T>::DB_Matrix(const DB_Matrix& y)
  : rows(y.rows),
    row_size(y.row_size),
    row_capacity(compute_capacity(y.row_size, max_num_columns())) {
}

template <typename T>
inline DB_Matrix<T>&
DB_Matrix<T>::operator=(const DB_Matrix& y) {
  // Without the following guard against auto-assignments we would
  // recompute the row capacity based on row size, possibly without
  // actually increasing the capacity of the rows.  This would lead to
  // an inconsistent state.
  if (this != &y) {
    // The following assignment may do nothing on auto-assignments...
    rows = y.rows;
    row_size = y.row_size;
    // ... hence the following assignment must not be done on
    // auto-assignments.
    row_capacity = compute_capacity(y.row_size, max_num_columns());
  }
  return *this;
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Specialization, typename Temp, typename To, typename T>
inline bool
l_m_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                    const DB_Matrix<T>& x,
                    const DB_Matrix<T>& y,
                    const Rounding_Dir dir,
                    Temp& tmp0,
                    Temp& tmp1,
                    Temp& tmp2) {
  const dimension_type x_num_rows = x.num_rows();
  if (x_num_rows != y.num_rows()) {
    return false;
  }
  assign_r(tmp0, 0, ROUND_NOT_NEEDED);
  for (dimension_type i = x_num_rows; i-- > 0; ) {
    const DB_Row<T>& x_i = x[i];
    const DB_Row<T>& y_i = y[i];
    for (dimension_type j = x_num_rows; j-- > 0; ) {
      const T& x_i_j = x_i[j];
      const T& y_i_j = y_i[j];
      if (is_plus_infinity(x_i_j)) {
        if (is_plus_infinity(y_i_j)) {
          continue;
        }
        else {
        pinf:
          assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
          return true;
        }
      }
      else if (is_plus_infinity(y_i_j)) {
        goto pinf;
      }
      const Temp* tmp1p;
      const Temp* tmp2p;
      if (x_i_j > y_i_j) {
        maybe_assign(tmp1p, tmp1, x_i_j, dir);
        maybe_assign(tmp2p, tmp2, y_i_j, inverse(dir));
      }
      else {
        maybe_assign(tmp1p, tmp1, y_i_j, dir);
        maybe_assign(tmp2p, tmp2, x_i_j, inverse(dir));
      }
      sub_assign_r(tmp1, *tmp1p, *tmp2p, dir);
      PPL_ASSERT(sgn(tmp1) >= 0);
      Specialization::combine(tmp0, tmp1, dir);
    }
  }
  Specialization::finalize(tmp0, dir);
  assign_r(r, tmp0, dir);
  return true;
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const DB_Matrix<T>& x,
                            const DB_Matrix<T>& y,
                            const Rounding_Dir dir,
                            Temp& tmp0,
                            Temp& tmp1,
                            Temp& tmp2) {
  return
    l_m_distance_assign<Rectilinear_Distance_Specialization<Temp> >(r, x, y,
                                                                    dir,
                                                                    tmp0,
                                                                    tmp1,
                                                                    tmp2);
}


#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const DB_Matrix<T>& x,
                          const DB_Matrix<T>& y,
                          const Rounding_Dir dir,
                          Temp& tmp0,
                          Temp& tmp1,
                          Temp& tmp2) {
  return
    l_m_distance_assign<Euclidean_Distance_Specialization<Temp> >(r, x, y,
                                                                  dir,
                                                                  tmp0,
                                                                  tmp1,
                                                                  tmp2);
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const DB_Matrix<T>& x,
                           const DB_Matrix<T>& y,
                           const Rounding_Dir dir,
                           Temp& tmp0,
                           Temp& tmp1,
                           Temp& tmp2) {
  return
    l_m_distance_assign<L_Infinity_Distance_Specialization<Temp> >(r, x, y,
                                                                   dir,
                                                                   tmp0,
                                                                   tmp1,
                                                                   tmp2);
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
inline void
swap(DB_Matrix<T>& x, DB_Matrix<T>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/DB_Matrix_templates.hh line 1. */
/* DB_Matrix class implementation: non-inline template functions.
*/


namespace Parma_Polyhedra_Library {

template <typename T>
DB_Matrix<T>::DB_Matrix(const dimension_type n_rows)
  : rows(n_rows),
    row_size(n_rows),
    row_capacity(compute_capacity(n_rows, max_num_columns())) {
  // Construct in direct order: will destroy in reverse order.
  for (dimension_type i = 0; i < n_rows; ++i) {
    rows[i].construct(n_rows, row_capacity);
  }
  PPL_ASSERT(OK());
}

template <typename T>
template <typename U>
DB_Matrix<T>::DB_Matrix(const DB_Matrix<U>& y)
  : rows(y.rows.size()),
    row_size(y.row_size),
    row_capacity(compute_capacity(y.row_size, max_num_columns())) {
  // Construct in direct order: will destroy in reverse order.
  for (dimension_type i = 0, n_rows = rows.size(); i < n_rows; ++i) {
    rows[i].construct_upward_approximation(y[i], row_capacity);
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
DB_Matrix<T>::grow(const dimension_type new_n_rows) {
  const dimension_type old_n_rows = rows.size();
  PPL_ASSERT(new_n_rows >= old_n_rows);

  if (new_n_rows > old_n_rows) {
    if (new_n_rows <= row_capacity) {
      // We can recycle the old rows.
      if (rows.capacity() < new_n_rows) {
        // Reallocation will take place.
        std::vector<DB_Row<T> > new_rows;
        new_rows.reserve(compute_capacity(new_n_rows, max_num_rows()));
        new_rows.insert(new_rows.end(), new_n_rows, DB_Row<T>());
        // Construct the new rows.
        dimension_type i = new_n_rows;
        while (i-- > old_n_rows) {
          new_rows[i].construct(new_n_rows, row_capacity);
        }
        // Steal the old rows.
        ++i;
        while (i-- > 0) {
          swap(new_rows[i], rows[i]);
        }
        // Put the new vector into place.
        using std::swap;
        swap(rows, new_rows);
      }
      else {
        // Reallocation will NOT take place.
        rows.insert(rows.end(), new_n_rows - old_n_rows, DB_Row<T>());
        for (dimension_type i = new_n_rows; i-- > old_n_rows; ) {
          rows[i].construct(new_n_rows, row_capacity);
        }
      }
    }
    else {
      // We cannot even recycle the old rows.
      DB_Matrix new_matrix;
      new_matrix.rows.reserve(compute_capacity(new_n_rows, max_num_rows()));
      new_matrix.rows.insert(new_matrix.rows.end(), new_n_rows, DB_Row<T>());
      // Construct the new rows.
      new_matrix.row_size = new_n_rows;
      new_matrix.row_capacity = compute_capacity(new_n_rows,
                                                 max_num_columns());
      dimension_type i = new_n_rows;
      while (i-- > old_n_rows) {
        new_matrix.rows[i].construct(new_matrix.row_size,
                                     new_matrix.row_capacity);
      }
      // Copy the old rows.
      ++i;
      while (i-- > 0) {
        // FIXME: copying may be unnecessarily costly.
        DB_Row<T> new_row(rows[i],
                          new_matrix.row_size,
                          new_matrix.row_capacity);
        swap(new_matrix.rows[i], new_row);
      }
      // Put the new vector into place.
      m_swap(new_matrix);
      return;
    }
  }
  // Here we have the right number of rows.
  if (new_n_rows > row_size) {
    // We need more columns.
    if (new_n_rows <= row_capacity) {
      // But we have enough capacity: we resize existing rows.
      for (dimension_type i = old_n_rows; i-- > 0; ) {
        rows[i].expand_within_capacity(new_n_rows);
      }
    }
    else {
      // Capacity exhausted: we must reallocate the rows and
      // make sure all the rows have the same capacity.
      const dimension_type new_row_capacity
        = compute_capacity(new_n_rows, max_num_columns());
      for (dimension_type i = old_n_rows; i-- > 0; ) {
        // FIXME: copying may be unnecessarily costly.
        DB_Row<T> new_row(rows[i], new_n_rows, new_row_capacity);
        swap(rows[i], new_row);
      }
      row_capacity = new_row_capacity;
    }
    // Rows have grown or shrunk.
    row_size = new_n_rows;
  }
}

template <typename T>
void
DB_Matrix<T>::resize_no_copy(const dimension_type new_n_rows) {
  dimension_type old_n_rows = rows.size();

  if (new_n_rows > old_n_rows) {
    // Rows will be inserted.
    if (new_n_rows <= row_capacity) {
      // We can recycle the old rows.
      if (rows.capacity() < new_n_rows) {
        // Reallocation (of vector `rows') will take place.
        std::vector<DB_Row<T> > new_rows;
        new_rows.reserve(compute_capacity(new_n_rows, max_num_rows()));
        new_rows.insert(new_rows.end(), new_n_rows, DB_Row<T>());
        // Construct the new rows (be careful: each new row must have
        // the same capacity as each one of the old rows).
        dimension_type i = new_n_rows;
        while (i-- > old_n_rows) {
          new_rows[i].construct(new_n_rows, row_capacity);
        // Steal the old rows.
        }
        ++i;
        while (i-- > 0) {
          swap(new_rows[i], rows[i]);
        }
        // Put the new vector into place.
        using std::swap;
        swap(rows, new_rows);
      }
      else {
        // Reallocation (of vector `rows') will NOT take place.
        rows.insert(rows.end(), new_n_rows - old_n_rows, DB_Row<T>());
        // Be careful: each new row must have
        // the same capacity as each one of the old rows.
        for (dimension_type i = new_n_rows; i-- > old_n_rows; ) {
          rows[i].construct(new_n_rows, row_capacity);
        }
      }
    }
    else {
      // We cannot even recycle the old rows: allocate a new matrix and swap.
      DB_Matrix new_matrix(new_n_rows);
      m_swap(new_matrix);
      return;
    }
  }
  else if (new_n_rows < old_n_rows) {
    // Drop some rows.
    rows.resize(new_n_rows);
    // Shrink the existing rows.
    for (dimension_type i = new_n_rows; i-- > 0; ) {
      rows[i].shrink(new_n_rows);
    }
    old_n_rows = new_n_rows;
  }
  // Here we have the right number of rows.
  if (new_n_rows > row_size) {
    // We need more columns.
    if (new_n_rows <= row_capacity) {
      // But we have enough capacity: we resize existing rows.
      for (dimension_type i = old_n_rows; i-- > 0; ) {
        rows[i].expand_within_capacity(new_n_rows);
      }
    }
    else {
      // Capacity exhausted: we must reallocate the rows and
      // make sure all the rows have the same capacity.
      const dimension_type new_row_capacity
        = compute_capacity(new_n_rows, max_num_columns());
      for (dimension_type i = old_n_rows; i-- > 0; ) {
        DB_Row<T> new_row(new_n_rows, new_row_capacity);
        swap(rows[i], new_row);
      }
      row_capacity = new_row_capacity;
    }
  }
  // DB_Rows have grown or shrunk.
  row_size = new_n_rows;
}

template <typename T>
void
DB_Matrix<T>::ascii_dump(std::ostream& s) const {
  const DB_Matrix<T>& x = *this;
  const char separator = ' ';
  const dimension_type nrows = x.num_rows();
  s << nrows << separator << "\n";
  for (dimension_type i = 0; i < nrows;  ++i) {
    for (dimension_type j = 0; j < nrows; ++j) {
      using namespace IO_Operators;
      s << x[i][j] << separator;
    }
    s << "\n";
  }
}

PPL_OUTPUT_TEMPLATE_DEFINITIONS(T, DB_Matrix<T>)

template <typename T>
bool
DB_Matrix<T>::ascii_load(std::istream& s) {
  dimension_type nrows;
  if (!(s >> nrows)) {
    return false;
  }
  resize_no_copy(nrows);
  DB_Matrix& x = *this;
  for (dimension_type i = 0; i < nrows;  ++i) {
    for (dimension_type j = 0; j < nrows; ++j) {
      Result r = input(x[i][j], s, ROUND_CHECK);
      if (result_relation(r) != VR_EQ || is_minus_infinity(x[i][j])) {
        return false;
      }
    }
  }

  // Check invariants.
  PPL_ASSERT(OK());
  return true;
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
bool
operator==(const DB_Matrix<T>& x, const DB_Matrix<T>& y) {
  const dimension_type x_num_rows = x.num_rows();
  if (x_num_rows != y.num_rows()) {
    return false;
  }
  for (dimension_type i = x_num_rows; i-- > 0; ) {
    if (x[i] != y[i]) {
      return false;
    }
  }
  return true;
}

template <typename T>
memory_size_type
DB_Matrix<T>::external_memory_in_bytes() const {
  memory_size_type n = rows.capacity() * sizeof(DB_Row<T>);
  for (dimension_type i = num_rows(); i-- > 0; ) {
    n += rows[i].external_memory_in_bytes(row_capacity);
  }
  return n;
}

template <typename T>
bool
DB_Matrix<T>::OK() const {
#ifndef NDEBUG
  using std::endl;
  using std::cerr;
#endif

  // The matrix must be square.
  if (num_rows() != row_size) {
#ifndef NDEBUG
    cerr << "DB_Matrix has fewer columns than rows:\n"
         << "row_size is " << row_size
         << ", num_rows() is " << num_rows() << "!"
         << endl;
#endif
    return false;
  }

  const DB_Matrix& x = *this;
  const dimension_type n_rows = x.num_rows();
  for (dimension_type i = 0; i < n_rows; ++i) {
    if (!x[i].OK(row_size, row_capacity)) {
      return false;
    }
  }

  // All checks passed.
  return true;
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Parma_Polyhedra_Library::DB_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
std::ostream&
IO_Operators::operator<<(std::ostream& s, const DB_Matrix<T>& c) {
  const dimension_type n = c.num_rows();
  for (dimension_type i = 0; i < n; ++i) {
    for (dimension_type j = 0; j < n; ++j) {
      s << c[i][j] << " ";
    }
    s << "\n";
  }
  return s;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/DB_Matrix_defs.hh line 324. */

/* Automatically generated from PPL source file ../src/WRD_coefficient_types_defs.hh line 1. */
/* Coefficient types of weakly-relational domains: declarations.
*/


/* Automatically generated from PPL source file ../src/WRD_coefficient_types_defs.hh line 28. */

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface \brief
  The production policy for checked numbers used in weakly-relational
  domains.
 */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct WRD_Extended_Number_Policy {
  //! Check for overflowed result.
  const_bool_nodef(check_overflow, true);

  //! Do not check for attempts to add infinities with different sign.
  const_bool_nodef(check_inf_add_inf, false);

  //! Do not check for attempts to subtract infinities with same sign.
  const_bool_nodef(check_inf_sub_inf, false);

  //! Do not check for attempts to multiply infinities by zero.
  const_bool_nodef(check_inf_mul_zero, false);

  //! Do not check for attempts to divide by zero.
  const_bool_nodef(check_div_zero, false);

  //! Do not check for attempts to divide infinities.
  const_bool_nodef(check_inf_div_inf, false);

  //! Do not check for attempts to compute remainder of infinities.
  const_bool_nodef(check_inf_mod, false);

  //! Do not checks for attempts to take the square root of a negative number.
  const_bool_nodef(check_sqrt_neg, false);

  //! Handle not-a-number special value.
  const_bool_nodef(has_nan, true);

  //! Handle infinity special values.
  const_bool_nodef(has_infinity, true);

  // `convertible' is intentionally not defined: the compile time
  // error on conversions is the expected behavior.

  //! Honor requests to check for FPU inexact results.
  const_bool_nodef(fpu_check_inexact, true);

  //! Do not make extra checks to detect FPU NaN results.
  const_bool_nodef(fpu_check_nan_result, false);

  // ROUND_DEFAULT_CONSTRUCTOR is intentionally not defined.
  // ROUND_DEFAULT_OPERATOR is intentionally not defined.
  // ROUND_DEFAULT_FUNCTION is intentionally not defined.
  // ROUND_DEFAULT_INPUT is intentionally not defined.
  // ROUND_DEFAULT_OUTPUT is intentionally not defined.

  /*! \brief
    Handles \p r: called by all constructors, operators and functions that
    do not return a Result value.
  */
  static void handle_result(Result r);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \ingroup PPL_CXX_interface \brief
  The debugging policy for checked numbers used in weakly-relational
  domains.
 */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
struct Debug_WRD_Extended_Number_Policy {
  //! Check for overflowed result.
  const_bool_nodef(check_overflow, true);

  //! Check for attempts to add infinities with different sign.
  const_bool_nodef(check_inf_add_inf, true);

  //! Check for attempts to subtract infinities with same sign.
  const_bool_nodef(check_inf_sub_inf, true);

  //! Check for attempts to multiply infinities by zero.
  const_bool_nodef(check_inf_mul_zero, true);

  //! Check for attempts to divide by zero.
  const_bool_nodef(check_div_zero, true);

  //! Check for attempts to divide infinities.
  const_bool_nodef(check_inf_div_inf, true);

  //! Check for attempts to compute remainder of infinities.
  const_bool_nodef(check_inf_mod, true);

  //! Checks for attempts to take the square root of a negative number.
  const_bool_nodef(check_sqrt_neg, true);

  //! Handle not-a-number special value.
  const_bool_nodef(has_nan, true);

  //! Handle infinity special values.
  const_bool_nodef(has_infinity, true);

  // `convertible' is intentionally not defined: the compile time
  // error on conversions is the expected behavior.

  //! Honor requests to check for FPU inexact results.
  const_bool_nodef(fpu_check_inexact, true);

  //! Make extra checks to detect FPU NaN results.
  const_bool_nodef(fpu_check_nan_result, true);

  // ROUND_DEFAULT_CONSTRUCTOR is intentionally not defined.
  // ROUND_DEFAULT_OPERATOR is intentionally not defined.
  // ROUND_DEFAULT_FUNCTION is intentionally not defined.
  // ROUND_DEFAULT_INPUT is intentionally not defined.
  // ROUND_DEFAULT_OUTPUT is intentionally not defined.

  /*! \brief
    Handles \p r: called by all constructors, operators and functions that
    do not return a Result value.
  */
  static void handle_result(Result r);
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/WRD_coefficient_types_inlines.hh line 1. */
/* Coefficient types of weakly-relational domains: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline void
WRD_Extended_Number_Policy::handle_result(Result r) {
  if (result_class(r) == VC_NAN) {
    throw_result_exception(r);
  }
}

inline void
Debug_WRD_Extended_Number_Policy::handle_result(Result r) {
  if (result_class(r) == VC_NAN) {
    throw_result_exception(r);
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/WRD_coefficient_types_defs.hh line 152. */

/* Automatically generated from PPL source file ../src/BD_Shape_defs.hh line 52. */
#include <cstddef>
#include <iosfwd>
#include <vector>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::BD_Shape
  Writes a textual representation of \p bds on \p s:
  <CODE>false</CODE> is written if \p bds is an empty polyhedron;
  <CODE>true</CODE> is written if \p bds is the universe polyhedron;
  a system of constraints defining \p bds is written otherwise,
  all constraints separated by ", ".
*/
template <typename T>
std::ostream&
operator<<(std::ostream& s, const BD_Shape<T>& bds);

} // namespace IO_Operators

//! Swaps \p x with \p y.
/*! \relates BD_Shape */
template <typename T>
void swap(BD_Shape<T>& x, BD_Shape<T>& y);

//! Returns <CODE>true</CODE> if and only if \p x and \p y are the same BDS.
/*! \relates BD_Shape
  Note that \p x and \p y may be dimension-incompatible shapes:
  in this case, the value <CODE>false</CODE> is returned.
*/
template <typename T>
bool operator==(const BD_Shape<T>& x, const BD_Shape<T>& y);

//! Returns <CODE>true</CODE> if and only if \p x and \p y are not the same BDS.
/*! \relates BD_Shape
  Note that \p x and \p y may be dimension-incompatible shapes:
  in this case, the value <CODE>true</CODE> is returned.
*/
template <typename T>
bool operator!=(const BD_Shape<T>& x, const BD_Shape<T>& y);

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates BD_Shape
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.
*/
template <typename To, typename T>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const BD_Shape<T>& x,
                                 const BD_Shape<T>& y,
                                 Rounding_Dir dir);

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates BD_Shape
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.
*/
template <typename Temp, typename To, typename T>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const BD_Shape<T>& x,
                                 const BD_Shape<T>& y,
                                 Rounding_Dir dir);

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates BD_Shape
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
template <typename Temp, typename To, typename T>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const BD_Shape<T>& x,
                                 const BD_Shape<T>& y,
                                 Rounding_Dir dir,
                                 Temp& tmp0,
                                 Temp& tmp1,
                                 Temp& tmp2);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates BD_Shape
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.
*/
template <typename To, typename T>
bool euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const BD_Shape<T>& x,
                               const BD_Shape<T>& y,
                               Rounding_Dir dir);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates BD_Shape
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.
*/
template <typename Temp, typename To, typename T>
bool euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const BD_Shape<T>& x,
                               const BD_Shape<T>& y,
                               Rounding_Dir dir);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates BD_Shape
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
template <typename Temp, typename To, typename T>
bool euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const BD_Shape<T>& x,
                               const BD_Shape<T>& y,
                               Rounding_Dir dir,
                               Temp& tmp0,
                               Temp& tmp1,
                               Temp& tmp2);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates BD_Shape
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.
*/
template <typename To, typename T>
bool l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const BD_Shape<T>& x,
                                const BD_Shape<T>& y,
                                Rounding_Dir dir);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates BD_Shape
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.
*/
template <typename Temp, typename To, typename T>
bool l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const BD_Shape<T>& x,
                                const BD_Shape<T>& y,
                                Rounding_Dir dir);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates BD_Shape
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
template <typename Temp, typename To, typename T>
bool l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const BD_Shape<T>& x,
                                const BD_Shape<T>& y,
                                Rounding_Dir dir,
                                Temp& tmp0,
                                Temp& tmp1,
                                Temp& tmp2);

// This class contains some helper functions that need to be friends of
// Linear_Expression.
class BD_Shape_Helpers {
public:
  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! Decodes the constraint \p c as a bounded difference.
  /*! \relates BD_Shape
    \return
    <CODE>true</CODE> if the constraint \p c is a
    \ref Bounded_Difference_Shapes "bounded difference";
    <CODE>false</CODE> otherwise.

    \param c
    The constraint to be decoded.

    \param c_num_vars
    If <CODE>true</CODE> is returned, then it will be set to the number
    of variables having a non-zero coefficient. The only legal values
    will therefore be 0, 1 and 2.

    \param c_first_var
    If <CODE>true</CODE> is returned and if \p c_num_vars is not set to 0,
    then it will be set to the index of the first variable having
    a non-zero coefficient in \p c.

    \param c_second_var
    If <CODE>true</CODE> is returned and if \p c_num_vars is set to 2,
    then it will be set to the index of the second variable having
    a non-zero coefficient in \p c. If \p c_num_vars is set to 1, this must be
    0.

    \param c_coeff
    If <CODE>true</CODE> is returned and if \p c_num_vars is not set to 0,
    then it will be set to the value of the first non-zero coefficient
    in \p c.
  */
  #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  static bool extract_bounded_difference(const Constraint& c,
                                         dimension_type& c_num_vars,
                                         dimension_type& c_first_var,
                                         dimension_type& c_second_var,
                                         Coefficient& c_coeff);
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Extracts leader indices from the predecessor relation.
/*! \relates BD_Shape */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
void compute_leader_indices(const std::vector<dimension_type>& predecessor,
                            std::vector<dimension_type>& indices);

} // namespace Parma_Polyhedra_Library

//! A bounded difference shape.
/*! \ingroup PPL_CXX_interface
  The class template BD_Shape<T> allows for the efficient representation
  of a restricted kind of <EM>topologically closed</EM> convex polyhedra
  called <EM>bounded difference shapes</EM> (BDSs, for short).
  The name comes from the fact that the closed affine half-spaces that
  characterize the polyhedron can be expressed by constraints of the form
  \f$\pm x_i \leq k\f$ or \f$x_i - x_j \leq k\f$, where the inhomogeneous
  term \f$k\f$ is a rational number.

  Based on the class template type parameter \p T, a family of extended
  numbers is built and used to approximate the inhomogeneous term of
  bounded differences. These extended numbers provide a representation
  for the value \f$+\infty\f$, as well as <EM>rounding-aware</EM>
  implementations for several arithmetic functions.
  The value of the type parameter \p T may be one of the following:
    - a bounded precision integer type (e.g., \c int32_t or \c int64_t);
    - a bounded precision floating point type (e.g., \c float or \c double);
    - an unbounded integer or rational type, as provided by GMP
      (i.e., \c mpz_class or \c mpq_class).

  The user interface for BDSs is meant to be as similar as possible to
  the one developed for the polyhedron class C_Polyhedron.

  The domain of BD shapes <EM>optimally supports</EM>:
    - tautological and inconsistent constraints and congruences;
    - bounded difference constraints;
    - non-proper congruences (i.e., equalities) that are expressible
      as bounded-difference constraints.

  Depending on the method, using a constraint or congruence that is not
  optimally supported by the domain will either raise an exception or
  result in a (possibly non-optimal) upward approximation.

  A constraint is a bounded difference if it has the form
    \f[
      a_i x_i - a_j x_j \relsym b
    \f]
  where \f$\mathord{\relsym} \in \{ \leq, =, \geq \}\f$ and
  \f$a_i\f$, \f$a_j\f$, \f$b\f$ are integer coefficients such that
  \f$a_i = 0\f$, or \f$a_j = 0\f$, or \f$a_i = a_j\f$.
  The user is warned that the above bounded difference Constraint object
  will be mapped into a \e correct and \e optimal approximation that,
  depending on the expressive power of the chosen template argument \p T,
  may loose some precision. Also note that strict constraints are not
  bounded differences.

  For instance, a Constraint object encoding \f$3x - 3y \leq 1\f$ will be
  approximated by:
    - \f$x - y \leq 1\f$,
      if \p T is a (bounded or unbounded) integer type;
    - \f$x - y \leq \frac{1}{3}\f$,
      if \p T is the unbounded rational type \c mpq_class;
    - \f$x - y \leq k\f$, where \f$k > \frac{1}{3}\f$,
      if \p T is a floating point type (having no exact representation
      for \f$\frac{1}{3}\f$).

  On the other hand, depending from the context, a Constraint object
  encoding \f$3x - y \leq 1\f$ will be either upward approximated
  (e.g., by safely ignoring it) or it will cause an exception.

  In the following examples it is assumed that the type argument \p T
  is one of the possible instances listed above and that variables
  <CODE>x</CODE>, <CODE>y</CODE> and <CODE>z</CODE> are defined
  (where they are used) as follows:
  \code
    Variable x(0);
    Variable y(1);
    Variable z(2);
  \endcode

  \par Example 1
  The following code builds a BDS corresponding to a cube in \f$\Rset^3\f$,
  given as a system of constraints:
  \code
    Constraint_System cs;
    cs.insert(x >= 0);
    cs.insert(x <= 1);
    cs.insert(y >= 0);
    cs.insert(y <= 1);
    cs.insert(z >= 0);
    cs.insert(z <= 1);
    BD_Shape<T> bd(cs);
  \endcode
  Since only those constraints having the syntactic form of a
  <EM>bounded difference</EM> are optimally supported, the following code
  will throw an exception (caused by constraints 7, 8 and 9):
  \code
    Constraint_System cs;
    cs.insert(x >= 0);
    cs.insert(x <= 1);
    cs.insert(y >= 0);
    cs.insert(y <= 1);
    cs.insert(z >= 0);
    cs.insert(z <= 1);
    cs.insert(x + y <= 0);      // 7
    cs.insert(x - z + x >= 0);  // 8
    cs.insert(3*z - y <= 1);    // 9
    BD_Shape<T> bd(cs);
  \endcode
*/
template <typename T>
class Parma_Polyhedra_Library::BD_Shape {
private:
  /*! \brief
    The (extended) numeric type of the inhomogeneous term of
    the inequalities defining a BDS.
  */
#ifndef NDEBUG
  typedef Checked_Number<T, Debug_WRD_Extended_Number_Policy> N;
#else
  typedef Checked_Number<T, WRD_Extended_Number_Policy> N;
#endif

public:
  //! The numeric base type upon which bounded differences are built.
  typedef T coefficient_type_base;

  /*! \brief
    The (extended) numeric type of the inhomogeneous term of the
    inequalities defining a BDS.
  */
  typedef N coefficient_type;

  //! Returns the maximum space dimension that a BDS can handle.
  static dimension_type max_space_dimension();

  /*! \brief
    Returns \c false indicating that this domain cannot recycle constraints.
  */
  static bool can_recycle_constraint_systems();

  /*! \brief
    Returns \c false indicating that this domain cannot recycle congruences.
  */
  static bool can_recycle_congruence_systems();

  //! \name Constructors, Assignment, Swap and Destructor
  //@{

  //! Builds a universe or empty BDS of the specified space dimension.
  /*!
    \param num_dimensions
    The number of dimensions of the vector space enclosing the BDS;

    \param kind
    Specifies whether the universe or the empty BDS has to be built.
  */
  explicit BD_Shape(dimension_type num_dimensions = 0,
                    Degenerate_Element kind = UNIVERSE);

  //! Ordinary copy constructor.
  /*!
    The complexity argument is ignored.
  */
  BD_Shape(const BD_Shape& y,
           Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a conservative, upward approximation of \p y.
  /*!
    The complexity argument is ignored.
  */
  template <typename U>
  explicit BD_Shape(const BD_Shape<U>& y,
                    Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a BDS from the system of constraints \p cs.
  /*!
    The BDS inherits the space dimension of \p cs.

    \param cs
    A system of BD constraints.

    \exception std::invalid_argument
    Thrown if \p cs contains a constraint which is not optimally supported
    by the BD shape domain.
  */
  explicit BD_Shape(const Constraint_System& cs);

  //! Builds a BDS from a system of congruences.
  /*!
    The BDS inherits the space dimension of \p cgs

    \param cgs
    A system of congruences.

    \exception std::invalid_argument
    Thrown if \p cgs contains congruences which are not optimally
    supported by the BD shape domain.
  */
  explicit BD_Shape(const Congruence_System& cgs);

  //! Builds a BDS from the system of generators \p gs.
  /*!
    Builds the smallest BDS containing the polyhedron defined by \p gs.
    The BDS inherits the space dimension of \p gs.

    \exception std::invalid_argument
    Thrown if the system of generators is not empty but has no points.
  */
  explicit BD_Shape(const Generator_System& gs);

  //! Builds a BDS from the polyhedron \p ph.
  /*!
    Builds a BDS containing \p ph using algorithms whose complexity
    does not exceed the one specified by \p complexity.  If
    \p complexity is \p ANY_COMPLEXITY, then the BDS built is the
    smallest one containing \p ph.
  */
  explicit BD_Shape(const Polyhedron& ph,
                    Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a BDS out of a box.
  /*!
    The BDS inherits the space dimension of the box.
    The built BDS is the most precise BDS that includes the box.

    \param box
    The box representing the BDS to be built.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.

    \exception std::length_error
    Thrown if the space dimension of \p box exceeds the maximum
    allowed space dimension.
  */
  template <typename Interval>
  explicit BD_Shape(const Box<Interval>& box,
                    Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a BDS out of a grid.
  /*!
    The BDS inherits the space dimension of the grid.
    The built BDS is the most precise BDS that includes the grid.

    \param grid
    The grid used to build the BDS.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.

    \exception std::length_error
    Thrown if the space dimension of \p grid exceeds the maximum
    allowed space dimension.
  */
  explicit BD_Shape(const Grid& grid,
                    Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a BDS from an octagonal shape.
  /*!
    The BDS inherits the space dimension of the octagonal shape.
    The built BDS is the most precise BDS that includes the octagonal shape.

    \param os
    The octagonal shape used to build the BDS.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.

    \exception std::length_error
    Thrown if the space dimension of \p os exceeds the maximum
    allowed space dimension.
  */
  template <typename U>
  explicit BD_Shape(const Octagonal_Shape<U>& os,
                    Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    The assignment operator
    (\p *this and \p y can be dimension-incompatible).
  */
  BD_Shape& operator=(const BD_Shape& y);

  /*! \brief
    Swaps \p *this with \p y
    (\p *this and \p y can be dimension-incompatible).
  */
  void m_swap(BD_Shape& y);

  //! Destructor.
  ~BD_Shape();

  //@} Constructors, Assignment, Swap and Destructor

  //! \name Member Functions that Do Not Modify the BD_Shape
  //@{

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  /*! \brief
    Returns \f$0\f$, if \p *this is empty; otherwise, returns the
    \ref Affine_Independence_and_Affine_Dimension "affine dimension"
    of \p *this.
  */
  dimension_type affine_dimension() const;

  //! Returns a system of constraints defining \p *this.
  Constraint_System constraints() const;

  //! Returns a minimized system of constraints defining \p *this.
  Constraint_System minimized_constraints() const;

  //! Returns a system of (equality) congruences satisfied by \p *this.
  Congruence_System congruences() const;

  /*! \brief
    Returns a minimal system of (equality) congruences
    satisfied by \p *this with the same affine dimension as \p *this.
  */
  Congruence_System minimized_congruences() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p expr is
    bounded from above in \p *this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_above(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p expr is
    bounded from below in \p *this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_below(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from above in \p *this, in which case
    the supremum value is computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if and only if the supremum is also the maximum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from above,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d
    and \p maximum are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from above in \p *this, in which case
    the supremum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if and only if the supremum is also the maximum value;

    \param g
    When maximization succeeds, will be assigned the point or
    closure point where \p expr reaches its supremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from above,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d, \p maximum
    and \p g are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from below in \p *this, in which case
    the infimum value is computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if and only if the infimum is also the minimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d
    and \p minimum are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from below in \p *this, in which case
    the infimum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if and only if the infimum is also the minimum value;

    \param g
    When minimization succeeds, will be assigned a point or
    closure point where \p expr reaches its infimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d, \p minimum
    and \p g are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if there exist a
    unique value \p val such that \p *this
    saturates the equality <CODE>expr = val</CODE>.

    \param expr
    The linear expression for which the frequency is needed;

    \param freq_n
    If <CODE>true</CODE> is returned, the value is set to \f$0\f$;
    Present for interface compatibility with class Grid, where
    the \ref Grid_Frequency "frequency" can have a non-zero value;

    \param freq_d
    If <CODE>true</CODE> is returned, the value is set to \f$1\f$;

    \param val_n
    The numerator of \p val;

    \param val_d
    The denominator of \p val;

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If <CODE>false</CODE> is returned, then \p freq_n, \p freq_d,
    \p val_n and \p val_d are left untouched.
  */
  bool frequency(const Linear_Expression& expr,
                 Coefficient& freq_n, Coefficient& freq_d,
                 Coefficient& val_n, Coefficient& val_d) const;

  //! Returns <CODE>true</CODE> if and only if \p *this contains \p y.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool contains(const BD_Shape& y) const;

  //! Returns <CODE>true</CODE> if and only if \p *this strictly contains \p y.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool strictly_contains(const BD_Shape& y) const;

  //! Returns <CODE>true</CODE> if and only if \p *this and \p y are disjoint.
  /*!
    \exception std::invalid_argument
    Thrown if \p x and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool is_disjoint_from(const BD_Shape& y) const;

  //! Returns the relations holding between \p *this and the constraint \p c.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible.
  */
  Poly_Con_Relation relation_with(const Constraint& c) const;

  //! Returns the relations holding between \p *this and the congruence \p cg.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible.
  */
  Poly_Con_Relation relation_with(const Congruence& cg) const;

  //! Returns the relations holding between \p *this and the generator \p g.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and generator \p g are dimension-incompatible.
  */
  Poly_Gen_Relation relation_with(const Generator& g) const;

  //! Returns <CODE>true</CODE> if and only if \p *this is an empty BDS.
  bool is_empty() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a universe BDS.
  bool is_universe() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is discrete.
  bool is_discrete() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    is a topologically closed subset of the vector space.
  */
  bool is_topologically_closed() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a bounded BDS.
  bool is_bounded() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    contains at least one integer point.
  */
  bool contains_integer_point() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p var is constrained in
    \p *this.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  bool constrains(Variable var) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this satisfies
    all its invariants.
  */
  bool OK() const;

  //@} Member Functions that Do Not Modify the BD_Shape

  //! \name Space-Dimension Preserving Member Functions that May Modify the BD_Shape
  //@{

  /*! \brief
    Adds a copy of constraint \p c to the system of bounded differences
    defining \p *this.

    \param c
    The constraint to be added.

    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible,
    or \p c is not optimally supported by the BD shape domain.
  */
  void add_constraint(const Constraint& c);

  /*! \brief
    Adds a copy of congruence \p cg to the system of congruences of \p *this.

    \param cg
    The congruence to be added.

    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible,
    or \p cg is not optimally supported by the BD shape domain.
  */
  void add_congruence(const Congruence& cg);

  /*! \brief
    Adds the constraints in \p cs to the system of bounded differences
    defining \p *this.

    \param  cs
    The constraints that will be added.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible,
    or \p cs contains a constraint which is not optimally supported
    by the BD shape domain.
  */
  void add_constraints(const Constraint_System& cs);

  /*! \brief
    Adds the constraints in \p cs to the system of constraints
    of \p *this.

    \param cs
    The constraint system to be added to \p *this.  The constraints in
    \p cs may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible,
    or \p cs contains a constraint which is not optimally supported
    by the BD shape domain.

    \warning
    The only assumption that can be made on \p cs upon successful or
    exceptional return is that it can be safely destroyed.
  */
  void add_recycled_constraints(Constraint_System& cs);

  /*! \brief
    Adds to \p *this constraints equivalent to the congruences in \p cgs.

    \param cgs
    Contains the congruences that will be added to the system of
    constraints of \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible,
    or \p cgs contains a congruence which is not optimally supported
    by the BD shape domain.
  */
  void add_congruences(const Congruence_System& cgs);

  /*! \brief
    Adds to \p *this constraints equivalent to the congruences in \p cgs.

    \param cgs
    Contains the congruences that will be added to the system of
    constraints of \p *this. Its elements may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible,
    or \p cgs contains a congruence which is not optimally supported
    by the BD shape domain.

    \warning
    The only assumption that can be made on \p cgs upon successful or
    exceptional return is that it can be safely destroyed.
  */
  void add_recycled_congruences(Congruence_System& cgs);

  /*! \brief
    Uses a copy of constraint \p c to refine the system of bounded differences
    defining \p *this.

    \param c
    The constraint. If it is not a bounded difference, it will be ignored.

    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible.
  */
  void refine_with_constraint(const Constraint& c);

  /*! \brief
    Uses a copy of congruence \p cg to refine the system of
    bounded differences  of \p *this.

    \param cg
    The congruence. If it is not a bounded difference equality, it
    will be ignored.

    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible.
  */
  void refine_with_congruence(const Congruence& cg);

  /*! \brief
    Uses a copy of the constraints in \p cs to refine the system of
    bounded differences defining \p *this.

    \param  cs
    The constraint system to be used. Constraints that are not bounded
    differences are ignored.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible.
  */
  void refine_with_constraints(const Constraint_System& cs);

  /*! \brief
    Uses a copy of the congruences in \p cgs to refine the system of
    bounded differences defining \p *this.

    \param  cgs
    The congruence system to be used. Congruences that are not bounded
    difference equalities are ignored.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible.
  */
  void refine_with_congruences(const Congruence_System& cgs);

  /*! \brief
    Refines the system of BD_Shape constraints defining \p *this using
    the constraint expressed by \p left \f$\leq\f$ \p right.

    \param left
    The linear form on intervals with floating point boundaries that
    is at the left of the comparison operator. All of its coefficients
    MUST be bounded.

    \param right
    The linear form on intervals with floating point boundaries that
    is at the right of the comparison operator. All of its coefficients
    MUST be bounded.

    \exception std::invalid_argument
    Thrown if \p left (or \p right) is dimension-incompatible with \p *this.

    This function is used in abstract interpretation to model a filter
    that is generated by a comparison of two expressions that are correctly
    approximated by \p left and \p right respectively.
  */
  template <typename Interval_Info>
  void refine_with_linear_form_inequality(
                   const Linear_Form<Interval<T, Interval_Info> >& left,
                   const Linear_Form<Interval<T, Interval_Info> >& right);

  /*! \brief
    Refines the system of BD_Shape constraints defining \p *this using
    the constraint expressed by \p left \f$\relsym\f$ \p right, where
    \f$\relsym\f$ is the relation symbol specified by \p relsym.

    \param left
    The linear form on intervals with floating point boundaries that
    is at the left of the comparison operator. All of its coefficients
    MUST be bounded.

    \param right
    The linear form on intervals with floating point boundaries that
    is at the right of the comparison operator. All of its coefficients
    MUST be bounded.

    \param relsym
    The relation symbol.

    \exception std::invalid_argument
    Thrown if \p left (or \p right) is dimension-incompatible with \p *this.

    \exception std::runtime_error
    Thrown if \p relsym is not a valid relation symbol.

    This function is used in abstract interpretation to model a filter
    that is generated by a comparison of two expressions that are correctly
    approximated by \p left and \p right respectively.
  */
  template <typename Interval_Info>
  void generalized_refine_with_linear_form_inequality(
                   const Linear_Form<Interval<T, Interval_Info> >& left,
                   const Linear_Form<Interval<T, Interval_Info> >& right,
                   Relation_Symbol relsym);

  //! Applies to \p dest the interval constraints embedded in \p *this.
  /*!
    \param dest
    The object to which the constraints will be added.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p dest.

    The template type parameter U must provide the following methods.
    \code
      dimension_type space_dimension() const
    \endcode
    returns the space dimension of the object.
    \code
      void set_empty()
    \endcode
    sets the object to an empty object.
    \code
      bool restrict_lower(dimension_type dim, const T& lb)
    \endcode
    restricts the object by applying the lower bound \p lb to the space
    dimension \p dim and returns <CODE>false</CODE> if and only if the
    object becomes empty.
    \code
      bool restrict_upper(dimension_type dim, const T& ub)
    \endcode
    restricts the object by applying the upper bound \p ub to the space
    dimension \p dim and returns <CODE>false</CODE> if and only if the
    object becomes empty.
  */
  template <typename U>
  void export_interval_constraints(U& dest) const;

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to space dimension \p var, assigning the result to \p *this.

    \param var
    The space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  void unconstrain(Variable var);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to the set of space dimensions \p vars,
    assigning the result to \p *this.

    \param vars
    The set of space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars.
  */
  void unconstrain(const Variables_Set& vars);

  //! Assigns to \p *this the intersection of \p *this and \p y.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void intersection_assign(const BD_Shape& y);

  /*! \brief
    Assigns to \p *this the smallest BDS containing the union
    of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void upper_bound_assign(const BD_Shape& y);

  /*! \brief
    If the upper bound of \p *this and \p y is exact, it is assigned
    to \p *this and <CODE>true</CODE> is returned,
    otherwise <CODE>false</CODE> is returned.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool upper_bound_assign_if_exact(const BD_Shape& y);

  /*! \brief
    If the \e integer upper bound of \p *this and \p y is exact,
    it is assigned to \p *this and <CODE>true</CODE> is returned;
    otherwise <CODE>false</CODE> is returned.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.

    \note
    The integer upper bound of two rational BDS is the smallest rational
    BDS containing all the integral points of the two arguments.
    This method requires that the coefficient type parameter \c T is
    an integral type.
  */
  bool integer_upper_bound_assign_if_exact(const BD_Shape& y);

  /*! \brief
    Assigns to \p *this the smallest BD shape containing
    the set difference of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void difference_assign(const BD_Shape& y);

  /*! \brief
    Assigns to \p *this a \ref Meet_Preserving_Simplification
    "meet-preserving simplification" of \p *this with respect to \p y.
    If \c false is returned, then the intersection is empty.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool simplify_using_context_assign(const BD_Shape& y);

  /*! \brief
    Assigns to \p *this the
    \ref Single_Update_Affine_Functions "affine image"
    of \p *this under the function mapping variable \p var into the
    affine expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is assigned.

    \param expr
    The numerator of the affine expression.

    \param denominator
    The denominator of the affine expression.

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this
    are dimension-incompatible or if \p var is not a dimension of \p *this.
  */
  void affine_image(Variable var,
                    const Linear_Expression& expr,
                    Coefficient_traits::const_reference denominator
                    = Coefficient_one());

  // FIXME: To be completed.
  /*! \brief
    Assigns to \p *this the \ref affine_form_relation "affine form image"
    of \p *this under the function mapping variable \p var into the
    affine expression(s) specified by \p lf.

    \param var
    The variable to which the affine expression is assigned.

    \param lf
    The linear form on intervals with floating point coefficients that
    defines the affine expression. ALL of its coefficients MUST be bounded.

    \exception std::invalid_argument
    Thrown if \p lf and \p *this are dimension-incompatible or if \p var
    is not a dimension of \p *this.
  */
  template <typename Interval_Info>
  void affine_form_image(Variable var,
                        const Linear_Form<Interval<T, Interval_Info> >& lf);

  /*! \brief
    Assigns to \p *this the
    \ref Single_Update_Affine_Functions "affine preimage"
    of \p *this under the function mapping variable \p var into the
    affine expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is substituted.

    \param expr
    The numerator of the affine expression.

    \param denominator
    The denominator of the affine expression.

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this
    are dimension-incompatible or if \p var is not a dimension of \p *this.
  */
  void affine_preimage(Variable var,
                       const Linear_Expression& expr,
                       Coefficient_traits::const_reference denominator
                       = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "affine relation"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym.

    \param var
    The left hand side variable of the generalized affine transfer function.

    \param relsym
    The relation symbol.

    \param expr
    The numerator of the right hand side affine expression.

    \param denominator
    The denominator of the right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this
    are dimension-incompatible or if \p var is not a dimension
    of \p *this or if \p relsym is a strict relation symbol.
  */
  void generalized_affine_image(Variable var,
                                Relation_Symbol relsym,
                                const Linear_Expression& expr,
                                Coefficient_traits::const_reference denominator
                                = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "affine relation"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.

    \param lhs
    The left hand side affine expression.

    \param relsym
    The relation symbol.

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs
    or if \p relsym is a strict relation symbol.
  */
  void generalized_affine_image(const Linear_Expression& lhs,
                                Relation_Symbol relsym,
                                const Linear_Expression& rhs);

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "affine relation"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym.

    \param var
    The left hand side variable of the generalized affine transfer function.

    \param relsym
    The relation symbol.

    \param expr
    The numerator of the right hand side affine expression.

    \param denominator
    The denominator of the right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this
    are dimension-incompatible or if \p var is not a dimension
    of \p *this or if \p relsym is a strict relation symbol.
  */
  void generalized_affine_preimage(Variable var,
                                   Relation_Symbol relsym,
                                   const Linear_Expression& expr,
                                   Coefficient_traits::const_reference
                                   denominator = Coefficient_one());

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "affine relation"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.

    \param lhs
    The left hand side affine expression.

    \param relsym
    The relation symbol.

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs
    or if \p relsym is a strict relation symbol.
  */
  void generalized_affine_preimage(const Linear_Expression& lhs,
                                   Relation_Symbol relsym,
                                   const Linear_Expression& rhs);

  /*!
    \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_image(Variable var,
                            const Linear_Expression& lb_expr,
                            const Linear_Expression& ub_expr,
                            Coefficient_traits::const_reference denominator
                            = Coefficient_one());

  /*!
    \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_preimage(Variable var,
                               const Linear_Expression& lb_expr,
                               const Linear_Expression& ub_expr,
                               Coefficient_traits::const_reference denominator
                               = Coefficient_one());
  /*! \brief
    Assigns to \p *this the result of computing the
    \ref Time_Elapse_Operator "time-elapse" between \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void time_elapse_assign(const BD_Shape& y);

  /*! \brief
    \ref Wrapping_Operator "Wraps" the specified dimensions of the
    vector space.

    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be wrapped.

    \param w
    The width of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param r
    The representation of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param o
    The overflow behavior of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param cs_p
    Possibly null pointer to a constraint system whose variables
    are contained in \p vars.  If <CODE>*cs_p</CODE> depends on
    variables not in \p vars, the behavior is undefined.
    When non-null, the pointed-to constraint system is assumed to
    represent the conditional or looping construct guard with respect
    to which wrapping is performed.  Since wrapping requires the
    computation of upper bounds and due to non-distributivity of
    constraint refinement over upper bounds, passing a constraint
    system in this way can be more precise than refining the result of
    the wrapping operation with the constraints in <CODE>*cs_p</CODE>.

    \param complexity_threshold
    A precision parameter of the \ref Wrapping_Operator "wrapping operator":
    higher values result in possibly improved precision.

    \param wrap_individually
    <CODE>true</CODE> if the dimensions should be wrapped individually
    (something that results in much greater efficiency to the detriment of
    precision).

    \exception std::invalid_argument
    Thrown if <CODE>*cs_p</CODE> is dimension-incompatible with
    \p vars, or if \p *this is dimension-incompatible \p vars or with
    <CODE>*cs_p</CODE>.
  */
  void wrap_assign(const Variables_Set& vars,
                   Bounded_Integer_Type_Width w,
                   Bounded_Integer_Type_Representation r,
                   Bounded_Integer_Type_Overflow o,
                   const Constraint_System* cs_p = 0,
                   unsigned complexity_threshold = 16,
                   bool wrap_individually = true);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates for the space dimensions corresponding to \p vars.

    \param vars
    Points with non-integer coordinates for these variables/space-dimensions
    can be discarded.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(const Variables_Set& vars,
                                    Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  //! Assigns to \p *this its topological closure.
  void topological_closure_assign();

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref CC76_extrapolation "CC76-extrapolation" between \p *this and \p y.

    \param y
    A BDS that <EM>must</EM> be contained in \p *this.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void CC76_extrapolation_assign(const BD_Shape& y, unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref CC76_extrapolation "CC76-extrapolation" between \p *this and \p y.

    \param y
    A BDS that <EM>must</EM> be contained in \p *this.

    \param first
    An iterator referencing the first stop-point.

    \param last
    An iterator referencing one past the last stop-point.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  template <typename Iterator>
  void CC76_extrapolation_assign(const BD_Shape& y,
                                 Iterator first, Iterator last,
                                 unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref BHMZ05_widening "BHMZ05-widening" of \p *this and \p y.

    \param y
    A BDS that <EM>must</EM> be contained in \p *this.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void BHMZ05_widening_assign(const BD_Shape& y, unsigned* tp = 0);

  /*! \brief
    Improves the result of the \ref BHMZ05_widening "BHMZ05-widening"
    computation by also enforcing those constraints in \p cs that are
    satisfied by all the points of \p *this.

    \param y
    A BDS that <EM>must</EM> be contained in \p *this.

    \param cs
    The system of constraints used to improve the widened BDS.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cs are dimension-incompatible or
    if \p cs contains a strict inequality.
  */
  void limited_BHMZ05_extrapolation_assign(const BD_Shape& y,
                                           const Constraint_System& cs,
                                           unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of restoring in \p y the constraints
    of \p *this that were lost by
    \ref CC76_extrapolation "CC76-extrapolation" applications.

    \param y
    A BDS that <EM>must</EM> contain \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.

    \note
    As was the case for widening operators, the argument \p y is meant to
    denote the value computed in the previous iteration step, whereas
    \p *this denotes the value computed in the current iteration step
    (in the <EM>decreasing</EM> iteration sequence). Hence, the call
    <CODE>x.CC76_narrowing_assign(y)</CODE> will assign to \p x
    the result of the computation \f$\mathtt{y} \Delta \mathtt{x}\f$.
  */
  void CC76_narrowing_assign(const BD_Shape& y);

  /*! \brief
    Improves the result of the \ref CC76_extrapolation "CC76-extrapolation"
    computation by also enforcing those constraints in \p cs that are
    satisfied by all the points of \p *this.

    \param y
    A BDS that <EM>must</EM> be contained in \p *this.

    \param cs
    The system of constraints used to improve the widened BDS.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cs are dimension-incompatible or
    if \p cs contains a strict inequality.
  */
  void limited_CC76_extrapolation_assign(const BD_Shape& y,
                                         const Constraint_System& cs,
                                         unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref H79_widening "H79-widening" between \p *this and \p y.

    \param y
    A BDS that <EM>must</EM> be contained in \p *this.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void H79_widening_assign(const BD_Shape& y, unsigned* tp = 0);

  //! Same as H79_widening_assign(y, tp).
  void widening_assign(const BD_Shape& y, unsigned* tp = 0);

  /*! \brief
    Improves the result of the \ref H79_widening "H79-widening"
    computation by also enforcing those constraints in \p cs that are
    satisfied by all the points of \p *this.

    \param y
    A BDS that <EM>must</EM> be contained in \p *this.

    \param cs
    The system of constraints used to improve the widened BDS.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cs are dimension-incompatible.
  */
  void limited_H79_extrapolation_assign(const BD_Shape& y,
                                        const Constraint_System& cs,
                                        unsigned* tp = 0);

  //@} Space-Dimension Preserving Member Functions that May Modify [...]

  //! \name Member Functions that May Modify the Dimension of the Vector Space
  //@{

  //! Adds \p m new dimensions and embeds the old BDS into the new space.
  /*!
    \param m
    The number of dimensions to add.

    The new dimensions will be those having the highest indexes in the new
    BDS, which is defined by a system of bounded differences in which the
    variables running through the new dimensions are unconstrained.
    For instance, when starting from the BDS \f$\cB \sseq \Rset^2\f$
    and adding a third dimension, the result will be the BDS
    \f[
      \bigl\{\,
        (x, y, z)^\transpose \in \Rset^3
      \bigm|
        (x, y)^\transpose \in \cB
      \,\bigr\}.
    \f]
  */
  void add_space_dimensions_and_embed(dimension_type m);

  /*! \brief
    Adds \p m new dimensions to the BDS and does not embed it in
    the new vector space.

    \param m
    The number of dimensions to add.

    The new dimensions will be those having the highest indexes in the
    new BDS, which is defined by a system of bounded differences in
    which the variables running through the new dimensions are all
    constrained to be equal to 0.
    For instance, when starting from the BDS \f$\cB \sseq \Rset^2\f$
    and adding a third dimension, the result will be the BDS
    \f[
      \bigl\{\,
        (x, y, 0)^\transpose \in \Rset^3
      \bigm|
        (x, y)^\transpose \in \cB
      \,\bigr\}.
    \f]
  */
  void add_space_dimensions_and_project(dimension_type m);

  /*! \brief
    Assigns to \p *this the \ref Concatenating_Polyhedra "concatenation"
    of \p *this and \p y, taken in this order.

    \exception std::length_error
    Thrown if the concatenation would cause the vector space
    to exceed dimension <CODE>max_space_dimension()</CODE>.
  */
  void concatenate_assign(const BD_Shape& y);

  //! Removes all the specified dimensions.
  /*!
    \param vars
    The set of Variable objects corresponding to the dimensions to be removed.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the Variable
    objects contained in \p vars.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  /*! \brief
    Removes the higher dimensions so that the resulting space
    will have dimension \p new_dimension.

    \exception std::invalid_argument
    Thrown if \p new_dimension is greater than the space dimension
    of \p *this.
  */
  void remove_higher_space_dimensions(dimension_type new_dimension);

  /*! \brief
    Remaps the dimensions of the vector space according to
    a \ref Mapping_the_Dimensions_of_the_Vector_Space "partial function".

    \param pfunc
    The partial function specifying the destiny of each dimension.

    The template type parameter Partial_Function must provide
    the following methods.
    \code
      bool has_empty_codomain() const
    \endcode
    returns <CODE>true</CODE> if and only if the represented partial
    function has an empty co-domain (i.e., it is always undefined).
    The <CODE>has_empty_codomain()</CODE> method will always be called
    before the methods below.  However, if
    <CODE>has_empty_codomain()</CODE> returns <CODE>true</CODE>, none
    of the functions below will be called.
    \code
      dimension_type max_in_codomain() const
    \endcode
    returns the maximum value that belongs to the co-domain
    of the partial function.
    \code
      bool maps(dimension_type i, dimension_type& j) const
    \endcode
    Let \f$f\f$ be the represented function and \f$k\f$ be the value
    of \p i.  If \f$f\f$ is defined in \f$k\f$, then \f$f(k)\f$ is
    assigned to \p j and <CODE>true</CODE> is returned.
    If \f$f\f$ is undefined in \f$k\f$, then <CODE>false</CODE> is
    returned.

    The result is undefined if \p pfunc does not encode a partial
    function with the properties described in the
    \ref Mapping_the_Dimensions_of_the_Vector_Space
    "specification of the mapping operator".
  */
  template <typename Partial_Function>
  void map_space_dimensions(const Partial_Function& pfunc);

  //! Creates \p m copies of the space dimension corresponding to \p var.
  /*!
    \param var
    The variable corresponding to the space dimension to be replicated;

    \param m
    The number of replicas to be created.

    \exception std::invalid_argument
    Thrown if \p var does not correspond to a dimension of the vector space.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the
    vector space to exceed dimension <CODE>max_space_dimension()</CODE>.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    and <CODE>var</CODE> has space dimension \f$k \leq n\f$,
    then the \f$k\f$-th space dimension is
    \ref expand_space_dimension "expanded" to \p m new space dimensions
    \f$n\f$, \f$n+1\f$, \f$\dots\f$, \f$n+m-1\f$.
  */
  void expand_space_dimension(Variable var, dimension_type m);

  //! Folds the space dimensions in \p vars into \p dest.
  /*!
    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be folded;

    \param dest
    The variable corresponding to the space dimension that is the
    destination of the folding operation.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p dest or with
    one of the Variable objects contained in \p vars.
    Also thrown if \p dest is contained in \p vars.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    <CODE>dest</CODE> has space dimension \f$k \leq n\f$,
    \p vars is a set of variables whose maximum space dimension
    is also less than or equal to \f$n\f$, and \p dest is not a member
    of \p vars, then the space dimensions corresponding to
    variables in \p vars are \ref fold_space_dimensions "folded"
    into the \f$k\f$-th space dimension.
  */
  void fold_space_dimensions(const Variables_Set& vars, Variable dest);

  //! Refines \p store with the constraints defining \p *this.
  /*!
    \param store
    The interval floating point abstract store to refine.
  */
  template <typename Interval_Info>
  void refine_fp_interval_abstract_store(Box<Interval<T, Interval_Info> >&
                                         store) const;


  //@} // Member Functions that May Modify the Dimension of the Vector Space

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns a 32-bit hash code for \p *this.

    If \p x and \p y are such that <CODE>x == y</CODE>,
    then <CODE>x.hash_code() == y.hash_code()</CODE>.
  */
  int32_t hash_code() const;

  friend bool operator==<T>(const BD_Shape<T>& x, const BD_Shape<T>& y);

  template <typename Temp, typename To, typename U>
  friend bool Parma_Polyhedra_Library
  ::rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const BD_Shape<U>& x, const BD_Shape<U>& y,
                                const Rounding_Dir dir,
                                Temp& tmp0, Temp& tmp1, Temp& tmp2);
  template <typename Temp, typename To, typename U>
  friend bool Parma_Polyhedra_Library
  ::euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                              const BD_Shape<U>& x, const BD_Shape<U>& y,
                              const Rounding_Dir dir,
                              Temp& tmp0, Temp& tmp1, Temp& tmp2);
  template <typename Temp, typename To, typename U>
  friend bool Parma_Polyhedra_Library
  ::l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const BD_Shape<U>& x, const BD_Shape<U>& y,
                               const Rounding_Dir dir,
                               Temp& tmp0, Temp& tmp1, Temp& tmp2);

private:
  template <typename U> friend class Parma_Polyhedra_Library::BD_Shape;
  template <typename Interval> friend class Parma_Polyhedra_Library::Box;

  //! The matrix representing the system of bounded differences.
  DB_Matrix<N> dbm;

#define PPL_IN_BD_Shape_CLASS
/* Automatically generated from PPL source file ../src/BDS_Status_idefs.hh line 1. */
/* BD_Shape<T>::Status class declaration.
*/


#ifndef PPL_IN_BD_Shape_CLASS
#error "Do not include BDS_Status_idefs.hh directly; use BD_Shape_defs.hh instead"
#endif

//! A conjunctive assertion about a BD_Shape<T> object.
/*! \ingroup PPL_CXX_interface
  The assertions supported are:
  - <EM>zero-dim universe</EM>: the BDS is the zero-dimensional
    vector space \f$\Rset^0 = \{\cdot\}\f$;
  - <EM>empty</EM>: the BDS is the empty set;
  - <EM>shortest-path closed</EM>: the BDS is represented by a shortest-path
    closed system of bounded differences, so that all the constraints are
    as tight as possible;
  - <EM>shortest-path reduced</EM>: the BDS is represented by a shortest-path
    closed system of bounded differences and each constraint in such a system
    is marked as being either redundant or non-redundant.

  Not all the conjunctions of these elementary assertions constitute
  a legal Status.  In fact:
  - <EM>zero-dim universe</EM> excludes any other assertion;
  - <EM>empty</EM>: excludes any other assertion;
  - <EM>shortest-path reduced</EM> implies <EM>shortest-path closed</EM>.
*/
class Status {
public:
  //! By default Status is the <EM>zero-dim universe</EM> assertion.
  Status();

  //! \name Test, remove or add an individual assertion from the conjunction.
  //@{
  bool test_zero_dim_univ() const;
  void reset_zero_dim_univ();
  void set_zero_dim_univ();

  bool test_empty() const;
  void reset_empty();
  void set_empty();

  bool test_shortest_path_closed() const;
  void reset_shortest_path_closed();
  void set_shortest_path_closed();

  bool test_shortest_path_reduced() const;
  void reset_shortest_path_reduced();
  void set_shortest_path_reduced();
  //@}

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

private:
  //! Status is implemented by means of a finite bitset.
  typedef unsigned int flags_t;

  //! \name Bit-masks for the individual assertions.
  //@{
  static const flags_t ZERO_DIM_UNIV         = 0U;
  static const flags_t EMPTY                 = 1U << 0;
  static const flags_t SHORTEST_PATH_CLOSED  = 1U << 1;
  static const flags_t SHORTEST_PATH_REDUCED = 1U << 2;
  //@}

  //! This holds the current bitset.
  flags_t flags;

  //! Construct from a bit-mask.
  Status(flags_t mask);

  //! Check whether <EM>all</EM> bits in \p mask are set.
  bool test_all(flags_t mask) const;

  //! Check whether <EM>at least one</EM> bit in \p mask is set.
  bool test_any(flags_t mask) const;

  //! Set the bits in \p mask.
  void set(flags_t mask);

  //! Reset the bits in \p mask.
  void reset(flags_t mask);
};

/* Automatically generated from PPL source file ../src/BD_Shape_defs.hh line 1944. */
#undef PPL_IN_BD_Shape_CLASS

  //! The status flags to keep track of the internal state.
  Status status;

  //! A matrix indicating which constraints are redundant.
  Bit_Matrix redundancy_dbm;

  //! Returns <CODE>true</CODE> if the BDS is the zero-dimensional universe.
  bool marked_zero_dim_univ() const;

  /*! \brief
    Returns <CODE>true</CODE> if the BDS is known to be empty.

    The return value <CODE>false</CODE> does not necessarily
    implies that \p *this is non-empty.
  */
  bool marked_empty() const;

  /*! \brief
    Returns <CODE>true</CODE> if the system of bounded differences
    is known to be shortest-path closed.

    The return value <CODE>false</CODE> does not necessarily
    implies that <CODE>this->dbm</CODE> is not shortest-path closed.
  */
  bool marked_shortest_path_closed() const;

  /*! \brief
    Returns <CODE>true</CODE> if the system of bounded differences
    is known to be shortest-path reduced.

    The return value <CODE>false</CODE> does not necessarily
    implies that <CODE>this->dbm</CODE> is not shortest-path reduced.
  */
  bool marked_shortest_path_reduced() const;

  //! Turns \p *this into an empty BDS.
  void set_empty();

  //! Turns \p *this into an zero-dimensional universe BDS.
  void set_zero_dim_univ();

  //! Marks \p *this as shortest-path closed.
  void set_shortest_path_closed();

  //! Marks \p *this as shortest-path closed.
  void set_shortest_path_reduced();

  //! Marks \p *this as possibly not shortest-path closed.
  void reset_shortest_path_closed();

  //! Marks \p *this as possibly not shortest-path reduced.
  void reset_shortest_path_reduced();

  //! Assigns to <CODE>this->dbm</CODE> its shortest-path closure.
  void shortest_path_closure_assign() const;

  /*! \brief
    Assigns to <CODE>this->dbm</CODE> its shortest-path closure and
    records into <CODE>this->redundancy_dbm</CODE> which of the entries
    in <CODE>this->dbm</CODE> are redundant.
  */
  void shortest_path_reduction_assign() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if <CODE>this->dbm</CODE>
    is shortest-path closed and <CODE>this->redundancy_dbm</CODE>
    correctly flags the redundant entries in <CODE>this->dbm</CODE>.
  */
  bool is_shortest_path_reduced() const;

  /*! \brief
    Incrementally computes shortest-path closure, assuming that only
    constraints affecting variable \p var need to be considered.

    \note
    It is assumed that \c *this, which was shortest-path closed,
    has only been modified by adding constraints affecting variable
    \p var. If this assumption is not satisfied, i.e., if a non-redundant
    constraint not affecting variable \p var has been added, the behavior
    is undefined.
  */
  void incremental_shortest_path_closure_assign(Variable var) const;

  //! Checks if and how \p expr is bounded in \p *this.
  /*!
    Returns <CODE>true</CODE> if and only if \p from_above is
    <CODE>true</CODE> and \p expr is bounded from above in \p *this,
    or \p from_above is <CODE>false</CODE> and \p expr is bounded
    from below in \p *this.

    \param expr
    The linear expression to test;

    \param from_above
    <CODE>true</CODE> if and only if the boundedness of interest is
    "from above".

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds(const Linear_Expression& expr, bool from_above) const;

  //! Maximizes or minimizes \p expr subject to \p *this.
  /*!
    \param expr
    The linear expression to be maximized or minimized subject to \p
    *this;

    \param maximize
    <CODE>true</CODE> if maximization is what is wanted;

    \param ext_n
    The numerator of the extremum value;

    \param ext_d
    The denominator of the extremum value;

    \param included
    <CODE>true</CODE> if and only if the extremum of \p expr can
    actually be reached in \p * this;

    \param g
    When maximization or minimization succeeds, will be assigned
    a point or closure point where \p expr reaches the
    corresponding extremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded in the appropriate
    direction, <CODE>false</CODE> is returned and \p ext_n, \p ext_d,
    \p included and \p g are left untouched.
  */
  bool max_min(const Linear_Expression& expr,
               bool maximize,
               Coefficient& ext_n, Coefficient& ext_d, bool& included,
               Generator& g) const;

  //! Maximizes or minimizes \p expr subject to \p *this.
  /*!
    \param expr
    The linear expression to be maximized or minimized subject to \p
    *this;

    \param maximize
    <CODE>true</CODE> if maximization is what is wanted;

    \param ext_n
    The numerator of the extremum value;

    \param ext_d
    The denominator of the extremum value;

    \param included
    <CODE>true</CODE> if and only if the extremum of \p expr can
    actually be reached in \p * this;

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded in the appropriate
    direction, <CODE>false</CODE> is returned and \p ext_n, \p ext_d,
    \p included and \p point are left untouched.
  */
  bool max_min(const Linear_Expression& expr,
               bool maximize,
               Coefficient& ext_n, Coefficient& ext_d, bool& included) const;

  /*! \brief
    If the upper bound of \p *this and \p y is exact it is assigned
    to \p *this and \c true is returned, otherwise \c false is returned.

    Current implementation is based on a variant of Algorithm 4.1 in
      A. Bemporad, K. Fukuda, and F. D. Torrisi
      <em>Convexity Recognition of the Union of Polyhedra</em>
      Technical Report AUT00-13, ETH Zurich, 2000
    tailored to the special case of BD shapes.

    \note
    It is assumed that \p *this and \p y are dimension-compatible;
    if the assumption does not hold, the behavior is undefined.
  */
  bool BFT00_upper_bound_assign_if_exact(const BD_Shape& y);

  /*! \brief
    If the upper bound of \p *this and \p y is exact it is assigned
    to \p *this and \c true is returned, otherwise \c false is returned.

    Implementation for the rational (resp., integer) case is based on
    Theorem 5.2 (resp. Theorem 5.3) of \ref BHZ09b "[BHZ09b]".
    The Boolean template parameter \c integer_upper_bound allows for
    choosing between the rational and integer upper bound algorithms.

    \note
    It is assumed that \p *this and \p y are dimension-compatible;
    if the assumption does not hold, the behavior is undefined.

    \note
    The integer case is only enabled if T is an integer data type.
  */
  template <bool integer_upper_bound>
  bool BHZ09_upper_bound_assign_if_exact(const BD_Shape& y);

  /*! \brief
    Uses the constraint \p c to refine \p *this.

    \param c
    The constraint to be added. Non BD constraints are ignored.

    \warning
    If \p c and \p *this are dimension-incompatible,
    the behavior is undefined.
  */
  void refine_no_check(const Constraint& c);

  /*! \brief
    Uses the congruence \p cg to refine \p *this.

    \param cg
    The congruence to be added.
    Nontrivial proper congruences are ignored.
    Non BD equalities are ignored.

    \warning
    If \p cg and \p *this are dimension-incompatible,
    the behavior is undefined.
  */
  void refine_no_check(const Congruence& cg);

  //! Adds the constraint <CODE>dbm[i][j] \<= k</CODE>.
  void add_dbm_constraint(dimension_type i, dimension_type j, const N& k);

  //! Adds the constraint <CODE>dbm[i][j] \<= numer/denom</CODE>.
  void add_dbm_constraint(dimension_type i, dimension_type j,
                          Coefficient_traits::const_reference numer,
                          Coefficient_traits::const_reference denom);

  /*! \brief
    Adds to the BDS the constraint
    \f$\mathrm{var} \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$.

    Note that the coefficient of \p var in \p expr is null.
  */
  void refine(Variable var, Relation_Symbol relsym,
              const Linear_Expression& expr,
              Coefficient_traits::const_reference denominator
              = Coefficient_one());

  //! Removes all the constraints on row/column \p v.
  void forget_all_dbm_constraints(dimension_type v);
  //! Removes all binary constraints on row/column \p v.
  void forget_binary_dbm_constraints(dimension_type v);

  //! An helper function for the computation of affine relations.
  /*!
    For each dbm index \p u (less than or equal to \p last_v and different
    from \p v), deduce constraints of the form <CODE>v - u \<= c</CODE>,
    starting from \p ub_v which is an upper bound for \p v.

    The shortest-path closure is able to deduce the constraint
    <CODE>v - u \<= ub_v - lb_u</CODE>. We can be more precise if variable
    \p u played an active role in the computation of the upper bound for
    \p v, i.e., if the corresponding coefficient
    <CODE>q == sc_expr[u]/sc_denom</CODE> is greater than zero. In particular:
      - if <CODE>q \>= 1</CODE>, then <CODE>v - u \<= ub_v - ub_u</CODE>;
      - if <CODE>0 \< q \< 1</CODE>, then
        <CODE>v - u \<= ub_v - (q*ub_u + (1-q)*lb_u)</CODE>.
  */
  void deduce_v_minus_u_bounds(dimension_type v,
                               dimension_type last_v,
                               const Linear_Expression& sc_expr,
                               Coefficient_traits::const_reference sc_denom,
                               const N& ub_v);

  /*! \brief
    Auxiliary function for \ref affine_form_relation "affine form image" that
    handle the general case: \f$l = c\f$
  */
  template <typename Interval_Info>
  void inhomogeneous_affine_form_image(const dimension_type& var_id,
                                       const Interval<T, Interval_Info>& b);

  /*! \brief
    Auxiliary function for \ref affine_form_relation "affine form
    image" that handle the general case: \f$l = ax + c\f$
  */
  template <typename Interval_Info>
  void
  one_variable_affine_form_image(const dimension_type& var_id,
                                 const Interval<T, Interval_Info>& b,
                                 const Interval<T, Interval_Info>& w_coeff,
                                 const dimension_type& w_id,
                                 const dimension_type& space_dim);

  /*! \brief
    Auxiliary function for \ref affine_form_relation "affine form image" that
    handle the general case: \f$l = ax + by + c\f$
  */
  template <typename Interval_Info>
  void
  two_variables_affine_form_image(const dimension_type& var_id,
                                  const Linear_Form<Interval<T,Interval_Info> >& lf,
                                  const dimension_type& space_dim);

  /*! \brief
    Auxiliary function for refine with linear form that handle
    the general case: \f$l = ax + c\f$
  */
  template <typename Interval_Info>
  void
  left_inhomogeneous_refine(const dimension_type& right_t,
                            const dimension_type& right_w_id,
                            const Linear_Form<Interval<T, Interval_Info> >& left,
                            const Linear_Form<Interval<T, Interval_Info> >& right);

  /*! \brief
    Auxiliary function for refine with linear form that handle
    the general case: \f$ax + b = cy + d\f$
  */
  template <typename Interval_Info>
  void
  left_one_var_refine(const dimension_type& left_w_id,
                      const dimension_type& right_t,
                      const dimension_type& right_w_id,
                      const Linear_Form<Interval<T, Interval_Info> >& left,
                      const Linear_Form<Interval<T, Interval_Info> >& right);

  /*! \brief
    Auxiliary function for refine with linear form that handle
    the general case.
  */
  template <typename Interval_Info>
  void general_refine(const dimension_type& left_w_id,
                      const dimension_type& right_w_id,
                      const Linear_Form<Interval<T, Interval_Info> >& left,
                      const Linear_Form<Interval<T, Interval_Info> >& right);

  template <typename Interval_Info>
  void linear_form_upper_bound(const Linear_Form<Interval<T, Interval_Info> >&
                               lf,
                               N& result) const;

  //! An helper function for the computation of affine relations.
  /*!
    For each dbm index \p u (less than or equal to \p last_v and different
    from \p v), deduce constraints of the form <CODE>u - v \<= c</CODE>,
    starting from \p minus_lb_v which is a lower bound for \p v.

    The shortest-path closure is able to deduce the constraint
    <CODE>u - v \<= ub_u - lb_v</CODE>. We can be more precise if variable
    \p u played an active role in the computation of the lower bound for
    \p v, i.e., if the corresponding coefficient
    <CODE>q == sc_expr[u]/sc_denom</CODE> is greater than zero.
    In particular:
      - if <CODE>q \>= 1</CODE>, then <CODE>u - v \<= lb_u - lb_v</CODE>;
      - if <CODE>0 \< q \< 1</CODE>, then
        <CODE>u - v \<= (q*lb_u + (1-q)*ub_u) - lb_v</CODE>.
  */
  void deduce_u_minus_v_bounds(dimension_type v,
                               dimension_type last_v,
                               const Linear_Expression& sc_expr,
                               Coefficient_traits::const_reference sc_denom,
                               const N& minus_lb_v);

  /*! \brief
    Adds to \p limiting_shape the bounded differences in \p cs
    that are satisfied by \p *this.
  */
  void get_limiting_shape(const Constraint_System& cs,
                          BD_Shape& limiting_shape) const;

  //! Compute the (zero-equivalence classes) predecessor relation.
  /*!
    It is assumed that the BDS is not empty and shortest-path closed.
  */
  void compute_predecessors(std::vector<dimension_type>& predecessor) const;

  //! Compute the leaders of zero-equivalence classes.
  /*!
    It is assumed that the BDS is not empty and shortest-path closed.
  */
  void compute_leaders(std::vector<dimension_type>& leaders) const;

  void drop_some_non_integer_points_helper(N& elem);

  friend std::ostream&
  Parma_Polyhedra_Library::IO_Operators
  ::operator<<<>(std::ostream& s, const BD_Shape<T>& c);

  //! \name Exception Throwers
  //@{
  void throw_dimension_incompatible(const char* method,
                                    const BD_Shape& y) const;

  void throw_dimension_incompatible(const char* method,
                                    dimension_type required_dim) const;

  void throw_dimension_incompatible(const char* method,
                                    const Constraint& c) const;

  void throw_dimension_incompatible(const char* method,
                                    const Congruence& cg) const;

  void throw_dimension_incompatible(const char* method,
                                    const Generator& g) const;

  void throw_dimension_incompatible(const char* method,
                                    const char* le_name,
                                    const Linear_Expression& le) const;

  template<typename Interval_Info>
  void
  throw_dimension_incompatible(const char* method,
                               const char* lf_name,
                               const Linear_Form<Interval<T, Interval_Info> >&
                               lf) const;

  static void throw_expression_too_complex(const char* method,
                                           const Linear_Expression& le);

  static void throw_invalid_argument(const char* method, const char* reason);
  //@} // Exception Throwers
};

/* Automatically generated from PPL source file ../src/BDS_Status_inlines.hh line 1. */
/* BD_Shape<T>::Status class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

template <typename T>
inline
BD_Shape<T>::Status::Status(flags_t mask)
  : flags(mask) {
}

template <typename T>
inline
BD_Shape<T>::Status::Status()
  : flags(ZERO_DIM_UNIV) {
}

template <typename T>
inline bool
BD_Shape<T>::Status::test_all(flags_t mask) const {
  return (flags & mask) == mask;
}

template <typename T>
inline bool
BD_Shape<T>::Status::test_any(flags_t mask) const {
  return (flags & mask) != 0;
}

template <typename T>
inline void
BD_Shape<T>::Status::set(flags_t mask) {
  flags |= mask;
}

template <typename T>
inline void
BD_Shape<T>::Status::reset(flags_t mask) {
  flags &= ~mask;
}

template <typename T>
inline bool
BD_Shape<T>::Status::test_zero_dim_univ() const {
  return flags == ZERO_DIM_UNIV;
}

template <typename T>
inline void
BD_Shape<T>::Status::reset_zero_dim_univ() {
  // This is a no-op if the current status is not zero-dim.
  if (flags == ZERO_DIM_UNIV) {
    // In the zero-dim space, if it is not the universe it is empty.
    flags = EMPTY;
  }
}

template <typename T>
inline void
BD_Shape<T>::Status::set_zero_dim_univ() {
  // Zero-dim universe is incompatible with anything else.
  flags = ZERO_DIM_UNIV;
}

template <typename T>
inline bool
BD_Shape<T>::Status::test_empty() const {
  return test_any(EMPTY);
}

template <typename T>
inline void
BD_Shape<T>::Status::reset_empty() {
  reset(EMPTY);
}

template <typename T>
inline void
BD_Shape<T>::Status::set_empty() {
  flags = EMPTY;
}

template <typename T>
inline bool
BD_Shape<T>::Status::test_shortest_path_closed() const {
  return test_any(SHORTEST_PATH_CLOSED);
}

template <typename T>
inline void
BD_Shape<T>::Status::reset_shortest_path_closed() {
  // A system is reduced only if it is also closed.
  reset(SHORTEST_PATH_CLOSED | SHORTEST_PATH_REDUCED);
}

template <typename T>
inline void
BD_Shape<T>::Status::set_shortest_path_closed() {
  set(SHORTEST_PATH_CLOSED);
}

template <typename T>
inline bool
BD_Shape<T>::Status::test_shortest_path_reduced() const {
  return test_any(SHORTEST_PATH_REDUCED);
}

template <typename T>
inline void
BD_Shape<T>::Status::reset_shortest_path_reduced() {
  reset(SHORTEST_PATH_REDUCED);
}

template <typename T>
inline void
BD_Shape<T>::Status::set_shortest_path_reduced() {
  PPL_ASSERT(test_shortest_path_closed());
  set(SHORTEST_PATH_REDUCED);
}

template <typename T>
bool
BD_Shape<T>::Status::OK() const {
  if (test_zero_dim_univ()) {
    // Zero-dim universe is OK.
    return true;
  }

  if (test_empty()) {
    Status copy = *this;
    copy.reset_empty();
    if (copy.test_zero_dim_univ()) {
      return true;
    }
    else {
#ifndef NDEBUG
      std::cerr << "The empty flag is incompatible with any other one."
                << std::endl;
#endif
      return false;
    }
  }

  // Shortest-path reduction implies shortest-path closure.
  if (test_shortest_path_reduced()) {
    if (test_shortest_path_closed()) {
      return true;
    }
    else {
#ifndef NDEBUG
      std::cerr << "The shortest-path reduction flag should also imply "
                << "the closure flag."
                << std::endl;
#endif
      return false;
    }
  }

  // Any other case is OK.
  return true;
}


namespace Implementation {

namespace BD_Shapes {

// These are the keywords that indicate the individual assertions.
extern const char* zero_dim_univ;
extern const char* empty;
extern const char* sp_closed;
extern const char* sp_reduced;
const char yes = '+';
const char no = '-';
const char separator = ' ';

/*! \relates Parma_Polyhedra_Library::BD_Shape::Status
  Reads a keyword and its associated on/off flag from \p s.
  Returns <CODE>true</CODE> if the operation is successful,
  returns <CODE>false</CODE> otherwise.
  When successful, \p positive is set to <CODE>true</CODE> if the flag
  is on; it is set to <CODE>false</CODE> otherwise.
*/
inline bool
get_field(std::istream& s, const char* keyword, bool& positive) {
  std::string str;
  if (!(s >> str)
      || (str[0] != yes && str[0] != no)
      || str.substr(1) != keyword) {
    return false;
  }
  positive = (str[0] == yes);
  return true;
}

} // namespace BD_Shapes

} // namespace Implementation

template <typename T>
void
BD_Shape<T>::Status::ascii_dump(std::ostream& s) const {
  using namespace Implementation::BD_Shapes;
  s << (test_zero_dim_univ() ? yes : no) << zero_dim_univ << separator
    << (test_empty() ? yes : no) << empty << separator
    << separator
    << (test_shortest_path_closed() ? yes : no) << sp_closed << separator
    << (test_shortest_path_reduced() ? yes : no) << sp_reduced << separator;
}

PPL_OUTPUT_TEMPLATE_DEFINITIONS_ASCII_ONLY(T, BD_Shape<T>::Status)

template <typename T>
bool
BD_Shape<T>::Status::ascii_load(std::istream& s) {
  using namespace Implementation::BD_Shapes;
  PPL_UNINITIALIZED(bool, positive);

  if (!get_field(s, zero_dim_univ, positive)) {
    return false;
  }
  if (positive) {
    set_zero_dim_univ();
  }

  if (!get_field(s, empty, positive)) {
    return false;
  }
  if (positive) {
    set_empty();
  }

  if (!get_field(s, sp_closed, positive)) {
    return false;
  }
  if (positive) {
    set_shortest_path_closed();
  }
  else {
    reset_shortest_path_closed();
  }

  if (!get_field(s, sp_reduced, positive)) {
    return false;
  }
  if (positive) {
    set_shortest_path_reduced();
  }
  else {
    reset_shortest_path_reduced();
  }

  // Check invariants.
  PPL_ASSERT(OK());
  return true;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/BD_Shape_inlines.hh line 1. */
/* BD_Shape class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/C_Polyhedron_defs.hh line 1. */
/* C_Polyhedron class declaration.
*/


/* Automatically generated from PPL source file ../src/C_Polyhedron_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class C_Polyhedron;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/NNC_Polyhedron_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class NNC_Polyhedron;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/C_Polyhedron_defs.hh line 33. */

//! A closed convex polyhedron.
/*! \ingroup PPL_CXX_interface
    An object of the class C_Polyhedron represents a
    <EM>topologically closed</EM> convex polyhedron
    in the vector space \f$\Rset^n\f$.

    When building a closed polyhedron starting from
    a system of constraints, an exception is thrown if the system
    contains a <EM>strict inequality</EM> constraint.
    Similarly, an exception is thrown when building a closed polyhedron
    starting from a system of generators containing a <EM>closure point</EM>.

    \note
    Such an exception will be obtained even if the system of
    constraints (resp., generators) actually defines
    a topologically closed subset of the vector space, i.e.,
    even if all the strict inequalities (resp., closure points)
    in the system happen to be redundant with respect to the
    system obtained by removing all the strict inequality constraints
    (resp., all the closure points).
    In contrast, when building a closed polyhedron starting from
    an object of the class NNC_Polyhedron,
    the precise topological closure test will be performed.
*/

class Parma_Polyhedra_Library::C_Polyhedron : public Polyhedron {
public:
  //! Builds either the universe or the empty C polyhedron.
  /*!
    \param num_dimensions
    The number of dimensions of the vector space enclosing the C polyhedron;

    \param kind
    Specifies whether a universe or an empty C polyhedron should be built.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space dimension.

    Both parameters are optional:
    by default, a 0-dimension space universe C polyhedron is built.
  */
  explicit C_Polyhedron(dimension_type num_dimensions = 0,
                        Degenerate_Element kind = UNIVERSE);

  //! Builds a C polyhedron from a system of constraints.
  /*!
    The polyhedron inherits the space dimension of the constraint system.

    \param cs
    The system of constraints defining the polyhedron.

    \exception std::invalid_argument
    Thrown if the system of constraints contains strict inequalities.
  */
  explicit C_Polyhedron(const Constraint_System& cs);

  //! Builds a C polyhedron recycling a system of constraints.
  /*!
    The polyhedron inherits the space dimension of the constraint system.

    \param cs
    The system of constraints defining the polyhedron.  It is not
    declared <CODE>const</CODE> because its data-structures may be
    recycled to build the polyhedron.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.

    \exception std::invalid_argument
    Thrown if the system of constraints contains strict inequalities.
  */
  C_Polyhedron(Constraint_System& cs, Recycle_Input dummy);

  //! Builds a C polyhedron from a system of generators.
  /*!
    The polyhedron inherits the space dimension of the generator system.

    \param gs
    The system of generators defining the polyhedron.

    \exception std::invalid_argument
    Thrown if the system of generators is not empty but has no points,
    or if it contains closure points.
  */
  explicit C_Polyhedron(const Generator_System& gs);

  //! Builds a C polyhedron recycling a system of generators.
  /*!
    The polyhedron inherits the space dimension of the generator system.

    \param gs
    The system of generators defining the polyhedron.  It is not
    declared <CODE>const</CODE> because its data-structures may be
    recycled to build the polyhedron.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.

    \exception std::invalid_argument
    Thrown if the system of generators is not empty but has no points,
    or if it contains closure points.
  */
  C_Polyhedron(Generator_System& gs, Recycle_Input dummy);

  //! Builds a C polyhedron from a system of congruences.
  /*!
    The polyhedron inherits the space dimension of the congruence system.

    \param cgs
    The system of congruences defining the polyhedron.
  */
  explicit C_Polyhedron(const Congruence_System& cgs);

  //! Builds a C polyhedron recycling a system of congruences.
  /*!
    The polyhedron inherits the space dimension of the congruence
    system.

    \param cgs
    The system of congruences defining the polyhedron.  It is not
    declared <CODE>const</CODE> because its data-structures may be
    recycled to build the polyhedron.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.
  */
  C_Polyhedron(Congruence_System& cgs, Recycle_Input dummy);

  /*! \brief
    Builds a C polyhedron representing the topological closure
    of the NNC polyhedron \p y.

    \param y
    The NNC polyhedron to be used;

    \param complexity
    This argument is ignored.
  */
  explicit C_Polyhedron(const NNC_Polyhedron& y,
                        Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a C polyhedron out of a box.
  /*!
    The polyhedron inherits the space dimension of the box
    and is the most precise that includes the box.
    The algorithm used has polynomial complexity.

    \param box
    The box representing the polyhedron to be approximated;

    \param complexity
    This argument is ignored.

    \exception std::length_error
    Thrown if the space dimension of \p box exceeds the maximum allowed
    space dimension.
  */
  template <typename Interval>
  explicit C_Polyhedron(const Box<Interval>& box,
                        Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a C polyhedron out of a BD shape.
  /*!
    The polyhedron inherits the space dimension of the BDS and is
    the most precise that includes the BDS.

    \param bd
    The BDS used to build the polyhedron.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.
  */
  template <typename U>
  explicit C_Polyhedron(const BD_Shape<U>& bd,
                        Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a C polyhedron out of an octagonal shape.
  /*!
    The polyhedron inherits the space dimension of the octagonal shape
    and is the most precise that includes the octagonal shape.

    \param os
    The octagonal shape used to build the polyhedron.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.
  */
  template <typename U>
  explicit C_Polyhedron(const Octagonal_Shape<U>& os,
                        Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a C polyhedron out of a grid.
  /*!
    The polyhedron inherits the space dimension of the grid
    and is the most precise that includes the grid.

    \param grid
    The grid used to build the polyhedron.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.
  */
  explicit C_Polyhedron(const Grid& grid,
                        Complexity_Class complexity = ANY_COMPLEXITY);

  //! Ordinary copy constructor.
  /*!
    The complexity argument is ignored.
  */
  C_Polyhedron(const C_Polyhedron& y,
               Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    The assignment operator.
    (\p *this and \p y can be dimension-incompatible.)
  */
  C_Polyhedron& operator=(const C_Polyhedron& y);

  //! Assigns to \p *this the topological closure of the NNC polyhedron \p y.
  C_Polyhedron& operator=(const NNC_Polyhedron& y);

  //! Destructor.
  ~C_Polyhedron();

  /*! \brief
    If the poly-hull of \p *this and \p y is exact it is assigned
    to \p *this and <CODE>true</CODE> is returned,
    otherwise <CODE>false</CODE> is returned.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool poly_hull_assign_if_exact(const C_Polyhedron& y);

  //! Same as poly_hull_assign_if_exact(y).
  bool upper_bound_assign_if_exact(const C_Polyhedron& y);

  /*! \brief
    Assigns to \p *this the smallest C polyhedron containing the
    result of computing the
    \ref Positive_Time_Elapse_Operator "positive time-elapse"
    between \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void positive_time_elapse_assign(const Polyhedron& y);
};

/* Automatically generated from PPL source file ../src/C_Polyhedron_inlines.hh line 1. */
/* C_Polyhedron class implementation: inline functions.
*/


#include <algorithm>
#include <stdexcept>

namespace Parma_Polyhedra_Library {

inline
C_Polyhedron::~C_Polyhedron() {
}

inline
C_Polyhedron::C_Polyhedron(dimension_type num_dimensions,
                           Degenerate_Element kind)
  : Polyhedron(NECESSARILY_CLOSED,
               check_space_dimension_overflow(num_dimensions,
                                              NECESSARILY_CLOSED,
                                              "C_Polyhedron(n, k)",
                                              "n exceeds the maximum "
                                              "allowed space dimension"),
               kind) {
}

inline
C_Polyhedron::C_Polyhedron(const Constraint_System& cs)
  : Polyhedron(NECESSARILY_CLOSED,
               check_obj_space_dimension_overflow(cs, NECESSARILY_CLOSED,
                                                  "C_Polyhedron(cs)",
                                                  "the space dimension of cs "
                                                  "exceeds the maximum allowed "
                                                  "space dimension")) {
}

inline
C_Polyhedron::C_Polyhedron(Constraint_System& cs, Recycle_Input)
  : Polyhedron(NECESSARILY_CLOSED,
               check_obj_space_dimension_overflow(cs, NECESSARILY_CLOSED,
                                                  "C_Polyhedron(cs, recycle)",
                                                  "the space dimension of cs "
                                                  "exceeds the maximum allowed "
                                                  "space dimension"),
               Recycle_Input()) {
}

inline
C_Polyhedron::C_Polyhedron(const Generator_System& gs)
  : Polyhedron(NECESSARILY_CLOSED,
               check_obj_space_dimension_overflow(gs, NECESSARILY_CLOSED,
                                                  "C_Polyhedron(gs)",
                                                  "the space dimension of gs "
                                                  "exceeds the maximum allowed "
                                                  "space dimension")) {
}

inline
C_Polyhedron::C_Polyhedron(Generator_System& gs, Recycle_Input)
  : Polyhedron(NECESSARILY_CLOSED,
               check_obj_space_dimension_overflow(gs, NECESSARILY_CLOSED,
                                                  "C_Polyhedron(gs, recycle)",
                                                  "the space dimension of gs "
                                                  "exceeds the maximum allowed "
                                                  "space dimension"),
               Recycle_Input()) {
}

template <typename Interval>
inline
C_Polyhedron::C_Polyhedron(const Box<Interval>& box, Complexity_Class)
  : Polyhedron(NECESSARILY_CLOSED,
               check_obj_space_dimension_overflow(box, NECESSARILY_CLOSED,
                                                  "C_Polyhedron(box)",
                                                  "the space dimension of box "
                                                  "exceeds the maximum allowed "
                                                  "space dimension")) {
}

template <typename U>
inline
C_Polyhedron::C_Polyhedron(const BD_Shape<U>& bd, Complexity_Class)
  : Polyhedron(NECESSARILY_CLOSED,
               check_space_dimension_overflow(bd.space_dimension(),
                                              NECESSARILY_CLOSED,
                                              "C_Polyhedron(bd)",
                                              "the space dimension of bd "
                                              "exceeds the maximum allowed "
                                              "space dimension"),
               UNIVERSE) {
  add_constraints(bd.constraints());
}

template <typename U>
inline
C_Polyhedron::C_Polyhedron(const Octagonal_Shape<U>& os, Complexity_Class)
  : Polyhedron(NECESSARILY_CLOSED,
               check_space_dimension_overflow(os.space_dimension(),
                                              NECESSARILY_CLOSED,
                                              "C_Polyhedron(os)",
                                              "the space dimension of os "
                                              "exceeds the maximum allowed "
                                              "space dimension"),
               UNIVERSE) {
  add_constraints(os.constraints());
}

inline
C_Polyhedron::C_Polyhedron(const C_Polyhedron& y, Complexity_Class)
  : Polyhedron(y) {
}

inline C_Polyhedron&
C_Polyhedron::operator=(const C_Polyhedron& y) {
  Polyhedron::operator=(y);
  return *this;
}

inline C_Polyhedron&
C_Polyhedron::operator=(const NNC_Polyhedron& y) {
  C_Polyhedron c_y(y);
  m_swap(c_y);
  return *this;
}

inline bool
C_Polyhedron::upper_bound_assign_if_exact(const C_Polyhedron& y) {
  return poly_hull_assign_if_exact(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/C_Polyhedron_defs.hh line 290. */

/* Automatically generated from PPL source file ../src/Octagonal_Shape_defs.hh line 1. */
/* Octagonal_Shape class declaration.
*/


/* Automatically generated from PPL source file ../src/OR_Matrix_defs.hh line 1. */
/* OR_Matrix class declaration.
*/


/* Automatically generated from PPL source file ../src/OR_Matrix_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename T>
class OR_Matrix;

}

/* Automatically generated from PPL source file ../src/OR_Matrix_defs.hh line 31. */
#include <cstddef>
#include <iosfwd>

#ifndef PPL_OR_MATRIX_EXTRA_DEBUG
#ifdef PPL_ABI_BREAKING_EXTRA_DEBUG
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  When PPL_OR_MATRIX_EXTRA_DEBUG evaluates to <CODE>true</CODE>, each
  instance of the class OR_Matrix::Pseudo_Row carries its own size;
  this enables extra consistency checks to be performed.
  \ingroup PPL_CXX_interface
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
#define PPL_OR_MATRIX_EXTRA_DEBUG 1
#else // !defined(PPL_ABI_BREAKING_EXTRA_DEBUG)
#define PPL_OR_MATRIX_EXTRA_DEBUG 0
#endif // !defined(PPL_ABI_BREAKING_EXTRA_DEBUG)
#endif // !defined(PPL_OR_MATRIX_EXTRA_DEBUG)

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are identical.
/*! \relates OR_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
bool operator==(const OR_Matrix<T>& x, const OR_Matrix<T>& y);

namespace IO_Operators {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Output operator.
/*! \relates Parma_Polyhedra_Library::OR_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
std::ostream&
operator<<(std::ostream& s, const OR_Matrix<T>& m);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library


#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A matrix representing octagonal constraints.
/*!
  An OR_Matrix object is a DB_Row object that allows
  the representation of a \em pseudo-triangular matrix,
  like the following:

<PRE>
         _ _
   0    |_|_|
   1    |_|_|_ _
   2    |_|_|_|_|
   3    |_|_|_|_|_ _
   4    |_|_|_|_|_|_|
   5    |_|_|_|_|_|_|
         . . .
         _ _ _ _ _ _       _
 2n-2   |_|_|_|_|_|_| ... |_|
 2n-1   |_|_|_|_|_|_| ... |_|
         0 1 2 3 4 5  ... 2n-1

</PRE>

  It is characterized by parameter n that defines the structure,
  and such that there are 2*n rows (and 2*n columns).
  It provides row_iterators for the access to the rows
  and element_iterators for the access to the elements.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)

template <typename T>
class Parma_Polyhedra_Library::OR_Matrix {
private:
  /*! \brief
    An object that behaves like a matrix's row with respect to
    the subscript operators.
  */
  template <typename U>
  class Pseudo_Row {
  public:
    /*! \brief
      Copy constructor allowing the construction of a const pseudo-row
      from a non-const pseudo-row.
      Ordinary copy constructor.
    */
    template <typename V>
    Pseudo_Row(const Pseudo_Row<V>& y);

    //! Destructor.
    ~Pseudo_Row();

    //! Subscript operator.
    U& operator[](dimension_type k) const;

    //! Default constructor: creates an invalid object that has to be assigned.
    Pseudo_Row();

    //! Assignment operator.
    Pseudo_Row& operator=(const Pseudo_Row& y);

#if !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 0)
  private:
#else
  // Work around a bug of GCC 4.0.x (and, likely, previous versions).
  public:
#endif

#if PPL_OR_MATRIX_EXTRA_DEBUG

    //! Private constructor for a Pseudo_Row with size \p s beginning at \p y.
    Pseudo_Row(U& y, dimension_type s);

#else // !PPL_OR_MATRIX_EXTRA_DEBUG

    //! Private constructor for a Pseudo_Row beginning at \p y.
    explicit Pseudo_Row(U& y);

#endif // !PPL_OR_MATRIX_EXTRA_DEBUG

    //! Holds a reference to the beginning of this row.
    U* first;

#if !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 0)
#else
  // Work around a bug of GCC 4.0.x (and, likely, previous versions).
  private:
#endif

#if PPL_OR_MATRIX_EXTRA_DEBUG

    //! The size of the row.
    dimension_type size_;

    //! Returns the size of the row.
    dimension_type size() const;

#endif // PPL_OR_MATRIX_EXTRA_DEBUG

    // FIXME: the EDG-based compilers (such as Comeau and Intel)
    // are here in wild disagreement with GCC: what is a legal friend
    // declaration for one, is illegal for the others.
#ifdef __EDG__
    template <typename V> template<typename W>
    friend class OR_Matrix<V>::Pseudo_Row;
    template <typename V> template<typename W>
    friend class OR_Matrix<V>::any_row_iterator;
#else
    template <typename V> friend class Pseudo_Row;
    template <typename V> friend class any_row_iterator;
#endif

    friend class OR_Matrix;
  }; // class Pseudo_Row

public:
  //! A (non const) reference to a matrix's row.
  typedef Pseudo_Row<T> row_reference_type;

  //! A const reference to a matrix's row.
  typedef Pseudo_Row<const T> const_row_reference_type;

private:
  /*! \brief
    A template class to derive both OR_Matrix::iterator
    and OR_Matrix::const_iterator.
  */
  template <typename U>
  class any_row_iterator {
  public:
    typedef std::random_access_iterator_tag iterator_category;
    typedef Pseudo_Row<U> value_type;
    typedef long difference_type;
    typedef const Pseudo_Row<U>* pointer;
    typedef const Pseudo_Row<U>& reference;

    //! Constructor to build past-the-end objects.
    any_row_iterator(dimension_type n_rows);

    /*! \brief
      Builds an iterator pointing at the beginning of an OR_Matrix whose
      first element is \p base;
    */
    explicit any_row_iterator(U& base);

    /*! \brief
      Copy constructor allowing the construction of a const_iterator
      from a non-const iterator.
    */
    template <typename V>
    any_row_iterator(const any_row_iterator<V>& y);

    /*! \brief
      Assignment operator allowing the assignment of a non-const iterator
      to a const_iterator.
    */
    template <typename V>
    any_row_iterator& operator=(const any_row_iterator<V>& y);

    //! Dereference operator.
    reference operator*() const;

    //! Indirect member selector.
    pointer operator->() const;

    //! Prefix increment operator.
    any_row_iterator& operator++();

    //! Postfix increment operator.
    any_row_iterator operator++(int);

    //! Prefix decrement operator.
    any_row_iterator& operator--();

    //! Postfix decrement operator.
    any_row_iterator operator--(int);

    //! Subscript operator.
    reference operator[](difference_type m) const;

    //! Assignment-increment operator.
    any_row_iterator& operator+=(difference_type m);

    //! Assignment-increment operator for \p m of unsigned type.
    template <typename Unsigned>
    typename Enable_If<(static_cast<Unsigned>(-1) > 0), any_row_iterator&>::type
    operator+=(Unsigned m);

    //! Assignment-decrement operator.
    any_row_iterator& operator-=(difference_type m);

    //! Returns the difference between \p *this and \p y.
    difference_type operator-(const any_row_iterator& y) const;

    //! Returns the sum of \p *this and \p m.
    any_row_iterator operator+(difference_type m) const;

    //! Returns the sum of \p *this and \p m, for \p m of unsigned type.
    template <typename Unsigned>
    typename Enable_If<(static_cast<Unsigned>(-1) > 0), any_row_iterator>::type
    operator+(Unsigned m) const;

    //! Returns the difference of \p *this and \p m.
    any_row_iterator operator-(difference_type m) const;

    //! Returns <CODE>true</CODE> if and only if \p *this is equal to \p y.
    bool operator==(const any_row_iterator& y) const;

    /*! \brief
      Returns <CODE>true</CODE> if and only if \p *this
      is different from \p y.
    */
    bool operator!=(const any_row_iterator& y) const;

    //! Returns <CODE>true</CODE> if and only if \p *this is less than \p y.
    bool operator<(const any_row_iterator& y) const;

    /*! \brief
      Returns <CODE>true</CODE> if and only if \p *this is less than
      or equal to \p y.
    */
    bool operator<=(const any_row_iterator& y) const;

    //! Returns <CODE>true</CODE> if and only if \p *this is greater than \p y.
    bool operator>(const any_row_iterator& y) const;

    /*! \brief
      Returns <CODE>true</CODE> if and only if \p *this is greater than
      or equal to \p y.
    */
    bool operator>=(const any_row_iterator& y) const;

    dimension_type row_size() const;

    dimension_type index() const;

  private:
    //! Represents the beginning of a row.
    Pseudo_Row<U> value;

    //! External index.
    dimension_type e;

    //! Internal index: <CODE>i = (e+1)*(e+1)/2</CODE>.
    dimension_type i;

    // FIXME: the EDG-based compilers (such as Comeau and Intel)
    // are here in wild disagreement with GCC: what is a legal friend
    // declaration for one, is illegal for the others.
#ifdef __EDG__
    template <typename V> template<typename W>
    friend class OR_Matrix<V>::any_row_iterator;
#else
    template <typename V> friend class any_row_iterator;
#endif
  }; // class any_row_iterator

public:
  //! A (non const) row iterator.
  typedef any_row_iterator<T> row_iterator;

  //! A const row iterator.
  typedef any_row_iterator<const T> const_row_iterator;

  //! A (non const) element iterator.
  typedef typename DB_Row<T>::iterator element_iterator;

  //! A const element iterator.
  typedef typename DB_Row<T>::const_iterator const_element_iterator;

public:
  //! Returns the maximum number of rows of a OR_Matrix.
  static dimension_type max_num_rows();

  //! Builds a matrix with specified dimensions.
  /*!
    \param num_dimensions
    The space dimension of the matrix that will be created.

    This constructor creates a matrix with \p 2*num_dimensions rows.
    Each element is initialized to plus infinity.
  */
  OR_Matrix(dimension_type num_dimensions);

  //! Copy constructor.
  OR_Matrix(const OR_Matrix& y);

  //! Constructs a conservative approximation of \p y.
  template <typename U>
  explicit OR_Matrix(const OR_Matrix<U>& y);

  //! Destructor.
  ~OR_Matrix();

  //! Assignment operator.
  OR_Matrix& operator=(const OR_Matrix& y);

private:
  template <typename U> friend class OR_Matrix;

  //! Contains the rows of the matrix.
  /*!
    A DB_Row which contains the rows of the OR_Matrix
    inserting each successive row to the end of the vec.
    To contain all the elements of OR_Matrix the size of the DB_Row
    is 2*n*(n+1), where the n is the characteristic parameter of
    OR_Matrix.
  */
  DB_Row<T> vec;

  //! Contains the dimension of the space of the matrix.
  dimension_type space_dim;

  //! Contains the capacity of \p vec.
  dimension_type vec_capacity;

  //! Private and not implemented: default construction is not allowed.
  OR_Matrix();

  /*! \brief
    Returns the index into <CODE>vec</CODE> of the first element
    of the row of index \p k.
  */
  static dimension_type row_first_element_index(dimension_type k);

public:
  //! Returns the size of the row of index \p k.
  static dimension_type row_size(dimension_type k);

  //! Swaps \p *this with \p y.
  void m_swap(OR_Matrix& y);

  //! Makes the matrix grow by adding more space dimensions.
  /*!
    \param new_dim
    The new dimension of the resized matrix.

    Adds new rows of right dimension to the end if
    there is enough capacity; otherwise, creates a new matrix,
    with the specified dimension, copying the old elements
    in the upper part of the new matrix, which is
    then assigned to \p *this.
    Each new element is initialized to plus infinity.
  */
  void grow(dimension_type new_dim);

  //! Makes the matrix shrink by removing the last space dimensions.
  /*!
    \param new_dim
    The new dimension of the resized matrix.

    Erases from matrix to the end the rows with index
    greater than 2*new_dim-1.
  */
  void shrink(dimension_type new_dim);

  //! Resizes the matrix without worrying about the old contents.
  /*!
    \param new_dim
    The new dimension of the resized matrix.

    If the new dimension is greater than the old one, it adds new rows
    of right dimension to the end if there is enough capacity; otherwise,
    it creates a new matrix, with the specified dimension, which is
    then assigned to \p *this.
    If the new dimension is less than the old one, it erase from the matrix
    the rows having index greater than 2*new_dim-1
  */
  void resize_no_copy(dimension_type new_dim);

  //! Returns the space-dimension of the matrix.
  dimension_type space_dimension() const;

  //! Returns the number of rows in the matrix.
  dimension_type num_rows() const;

  //! \name Subscript operators.
  //@{
  //! Returns a reference to the \p k-th row of the matrix.
  row_reference_type operator[](dimension_type k);

  //! Returns a constant reference to the \p k-th row of the matrix.
  const_row_reference_type operator[](dimension_type k) const;
  //@}


  /*! \brief
    Returns an iterator pointing to the first row,
    if \p *this is not empty;
    otherwise, returns the past-the-end const_iterator.
  */
  row_iterator row_begin();

  //! Returns the past-the-end const_iterator.
  row_iterator row_end();

  /*! \brief
    Returns a const row iterator pointing to the first row,
    if \p *this is not empty;
    otherwise, returns the past-the-end const_iterator.
  */
  const_row_iterator row_begin() const;

  //! Returns the past-the-end const row iterator.
  const_row_iterator row_end() const;

  /*! \brief
    Returns an iterator pointing to the first element,
    if \p *this is not empty;
    otherwise, returns the past-the-end const_iterator.
  */
  element_iterator element_begin();

  //! Returns the past-the-end const_iterator.
  element_iterator element_end();

  /*! \brief
    Returns a const element iterator pointing to the first element,
    if \p *this is not empty;
    otherwise, returns the past-the-end const_iterator.
  */
  const_element_iterator element_begin() const;

  //! Returns the past-the-end const element iterator.
  const_element_iterator element_end() const;

  //! Clears the matrix deallocating all its rows.
  void clear();

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  friend bool operator==<T>(const OR_Matrix<T>& x, const OR_Matrix<T>& y);

  //! Checks if all the invariants are satisfied.
  bool OK() const;
};

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Swaps \p x with \p y.
/*! \relates OR_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
void swap(OR_Matrix<T>& x, OR_Matrix<T>& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns <CODE>true</CODE> if and only if \p x and \p y are different.
/*! \relates OR_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
bool operator!=(const OR_Matrix<T>& x, const OR_Matrix<T>& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates OR_Matrix
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into to \p r
  and returns <CODE>true</CODE>;  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const OR_Matrix<T>& x,
                                 const OR_Matrix<T>& y,
                                 Rounding_Dir dir,
                                 Temp& tmp0,
                                 Temp& tmp1,
                                 Temp& tmp2);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Computes the euclidean distance between \p x and \p y.
/*! \relates OR_Matrix
  If the Euclidean distance between \p x and \p y is defined,
  stores an approximation of it into to \p r
  and returns <CODE>true</CODE>;  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
bool euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const OR_Matrix<T>& x,
                               const OR_Matrix<T>& y,
                               Rounding_Dir dir,
                               Temp& tmp0,
                               Temp& tmp1,
                               Temp& tmp2);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates OR_Matrix
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into to \p r
  and returns <CODE>true</CODE>;  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
bool l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const OR_Matrix<T>& x,
                                 const OR_Matrix<T>& y,
                                 Rounding_Dir dir,
                                 Temp& tmp0,
                                 Temp& tmp1,
                                 Temp& tmp2);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/OR_Matrix_inlines.hh line 1. */
/* OR_Matrix class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/OR_Matrix_inlines.hh line 33. */
#include <algorithm>

namespace Parma_Polyhedra_Library {

template <typename T>
inline dimension_type
OR_Matrix<T>::row_first_element_index(const dimension_type k) {
  return ((k + 1)*(k + 1))/2;
}

template <typename T>
inline dimension_type
OR_Matrix<T>::row_size(const dimension_type k) {
  return k + 2 - k % 2;
}

#if PPL_OR_MATRIX_EXTRA_DEBUG

template <typename T>
template <typename U>
inline dimension_type
OR_Matrix<T>::Pseudo_Row<U>::size() const {
  return size_;
}

#endif // PPL_OR_MATRIX_EXTRA_DEBUG

template <typename T>
template <typename U>
inline
OR_Matrix<T>::Pseudo_Row<U>::Pseudo_Row()
  : first(0)
#if PPL_OR_MATRIX_EXTRA_DEBUG
  , size_(0)
#endif
{
}

template <typename T>
template <typename U>
inline
OR_Matrix<T>::Pseudo_Row<U>::Pseudo_Row(U& y
#if PPL_OR_MATRIX_EXTRA_DEBUG
                , dimension_type s
#endif
                )
  : first(&y)
#if PPL_OR_MATRIX_EXTRA_DEBUG
  , size_(s)
#endif
{
}

template <typename T>
template <typename U>
template <typename V>
inline
OR_Matrix<T>::Pseudo_Row<U>::Pseudo_Row(const Pseudo_Row<V>& y)
  : first(y.first)
#if PPL_OR_MATRIX_EXTRA_DEBUG
    , size_(y.size_)
#endif
{
}

template <typename T>
template <typename U>
inline OR_Matrix<T>::Pseudo_Row<U>&
OR_Matrix<T>::Pseudo_Row<U>::operator=(const Pseudo_Row& y) {
  first = y.first;
#if PPL_OR_MATRIX_EXTRA_DEBUG
  size_ = y.size_;
#endif
  return *this;
}

template <typename T>
template <typename U>
inline
OR_Matrix<T>::Pseudo_Row<U>::~Pseudo_Row() {
}

template <typename T>
template <typename U>
inline U&
OR_Matrix<T>::Pseudo_Row<U>::operator[](const dimension_type k) const {
#if PPL_OR_MATRIX_EXTRA_DEBUG
  PPL_ASSERT(k < size_);
#endif
  return *(first + k);
}

template <typename T>
template <typename U>
inline
OR_Matrix<T>::any_row_iterator<U>
::any_row_iterator(const dimension_type n_rows)
  : value(),
    e(n_rows)
    // Field `i' is intentionally not initialized here.
{
#if PPL_OR_MATRIX_EXTRA_DEBUG
  // Turn `value' into a valid object.
  value.size_ = OR_Matrix::row_size(e);
#endif
}

template <typename T>
template <typename U>
inline
OR_Matrix<T>::any_row_iterator<U>::any_row_iterator(U& base)
  :  value(base
#if PPL_OR_MATRIX_EXTRA_DEBUG
           , OR_Matrix<T>::row_size(0)
#endif
           ),
     e(0),
     i(0) {
}

template <typename T>
template <typename U>
template <typename V>
inline
OR_Matrix<T>::any_row_iterator<U>
::any_row_iterator(const any_row_iterator<V>& y)
  : value(y.value),
    e(y.e),
    i(y.i) {
}

template <typename T>
template <typename U>
template <typename V>
inline typename OR_Matrix<T>::template any_row_iterator<U>&
OR_Matrix<T>::any_row_iterator<U>::operator=(const any_row_iterator<V>& y) {
  value = y.value;
  e = y.e;
  i = y.i;
  return *this;
}

template <typename T>
template <typename U>
inline typename OR_Matrix<T>::template any_row_iterator<U>::reference
OR_Matrix<T>::any_row_iterator<U>::operator*() const {
  return value;
}

template <typename T>
template <typename U>
inline typename OR_Matrix<T>::template any_row_iterator<U>::pointer
OR_Matrix<T>::any_row_iterator<U>::operator->() const {
  return &value;
}

template <typename T>
template <typename U>
inline typename OR_Matrix<T>::template any_row_iterator<U>&
OR_Matrix<T>::any_row_iterator<U>::operator++() {
  ++e;
  dimension_type increment = e;
  if (e % 2 != 0) {
    ++increment;
  }
#if PPL_OR_MATRIX_EXTRA_DEBUG
  else {
    value.size_ += 2;
  }
#endif
  i += increment;
  value.first += increment;
  return *this;
}

template <typename T>
template <typename U>
inline typename OR_Matrix<T>::template any_row_iterator<U>
OR_Matrix<T>::any_row_iterator<U>::operator++(int) {
  any_row_iterator old = *this;
  ++(*this);
  return old;
}

template <typename T>
template <typename U>
inline typename OR_Matrix<T>::template any_row_iterator<U>&
OR_Matrix<T>::any_row_iterator<U>::operator--() {
  dimension_type decrement = e + 1;
  --e;
  if (e % 2 != 0) {
    ++decrement;
#if PPL_OR_MATRIX_EXTRA_DEBUG
    value.size_ -= 2;
#endif
  }
  i -= decrement;
  value.first -= decrement;
  return *this;
}

template <typename T>
template <typename U>
inline typename OR_Matrix<T>::template any_row_iterator<U>
OR_Matrix<T>::any_row_iterator<U>::operator--(int) {
  any_row_iterator old = *this;
  --(*this);
  return old;
}

template <typename T>
template <typename U>
inline typename OR_Matrix<T>::template any_row_iterator<U>&
OR_Matrix<T>::any_row_iterator<U>::operator+=(const difference_type m) {
  difference_type e_dt = static_cast<difference_type>(e);
  difference_type i_dt = static_cast<difference_type>(i);
  difference_type increment = m + (m * m) / 2 + m * e_dt;
  if (e_dt % 2 == 0 && m % 2 != 0) {
    ++increment;
  }
  e_dt += m;
  i_dt += increment;
  e = static_cast<dimension_type>(e_dt);
  i = static_cast<dimension_type>(i_dt);
  value.first += increment;
#if PPL_OR_MATRIX_EXTRA_DEBUG
  difference_type value_size_dt = static_cast<difference_type>(value.size_);
  value_size_dt += (m - m % 2);
  value.size_ = static_cast<dimension_type>(value_size_dt);
#endif
  return *this;
}

template <typename T>
template <typename U>
template <typename Unsigned>
inline typename
Enable_If<(static_cast<Unsigned>(-1) > 0),
            typename OR_Matrix<T>::template any_row_iterator<U>& >::type
OR_Matrix<T>::any_row_iterator<U>::operator+=(Unsigned m) {
  dimension_type n = m;
  dimension_type increment = n + (n*n)/2 + n*e;
  if (e % 2 == 0 && n % 2 != 0) {
    ++increment;
  }
  e += n;
  i += increment;
  value.first += increment;
#if PPL_OR_MATRIX_EXTRA_DEBUG
  value.size_ = value.size_ + n - n % 2;
#endif
  return *this;
}

template <typename T>
template <typename U>
inline typename OR_Matrix<T>::template any_row_iterator<U>&
OR_Matrix<T>::any_row_iterator<U>::operator-=(difference_type m) {
  return *this += -m;
}

template <typename T>
template <typename U>
inline typename OR_Matrix<T>::template any_row_iterator<U>::difference_type
OR_Matrix<T>::any_row_iterator<U>::operator-(const any_row_iterator& y) const {
  return e - y.e;
}

template <typename T>
template <typename U>
inline typename OR_Matrix<T>::template any_row_iterator<U>
OR_Matrix<T>::any_row_iterator<U>::operator+(difference_type m) const {
  any_row_iterator r = *this;
  r += m;
  return r;
}

template <typename T>
template <typename U>
template <typename Unsigned>
inline typename
Enable_If<(static_cast<Unsigned>(-1) > 0),
            typename OR_Matrix<T>::template any_row_iterator<U> >::type
OR_Matrix<T>::any_row_iterator<U>::operator+(Unsigned m) const {
  any_row_iterator r = *this;
  r += m;
  return r;
}

template <typename T>
template <typename U>
inline typename OR_Matrix<T>::template any_row_iterator<U>
OR_Matrix<T>::any_row_iterator<U>::operator-(const difference_type m) const {
  any_row_iterator r = *this;
  r -= m;
  return r;
}

template <typename T>
template <typename U>
inline bool
OR_Matrix<T>::any_row_iterator<U>
::operator==(const any_row_iterator& y) const {
  return e == y.e;
}

template <typename T>
template <typename U>
inline bool
OR_Matrix<T>::any_row_iterator<U>
::operator!=(const any_row_iterator& y) const {
  return e != y.e;
}

template <typename T>
template <typename U>
inline bool
OR_Matrix<T>::any_row_iterator<U>::operator<(const any_row_iterator& y) const {
  return e < y.e;
}

template <typename T>
template <typename U>
inline bool
OR_Matrix<T>::any_row_iterator<U>
::operator<=(const any_row_iterator& y) const {
  return e <= y.e;
}

template <typename T>
template <typename U>
inline bool
OR_Matrix<T>::any_row_iterator<U>::operator>(const any_row_iterator& y) const {
  return e > y.e;
}

template <typename T>
template <typename U>
inline bool
OR_Matrix<T>::any_row_iterator<U>
::operator>=(const any_row_iterator& y) const {
  return e >= y.e;
}

template <typename T>
template <typename U>
inline dimension_type
OR_Matrix<T>::any_row_iterator<U>::row_size() const {
  return OR_Matrix::row_size(e);
}

template <typename T>
template <typename U>
inline dimension_type
OR_Matrix<T>::any_row_iterator<U>::index() const {
  return e;
}

template <typename T>
inline typename OR_Matrix<T>::row_iterator
OR_Matrix<T>::row_begin() {
  return num_rows() == 0 ? row_iterator(0) : row_iterator(vec[0]);
}

template <typename T>
inline typename OR_Matrix<T>::row_iterator
OR_Matrix<T>::row_end() {
  return row_iterator(num_rows());
}

template <typename T>
inline typename OR_Matrix<T>::const_row_iterator
OR_Matrix<T>::row_begin() const {
  return num_rows() == 0 ? const_row_iterator(0) : const_row_iterator(vec[0]);
}

template <typename T>
inline typename OR_Matrix<T>::const_row_iterator
OR_Matrix<T>::row_end() const {
  return const_row_iterator(num_rows());
}

template <typename T>
inline typename OR_Matrix<T>::element_iterator
OR_Matrix<T>::element_begin() {
  return vec.begin();
}

template <typename T>
inline typename OR_Matrix<T>::element_iterator
OR_Matrix<T>::element_end() {
  return vec.end();
}

template <typename T>
inline typename OR_Matrix<T>::const_element_iterator
OR_Matrix<T>::element_begin() const {
  return vec.begin();
}

template <typename T>
inline typename OR_Matrix<T>::const_element_iterator
OR_Matrix<T>::element_end() const {
  return vec.end();
}

template <typename T>
inline void
OR_Matrix<T>::m_swap(OR_Matrix& y) {
  using std::swap;
  swap(vec, y.vec);
  swap(space_dim, y.space_dim);
  swap(vec_capacity, y.vec_capacity);
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns the integer square root of \p x.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
inline dimension_type
isqrt(dimension_type x) {
  dimension_type r = 0;
  const dimension_type FIRST_BIT_MASK = 0x40000000U;
  for (dimension_type t = FIRST_BIT_MASK; t != 0; t >>= 2) {
    const dimension_type s = r + t;
    if (s <= x) {
      x -= s;
      r = s + t;
    }
    r >>= 1;
  }
  return r;
}

template <typename T>
inline dimension_type
OR_Matrix<T>::max_num_rows() {
  // Compute the maximum number of rows that are contained in a DB_Row
  // that allocates a pseudo-triangular matrix.
  const dimension_type k = isqrt(2*DB_Row<T>::max_size() + 1);
  return (k - 1) - (k - 1) % 2;
}

template <typename T>
inline memory_size_type
OR_Matrix<T>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename T>
inline
OR_Matrix<T>::OR_Matrix(const dimension_type num_dimensions)
  : vec(2*num_dimensions*(num_dimensions + 1)),
    space_dim(num_dimensions),
    vec_capacity(vec.size()) {
}

template <typename T>
inline
OR_Matrix<T>::~OR_Matrix() {
}

template <typename T>
inline typename OR_Matrix<T>::row_reference_type
OR_Matrix<T>::operator[](dimension_type k) {
  return row_reference_type(vec[row_first_element_index(k)]
#if PPL_OR_MATRIX_EXTRA_DEBUG
                            , row_size(k)
#endif
                            );
}

template <typename T>
inline typename OR_Matrix<T>::const_row_reference_type
OR_Matrix<T>::operator[](dimension_type k) const {
  return const_row_reference_type(vec[row_first_element_index(k)]
#if PPL_OR_MATRIX_EXTRA_DEBUG
                                  , row_size(k)
#endif
                                  );
}

template <typename T>
inline dimension_type
OR_Matrix<T>::space_dimension() const {
  return space_dim;
}

template <typename T>
inline dimension_type
OR_Matrix<T>::num_rows() const {
  return 2*space_dimension();
}

template <typename T>
inline void
OR_Matrix<T>::clear() {
  OR_Matrix<T>(0).m_swap(*this);
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates OR_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
inline bool
operator==(const OR_Matrix<T>& x, const OR_Matrix<T>& y) {
  return x.space_dim == y.space_dim && x.vec == y.vec;
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates OR_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
inline bool
operator!=(const OR_Matrix<T>& x, const OR_Matrix<T>& y) {
  return !(x == y);
}

template <typename T>
inline
OR_Matrix<T>::OR_Matrix(const OR_Matrix& y)
  : vec(y.vec),
    space_dim(y.space_dim),
    vec_capacity(compute_capacity(y.vec.size(),
                                  DB_Row<T>::max_size())) {
}

template <typename T>
template <typename U>
inline
OR_Matrix<T>::OR_Matrix(const OR_Matrix<U>& y)
  : vec(),
    space_dim(y.space_dim),
    vec_capacity(compute_capacity(y.vec.size(),
                                  DB_Row<T>::max_size())) {
  vec.construct_upward_approximation(y.vec, vec_capacity);
  PPL_ASSERT(OK());
}

template <typename T>
inline OR_Matrix<T>&
OR_Matrix<T>::operator=(const OR_Matrix& y) {
  vec = y.vec;
  space_dim = y.space_dim;
  vec_capacity = compute_capacity(y.vec.size(), DB_Row<T>::max_size());
  return *this;
}

template <typename T>
inline void
OR_Matrix<T>::grow(const dimension_type new_dim) {
  PPL_ASSERT(new_dim >= space_dim);
  if (new_dim > space_dim) {
    const dimension_type new_size = 2*new_dim*(new_dim + 1);
    if (new_size <= vec_capacity) {
      // We can recycle the old vec.
      vec.expand_within_capacity(new_size);
      space_dim = new_dim;
    }
    else {
      // We cannot recycle the old vec.
      OR_Matrix<T> new_matrix(new_dim);
      element_iterator j = new_matrix.element_begin();
      for (element_iterator i = element_begin(),
             mend = element_end(); i != mend; ++i, ++j) {
        assign_or_swap(*j, *i);
      }
      m_swap(new_matrix);
    }
  }
}

template <typename T>
inline void
OR_Matrix<T>::shrink(const dimension_type new_dim) {
  PPL_ASSERT(new_dim <= space_dim);
  const dimension_type new_size = 2*new_dim*(new_dim + 1);
  vec.shrink(new_size);
  space_dim = new_dim;
}

template <typename T>
inline void
OR_Matrix<T>::resize_no_copy(const dimension_type new_dim) {
  if (new_dim > space_dim) {
    const dimension_type new_size = 2*new_dim*(new_dim + 1);
    if (new_size <= vec_capacity) {
      // We can recycle the old vec.
      vec.expand_within_capacity(new_size);
      space_dim = new_dim;
    }
    else {
      // We cannot recycle the old vec.
      OR_Matrix<T> new_matrix(new_dim);
      m_swap(new_matrix);
    }
  }
  else if (new_dim < space_dim) {
    shrink(new_dim);
  }
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates OR_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Specialization, typename Temp, typename To, typename T>
inline bool
l_m_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                    const OR_Matrix<T>& x,
                    const OR_Matrix<T>& y,
                    const Rounding_Dir dir,
                    Temp& tmp0,
                    Temp& tmp1,
                    Temp& tmp2) {
  if (x.num_rows() != y.num_rows()) {
    return false;
  }
  assign_r(tmp0, 0, ROUND_NOT_NEEDED);
  for (typename OR_Matrix<T>::const_element_iterator
         i = x.element_begin(), j = y.element_begin(),
         mat_end = x.element_end(); i != mat_end; ++i, ++j) {
    const T& x_i = *i;
    const T& y_i = *j;
    if (is_plus_infinity(x_i)) {
      if (is_plus_infinity(y_i)) {
        continue;
      }
      else {
      pinf:
        assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
        return true;
      }
    }
    else if (is_plus_infinity(y_i)) {
      goto pinf;
    }
    const Temp* tmp1p;
    const Temp* tmp2p;
    if (x_i > y_i) {
      maybe_assign(tmp1p, tmp1, x_i, dir);
      maybe_assign(tmp2p, tmp2, y_i, inverse(dir));
    }
    else {
      maybe_assign(tmp1p, tmp1, y_i, dir);
      maybe_assign(tmp2p, tmp2, x_i, inverse(dir));
    }
    sub_assign_r(tmp1, *tmp1p, *tmp2p, dir);
    PPL_ASSERT(sgn(tmp1) >= 0);
    Specialization::combine(tmp0, tmp1, dir);
  }

  Specialization::finalize(tmp0, dir);
  assign_r(r, tmp0, dir);
  return true;
}


#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates OR_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const OR_Matrix<T>& x,
                            const OR_Matrix<T>& y,
                            const Rounding_Dir dir,
                            Temp& tmp0,
                            Temp& tmp1,
                            Temp& tmp2) {
  return
    l_m_distance_assign<Rectilinear_Distance_Specialization<Temp> >(r, x, y,
                                                                    dir,
                                                                    tmp0,
                                                                    tmp1,
                                                                    tmp2);
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates OR_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const OR_Matrix<T>& x,
                          const OR_Matrix<T>& y,
                          const Rounding_Dir dir,
                          Temp& tmp0,
                          Temp& tmp1,
                          Temp& tmp2) {
  return
    l_m_distance_assign<Euclidean_Distance_Specialization<Temp> >(r, x, y,
                                                                  dir,
                                                                  tmp0,
                                                                  tmp1,
                                                                  tmp2);
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates OR_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Temp, typename To, typename T>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const OR_Matrix<T>& x,
                           const OR_Matrix<T>& y,
                           const Rounding_Dir dir,
                           Temp& tmp0,
                           Temp& tmp1,
                           Temp& tmp2) {
  return
    l_m_distance_assign<L_Infinity_Distance_Specialization<Temp> >(r, x, y,
                                                                   dir,
                                                                   tmp0,
                                                                   tmp1,
                                                                   tmp2);
}

/*! \relates OR_Matrix */
template <typename T>
inline void
swap(OR_Matrix<T>& x, OR_Matrix<T>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/OR_Matrix_templates.hh line 1. */
/* OR_Matrix class implementation: non-inline template functions.
*/


#include <iostream>

namespace Parma_Polyhedra_Library {

template <typename T>
memory_size_type
OR_Matrix<T>::external_memory_in_bytes() const{
  return vec.external_memory_in_bytes();
}

template <typename T>
bool
OR_Matrix<T>::OK() const {
#ifndef NDEBUG
  using std::endl;
  using std::cerr;
#endif
  // The right number of cells should be in use.
  const dimension_type dim = space_dimension();
  if (vec.size() != 2*dim*(dim + 1)) {
#ifndef NDEBUG
    cerr << "OR_Matrix has a wrong number of cells:\n"
         << "vec.size() is " << vec.size()
         << ", expected size is " << (2*dim*(dim+1)) << "!\n";
#endif
    return false;
  }

  // The underlying DB_Row should be OK.
  if (!vec.OK(vec.size(), vec_capacity)) {
    return false;
  }

  // All checks passed.
  return true;
}

template <typename T>
void
OR_Matrix<T>::ascii_dump(std::ostream& s) const {
  const OR_Matrix<T>& x = *this;
  const char separator = ' ';
  dimension_type space = x.space_dimension();
  s << space << separator << "\n";
  for (const_row_iterator i = x.row_begin(),
         x_row_end = x.row_end(); i != x_row_end; ++i) {
    const_row_reference_type r = *i;
    dimension_type rs = i.row_size();
    for (dimension_type j = 0; j < rs; ++j) {
      using namespace IO_Operators;
      s << r[j] << separator;
    }
    s << "\n";
  }
}

PPL_OUTPUT_TEMPLATE_DEFINITIONS(T, OR_Matrix<T>)

template <typename T>
bool
OR_Matrix<T>::ascii_load(std::istream& s) {
  dimension_type space;
  if (!(s >> space)) {
    return false;
  }
  resize_no_copy(space);
  for (row_iterator i = row_begin(),
         this_row_end = row_end(); i != this_row_end; ++i) {
    row_reference_type r_i = *i;
    const dimension_type rs = i.row_size();
    for (dimension_type j = 0; j < rs; ++j) {
      Result r = input(r_i[j], s, ROUND_CHECK);
      if (result_relation(r) != VR_EQ || is_minus_infinity(r_i[j])) {
        return false;
      }
    }
  }
  PPL_ASSERT(OK());
  return true;
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Parma_Polyhedra_Library::OR_Matrix */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename T>
std::ostream&
IO_Operators::operator<<(std::ostream& s, const OR_Matrix<T>& m) {
  for (typename OR_Matrix<T>::const_row_iterator m_iter = m.row_begin(),
         m_end = m.row_end(); m_iter != m_end; ++m_iter) {
    typename OR_Matrix<T>::const_row_reference_type r_m = *m_iter;
    const dimension_type mr_size = m_iter.row_size();
    for (dimension_type j = 0; j < mr_size; ++j) {
      s << r_m[j] << " ";
    }
    s << "\n";
  }
  return s;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/OR_Matrix_defs.hh line 609. */

/* Automatically generated from PPL source file ../src/Octagonal_Shape_defs.hh line 50. */
#include <vector>
#include <cstddef>
#include <climits>
#include <iosfwd>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Octagonal_Shape
  Writes a textual representation of \p oct on \p s:
  <CODE>false</CODE> is written if \p oct is an empty polyhedron;
  <CODE>true</CODE> is written if \p oct is a universe polyhedron;
  a system of constraints defining \p oct is written otherwise,
  all constraints separated by ", ".
*/
template <typename T>
std::ostream&
operator<<(std::ostream& s, const Octagonal_Shape<T>& oct);

} // namespace IO_Operators

//! Swaps \p x with \p y.
/*! \relates Octagonal_Shape */
template <typename T>
void swap(Octagonal_Shape<T>& x, Octagonal_Shape<T>& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if \p x and \p y are the same octagon.

  \relates Octagonal_Shape
  Note that \p x and \p y may be dimension-incompatible shapes:
  in this case, the value <CODE>false</CODE> is returned.
*/
template <typename T>
bool operator==(const Octagonal_Shape<T>& x, const Octagonal_Shape<T>& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if \p x and \p y are different shapes.

  \relates Octagonal_Shape
  Note that \p x and \p y may be dimension-incompatible shapes:
  in this case, the value <CODE>true</CODE> is returned.
*/
template <typename T>
bool operator!=(const Octagonal_Shape<T>& x, const Octagonal_Shape<T>& y);

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates Octagonal_Shape
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.
*/
template <typename To, typename T>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const Octagonal_Shape<T>& x,
                                 const Octagonal_Shape<T>& y,
                                 Rounding_Dir dir);

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates Octagonal_Shape
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.
*/
template <typename Temp, typename To, typename T>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const Octagonal_Shape<T>& x,
                                 const Octagonal_Shape<T>& y,
                                 Rounding_Dir dir);

//! Computes the rectilinear (or Manhattan) distance between \p x and \p y.
/*! \relates Octagonal_Shape
  If the rectilinear distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
template <typename Temp, typename To, typename T>
bool rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                 const Octagonal_Shape<T>& x,
                                 const Octagonal_Shape<T>& y,
                                 Rounding_Dir dir,
                                 Temp& tmp0,
                                 Temp& tmp1,
                                 Temp& tmp2);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates Octagonal_Shape
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.
*/
template <typename To, typename T>
bool euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const Octagonal_Shape<T>& x,
                               const Octagonal_Shape<T>& y,
                               Rounding_Dir dir);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates Octagonal_Shape
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.
*/
template <typename Temp, typename To, typename T>
bool euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const Octagonal_Shape<T>& x,
                               const Octagonal_Shape<T>& y,
                               Rounding_Dir dir);

//! Computes the euclidean distance between \p x and \p y.
/*! \relates Octagonal_Shape
  If the euclidean distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
template <typename Temp, typename To, typename T>
bool euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const Octagonal_Shape<T>& x,
                               const Octagonal_Shape<T>& y,
                               Rounding_Dir dir,
                               Temp& tmp0,
                               Temp& tmp1,
                               Temp& tmp2);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates Octagonal_Shape
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<To, Extended_Number_Policy\></CODE>.
*/
template <typename To, typename T>
bool l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const Octagonal_Shape<T>& x,
                                const Octagonal_Shape<T>& y,
                                Rounding_Dir dir);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates Octagonal_Shape
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using variables of type
  <CODE>Checked_Number\<Temp, Extended_Number_Policy\></CODE>.
*/
template <typename Temp, typename To, typename T>
bool l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const Octagonal_Shape<T>& x,
                                const Octagonal_Shape<T>& y,
                                Rounding_Dir dir);

//! Computes the \f$L_\infty\f$ distance between \p x and \p y.
/*! \relates Octagonal_Shape
  If the \f$L_\infty\f$ distance between \p x and \p y is defined,
  stores an approximation of it into \p r and returns <CODE>true</CODE>;
  returns <CODE>false</CODE> otherwise.

  The direction of the approximation is specified by \p dir.

  All computations are performed using the temporary variables
  \p tmp0, \p tmp1 and \p tmp2.
*/
template <typename Temp, typename To, typename T>
bool l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const Octagonal_Shape<T>& x,
                                const Octagonal_Shape<T>& y,
                                Rounding_Dir dir,
                                Temp& tmp0,
                                Temp& tmp1,
                                Temp& tmp2);

// This class contains some helper functions that need to be friends of
// Linear_Expression.
class Octagonal_Shape_Helper {
public:
  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! Decodes the constraint \p c as an octagonal difference.
  /*! \relates Octagonal_Shape
    \return
    <CODE>true</CODE> if the constraint \p c is an octagonal difference;
    <CODE>false</CODE> otherwise.

    \param c
    The constraint to be decoded.

    \param c_space_dim
    The space dimension of the constraint \p c (it is <EM>assumed</EM>
    to match the actual space dimension of \p c).

    \param c_num_vars
    If <CODE>true</CODE> is returned, then it will be set to the number
    of variables having a non-zero coefficient. The only legal values
    will therefore be 0, 1 and 2.

    \param c_first_var
    If <CODE>true</CODE> is returned and if \p c_num_vars is not set to 0,
    then it will be set to the index of the first variable having
    a non-zero coefficient in \p c.

    \param c_second_var
    If <CODE>true</CODE> is returned and if \p c_num_vars is set to 2,
    then it will be set to the index of the second variable having
    a non-zero coefficient in \p c.

    \param c_coeff
    If <CODE>true</CODE> is returned and if \p c_num_vars is not set to 0,
    then it will be set to the value of the first non-zero coefficient
    in \p c.

    \param c_term
    If <CODE>true</CODE> is returned and if \p c_num_vars is not set to 0,
    then it will be set to the right value of the inhomogeneous term
    of \p c.
  */
  #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  static bool extract_octagonal_difference(const Constraint& c,
                                           dimension_type c_space_dim,
                                           dimension_type& c_num_vars,
                                           dimension_type& c_first_var,
                                           dimension_type& c_second_var,
                                           Coefficient& c_coeff,
                                           Coefficient& c_term);
};

} // namespace Parma_Polyhedra_Library

//! An octagonal shape.
/*! \ingroup PPL_CXX_interface
  The class template Octagonal_Shape<T> allows for the efficient
  representation of a restricted kind of <EM>topologically closed</EM>
  convex polyhedra called <EM>octagonal shapes</EM> (OSs, for short).
  The name comes from the fact that, in a vector space of dimension 2,
  bounded OSs are polygons with at most eight sides.
  The closed affine half-spaces that characterize the OS can be expressed
  by constraints of the form
  \f[
    ax_i + bx_j \leq k
  \f]
  where \f$a, b \in \{-1, 0, 1\}\f$ and \f$k\f$ is a rational number,
  which are called <EM>octagonal constraints</EM>.

  Based on the class template type parameter \p T, a family of extended
  numbers is built and used to approximate the inhomogeneous term of
  octagonal constraints. These extended numbers provide a representation
  for the value \f$+\infty\f$, as well as <EM>rounding-aware</EM>
  implementations for several arithmetic functions.
  The value of the type parameter \p T may be one of the following:
    - a bounded precision integer type (e.g., \c int32_t or \c int64_t);
    - a bounded precision floating point type (e.g., \c float or \c double);
    - an unbounded integer or rational type, as provided by GMP
      (i.e., \c mpz_class or \c mpq_class).

  The user interface for OSs is meant to be as similar as possible to
  the one developed for the polyhedron class C_Polyhedron.

  The OS domain <EM>optimally supports</EM>:
    - tautological and inconsistent constraints and congruences;
    - octagonal constraints;
    - non-proper congruences (i.e., equalities) that are expressible
      as octagonal constraints.

  Depending on the method, using a constraint or congruence that is not
  optimally supported by the domain will either raise an exception or
  result in a (possibly non-optimal) upward approximation.

  A constraint is octagonal if it has the form
    \f[
      \pm a_i x_i \pm a_j x_j \relsym b
    \f]
  where \f$\mathord{\relsym} \in \{ \leq, =, \geq \}\f$ and
  \f$a_i\f$, \f$a_j\f$, \f$b\f$ are integer coefficients such that
  \f$a_i = 0\f$, or \f$a_j = 0\f$, or \f$a_i = a_j\f$.
  The user is warned that the above octagonal Constraint object
  will be mapped into a \e correct and \e optimal approximation that,
  depending on the expressive power of the chosen template argument \p T,
  may loose some precision.
  Also note that strict constraints are not octagonal.

  For instance, a Constraint object encoding \f$3x + 3y \leq 1\f$ will be
  approximated by:
    - \f$x + y \leq 1\f$,
      if \p T is a (bounded or unbounded) integer type;
    - \f$x + y \leq \frac{1}{3}\f$,
      if \p T is the unbounded rational type \c mpq_class;
    - \f$x + y \leq k\f$, where \f$k > \frac{1}{3}\f$,
      if \p T is a floating point type (having no exact representation
      for \f$\frac{1}{3}\f$).

  On the other hand, depending from the context, a Constraint object
  encoding \f$3x - y \leq 1\f$ will be either upward approximated
  (e.g., by safely ignoring it) or it will cause an exception.

  In the following examples it is assumed that the type argument \p T
  is one of the possible instances listed above and that variables
  \c x, \c y and \c z are defined (where they are used) as follows:
  \code
    Variable x(0);
    Variable y(1);
    Variable z(2);
  \endcode

  \par Example 1
  The following code builds an OS corresponding to a cube in \f$\Rset^3\f$,
  given as a system of constraints:
  \code
    Constraint_System cs;
    cs.insert(x >= 0);
    cs.insert(x <= 3);
    cs.insert(y >= 0);
    cs.insert(y <= 3);
    cs.insert(z >= 0);
    cs.insert(z <= 3);
    Octagonal_Shape<T> oct(cs);
  \endcode
  In contrast, the following code will raise an exception,
  since constraints 7, 8, and 9 are not octagonal:
  \code
    Constraint_System cs;
    cs.insert(x >= 0);
    cs.insert(x <= 3);
    cs.insert(y >= 0);
    cs.insert(y <= 3);
    cs.insert(z >= 0);
    cs.insert(z <= 3);
    cs.insert(x - 3*y <= 5);    // (7)
    cs.insert(x - y + z <= 5);  // (8)
    cs.insert(x + y + z <= 5);  // (9)
    Octagonal_Shape<T> oct(cs);
  \endcode
*/
template <typename T>
class Parma_Polyhedra_Library::Octagonal_Shape {
private:
  /*! \brief
    The (extended) numeric type of the inhomogeneous term of
    the inequalities defining an OS.
  */
#ifndef NDEBUG
  typedef Checked_Number<T, Debug_WRD_Extended_Number_Policy> N;
#else
  typedef Checked_Number<T, WRD_Extended_Number_Policy> N;
#endif

public:

  //! The numeric base type upon which OSs are built.
  typedef T coefficient_type_base;

  /*! \brief
    The (extended) numeric type of the inhomogeneous term of the
    inequalities defining an OS.
  */
  typedef N coefficient_type;

  //! Returns the maximum space dimension that an OS can handle.
  static dimension_type max_space_dimension();

  /*! \brief
    Returns false indicating that this domain cannot recycle constraints
  */
  static bool can_recycle_constraint_systems();

  /*! \brief
    Returns false indicating that this domain cannot recycle congruences
  */
  static bool can_recycle_congruence_systems();

  //! \name Constructors, Assignment, Swap and Destructor
  //@{

  //! Builds an universe or empty OS of the specified space dimension.
  /*!
    \param num_dimensions
    The number of dimensions of the vector space enclosing the OS;

    \param kind
    Specifies whether the universe or the empty OS has to be built.
  */
  explicit Octagonal_Shape(dimension_type num_dimensions = 0,
                           Degenerate_Element kind = UNIVERSE);

  //! Ordinary copy constructor.
  /*!
    The complexity argument is ignored.
  */
  Octagonal_Shape(const Octagonal_Shape& y,
                  Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a conservative, upward approximation of \p y.
  /*!
    The complexity argument is ignored.
  */
  template <typename U>
  explicit Octagonal_Shape(const Octagonal_Shape<U>& y,
                           Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds an OS from the system of constraints \p cs.
  /*!
    The OS inherits the space dimension of \p cs.

    \param cs
    A system of octagonal constraints.

    \exception std::invalid_argument
    Thrown if \p cs contains a constraint which is not optimally supported
    by the Octagonal shape domain.
  */
  explicit Octagonal_Shape(const Constraint_System& cs);

  //! Builds an OS from a system of congruences.
  /*!
    The OS inherits the space dimension of \p cgs

    \param cgs
    A system of congruences.

    \exception std::invalid_argument
    Thrown if \p cgs contains a congruence which is not optimally supported
    by the Octagonal shape domain.
  */
  explicit Octagonal_Shape(const Congruence_System& cgs);

  //! Builds an OS from the system of generators \p gs.
  /*!
    Builds the smallest OS containing the polyhedron defined by \p gs.
    The OS inherits the space dimension of \p gs.

    \exception std::invalid_argument
    Thrown if the system of generators is not empty but has no points.
  */
  explicit Octagonal_Shape(const Generator_System& gs);

  //! Builds an OS from the polyhedron \p ph.
  /*!
    Builds an OS containing \p ph using algorithms whose complexity
    does not exceed the one specified by \p complexity.  If
    \p complexity is \p ANY_COMPLEXITY, then the OS built is the
    smallest one containing \p ph.
  */
  explicit Octagonal_Shape(const Polyhedron& ph,
                           Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds an OS out of a box.
  /*!
    The OS inherits the space dimension of the box.
    The built OS is the most precise OS that includes the box.

    \param box
    The box representing the OS to be built.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.

    \exception std::length_error
    Thrown if the space dimension of \p box exceeds the maximum
    allowed space dimension.
  */
  template <typename Interval>
  explicit Octagonal_Shape(const Box<Interval>& box,
                           Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds an OS that approximates a grid.
  /*!
    The OS inherits the space dimension of the grid.
    The built OS is the most precise OS that includes the grid.

    \param grid
    The grid used to build the OS.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.

    \exception std::length_error
    Thrown if the space dimension of \p grid exceeds the maximum
    allowed space dimension.
  */
  explicit Octagonal_Shape(const Grid& grid,
                           Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds an OS from a BD shape.
  /*!
    The OS inherits the space dimension of the BD shape.
    The built OS is the most precise OS that includes the BD shape.

    \param bd
    The BD shape used to build the OS.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.

    \exception std::length_error
    Thrown if the space dimension of \p bd exceeds the maximum
    allowed space dimension.
  */
  template <typename U>
  explicit Octagonal_Shape(const BD_Shape<U>& bd,
                           Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    The assignment operator.
    (\p *this and \p y can be dimension-incompatible.)
  */
  Octagonal_Shape& operator=(const Octagonal_Shape& y);

  /*! \brief
    Swaps \p *this with octagon \p y.
    (\p *this and \p y can be dimension-incompatible.)
  */
  void m_swap(Octagonal_Shape& y);

  //! Destructor.
  ~Octagonal_Shape();

  //@} Constructors, Assignment, Swap and Destructor

  //! \name Member Functions that Do Not Modify the Octagonal_Shape
  //@{

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  /*! \brief
    Returns \f$0\f$, if \p *this is empty; otherwise, returns the
    \ref Affine_Independence_and_Affine_Dimension "affine dimension"
    of \p *this.
  */
  dimension_type affine_dimension() const;

  //! Returns the system of constraints defining \p *this.
  Constraint_System constraints() const;

  //! Returns a minimized system of constraints defining \p *this.
  Constraint_System minimized_constraints() const;

  //! Returns a system of (equality) congruences satisfied by \p *this.
  Congruence_System congruences() const;

  /*! \brief
    Returns a minimal system of (equality) congruences
    satisfied by \p *this with the same affine dimension as \p *this.
  */
  Congruence_System minimized_congruences() const;

  //! Returns <CODE>true</CODE> if and only if \p *this contains \p y.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool contains(const Octagonal_Shape& y) const;

  //! Returns <CODE>true</CODE> if and only if \p *this strictly contains \p y.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool strictly_contains(const Octagonal_Shape& y) const;

  //! Returns <CODE>true</CODE> if and only if \p *this and \p y are disjoint.
  /*!
    \exception std::invalid_argument
    Thrown if \p x and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool is_disjoint_from(const Octagonal_Shape& y) const;

  /*! \brief
    Returns the relations holding between \p *this and the constraint \p c.

    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible.
  */
  Poly_Con_Relation relation_with(const Constraint& c) const;

  /*! \brief
    Returns the relations holding between \p *this and the congruence \p cg.

    \exception std::invalid_argument
    Thrown if \p *this and \p cg are dimension-incompatible.
  */
  Poly_Con_Relation relation_with(const Congruence& cg) const;

  /*! \brief
    Returns the relations holding between \p *this and the generator \p g.

    \exception std::invalid_argument
    Thrown if \p *this and generator \p g are dimension-incompatible.
  */
  Poly_Gen_Relation relation_with(const Generator& g) const;

  //! Returns <CODE>true</CODE> if and only if \p *this is an empty OS.
  bool is_empty() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is a universe OS.
  bool is_universe() const;

  //! Returns <CODE>true</CODE> if and only if \p *this is discrete.
  bool is_discrete() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    is a bounded OS.
  */
  bool is_bounded() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    is a topologically closed subset of the vector space.
  */
  bool is_topologically_closed() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    contains (at least) an integer point.
  */
  bool contains_integer_point() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p var is constrained in
    \p *this.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  bool constrains(Variable var) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p expr is
    bounded from above in \p *this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_above(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p expr is
    bounded from below in \p *this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_below(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from above in \p *this, in which case
    the supremum value is computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if and only if the supremum is also the maximum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from above,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d
    and \p maximum are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from above in \p *this, in which case
    the supremum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if and only if the supremum is also the maximum value;

    \param g
    When maximization succeeds, will be assigned the point or
    closure point where \p expr reaches its supremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from above,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d, \p maximum
    and \p g are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from below in \p *this, in which case
    the infimum value is computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if and only if the infimum is also the minimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d
    and \p minimum are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from below in \p *this, in which case
    the infimum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if and only if the infimum is also the minimum value;

    \param g
    When minimization succeeds, will be assigned a point or
    closure point where \p expr reaches its infimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d, \p minimum
    and \p g are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if there exist a
    unique value \p val such that \p *this
    saturates the equality <CODE>expr = val</CODE>.

    \param expr
    The linear expression for which the frequency is needed;

    \param freq_n
    If <CODE>true</CODE> is returned, the value is set to \f$0\f$;
    Present for interface compatibility with class Grid, where
    the \ref Grid_Frequency "frequency" can have a non-zero value;

    \param freq_d
    If <CODE>true</CODE> is returned, the value is set to \f$1\f$;

    \param val_n
    The numerator of \p val;

    \param val_d
    The denominator of \p val;

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If <CODE>false</CODE> is returned, then \p freq_n, \p freq_d,
    \p val_n and \p val_d are left untouched.
  */
  bool frequency(const Linear_Expression& expr,
                 Coefficient& freq_n, Coefficient& freq_d,
                 Coefficient& val_n, Coefficient& val_d) const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  //@} Member Functions that Do Not Modify the Octagonal_Shape

  //! \name Space-Dimension Preserving Member Functions that May Modify the Octagonal_Shape
  //@{

  /*! \brief
    Adds a copy of constraint \p c to the system of constraints
    defining \p *this.

    \param c
    The constraint to be added.

    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible,
    or \p c is not optimally supported by the OS domain.
  */
  void add_constraint(const Constraint& c);

  /*! \brief
    Adds the constraints in \p cs to the system of constraints
    defining \p *this.

    \param  cs
    The constraints that will be added.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible,
    or \p cs contains a constraint which is not optimally supported
    by the OS domain.
  */
  void add_constraints(const Constraint_System& cs);

  /*! \brief
    Adds the constraints in \p cs to the system of constraints
    of \p *this.

    \param cs
    The constraint system to be added to \p *this.  The constraints in
    \p cs may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible,
    or \p cs contains a constraint which is not optimally supported
    by the OS domain.

    \warning
    The only assumption that can be made on \p cs upon successful or
    exceptional return is that it can be safely destroyed.
  */
  void add_recycled_constraints(Constraint_System& cs);

  /*! \brief
    Adds to \p *this a constraint equivalent to the congruence \p cg.

    \param cg
    The congruence to be added.

    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible,
    or \p cg is not optimally supported by the OS domain.
  */
  void add_congruence(const Congruence& cg);

  /*! \brief
    Adds to \p *this constraints equivalent to the congruences in \p cgs.

    \param cgs
    The congruences to be added.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible,
    or \p cgs contains a congruence which is not optimally supported
    by the OS domain.
  */
  void add_congruences(const Congruence_System& cgs);

  /*! \brief
    Adds to \p *this constraints equivalent to the congruences in \p cgs.

    \param cgs
    The congruence system to be added to \p *this.  The congruences in
    \p cgs may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible,
    or \p cgs contains a congruence which is not optimally supported
    by the OS domain.

    \warning
    The only assumption that can be made on \p cgs upon successful or
    exceptional return is that it can be safely destroyed.
  */
  void add_recycled_congruences(Congruence_System& cgs);

  /*! \brief
    Uses a copy of constraint \p c to refine the system of octagonal
    constraints defining \p *this.

    \param c
    The constraint. If it is not a octagonal constraint, it will be ignored.

    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible.
  */
  void refine_with_constraint(const Constraint& c);

  /*! \brief
    Uses a copy of congruence \p cg to refine the system of
    octagonal constraints  of \p *this.

    \param cg
    The congruence. If it is not a octagonal equality, it
    will be ignored.

    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible.
  */
  void refine_with_congruence(const Congruence& cg);

  /*! \brief
    Uses a copy of the constraints in \p cs to refine the system of
    octagonal constraints defining \p *this.

    \param  cs
    The constraint system to be used. Constraints that are not octagonal
    are ignored.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible.
  */
  void refine_with_constraints(const Constraint_System& cs);

  /*! \brief
    Uses a copy of the congruences in \p cgs to refine the system of
    octagonal constraints defining \p *this.

    \param  cgs
    The congruence system to be used. Congruences that are not octagonal
    equalities are ignored.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible.
  */
  void refine_with_congruences(const Congruence_System& cgs);

  /*! \brief
    Refines the system of octagonal constraints defining \p *this using
    the constraint expressed by \p left \f$\leq\f$ \p right.

    \param left
    The linear form on intervals with floating point boundaries that
    is at the left of the comparison operator. All of its coefficients
    MUST be bounded.

    \param right
    The linear form on intervals with floating point boundaries that
    is at the right of the comparison operator. All of its coefficients
    MUST be bounded.

    \exception std::invalid_argument
    Thrown if \p left (or \p right) is dimension-incompatible with \p *this.

    This function is used in abstract interpretation to model a filter
    that is generated by a comparison of two expressions that are correctly
    approximated by \p left and \p right respectively.
  */
  template <typename Interval_Info>
  void refine_with_linear_form_inequality(
                   const Linear_Form< Interval<T, Interval_Info> >& left,
                   const Linear_Form< Interval<T, Interval_Info> >& right);

  /*! \brief
    Refines the system of octagonal constraints defining \p *this using
    the constraint expressed by \p left \f$\relsym\f$ \p right, where
    \f$\relsym\f$ is the relation symbol specified by \p relsym.

    \param left
    The linear form on intervals with floating point boundaries that
    is at the left of the comparison operator. All of its coefficients
    MUST be bounded.

    \param right
    The linear form on intervals with floating point boundaries that
    is at the right of the comparison operator. All of its coefficients
    MUST be bounded.

    \param relsym
    The relation symbol.

    \exception std::invalid_argument
    Thrown if \p left (or \p right) is dimension-incompatible with \p *this.

    \exception std::runtime_error
    Thrown if \p relsym is not a valid relation symbol.

    This function is used in abstract interpretation to model a filter
    that is generated by a comparison of two expressions that are correctly
    approximated by \p left and \p right respectively.
  */
  template <typename Interval_Info>
  void generalized_refine_with_linear_form_inequality(
                   const Linear_Form< Interval<T, Interval_Info> >& left,
                   const Linear_Form< Interval<T, Interval_Info> >& right,
                   Relation_Symbol relsym);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to space dimension \p var, assigning the result to \p *this.

    \param var
    The space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  void unconstrain(Variable var);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to the set of space dimensions \p vars,
    assigning the result to \p *this.

    \param vars
    The set of space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars.
  */
  void unconstrain(const Variables_Set& vars);

  //! Assigns to \p *this the intersection of \p *this and \p y.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void intersection_assign(const Octagonal_Shape& y);

  /*! \brief
    Assigns to \p *this the smallest OS that contains
    the convex union of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void upper_bound_assign(const Octagonal_Shape& y);

  /*! \brief
    If the upper bound of \p *this and \p y is exact, it is assigned
    to \p *this and <CODE>true</CODE> is returned,
    otherwise <CODE>false</CODE> is returned.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.

    Implementation is based on Theorem 6.3 of \ref BHZ09b "[BHZ09b]".
  */
  bool upper_bound_assign_if_exact(const Octagonal_Shape& y);

  /*! \brief
    If the \e integer upper bound of \p *this and \p y is exact,
    it is assigned to \p *this and <CODE>true</CODE> is returned;
    otherwise <CODE>false</CODE> is returned.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.

    \note
    This operator is only available when the class template parameter
    \c T is bound to an integer data type.

    \note
    The integer upper bound of two rational OS is the smallest
    rational OS containing all the integral points in the two arguments.
    In general, the result is \e not an upper bound for the two input
    arguments, as it may cut away non-integral portions of the two
    rational shapes.

    Implementation is based on Theorem 6.8 of \ref BHZ09b "[BHZ09b]".
  */
  bool integer_upper_bound_assign_if_exact(const Octagonal_Shape& y);

  /*! \brief
    Assigns to \p *this the smallest octagon containing
    the set difference of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void difference_assign(const Octagonal_Shape& y);

  /*! \brief
    Assigns to \p *this a \ref Meet_Preserving_Simplification
    "meet-preserving simplification" of \p *this with respect to \p y.
    If \c false is returned, then the intersection is empty.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool simplify_using_context_assign(const Octagonal_Shape& y);

  /*! \brief
    Assigns to \p *this the \ref affine_relation "affine image"
    of \p *this under the function mapping variable \p var into the
    affine expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is assigned.

    \param expr
    The numerator of the affine expression.

    \param denominator
    The denominator of the affine expression.

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this
    are dimension-incompatible or if \p var is not a dimension of \p *this.
  */
  void affine_image(Variable var,
                    const Linear_Expression& expr,
                    Coefficient_traits::const_reference  denominator
                    = Coefficient_one());

  // FIXME: To be completed.
  /*! \brief
    Assigns to \p *this the \ref affine_form_relation "affine form image"
    of \p *this under the function mapping variable \p var into the
    affine expression(s) specified by \p lf.

    \param var
    The variable to which the affine expression is assigned.

    \param lf
    The linear form on intervals with floating point boundaries that
    defines the affine expression(s). ALL of its coefficients MUST be bounded.

    \exception std::invalid_argument
    Thrown if \p lf and \p *this are dimension-incompatible or if \p var
    is not a dimension of \p *this.

    This function is used in abstract interpretation to model an assignment
    of a value that is correctly overapproximated by \p lf to the
    floating point variable represented by \p var.
  */
  template <typename Interval_Info>
  void affine_form_image(Variable var,
                        const Linear_Form< Interval<T, Interval_Info> >& lf);

  /*! \brief
    Assigns to \p *this the \ref affine_relation "affine preimage"
    of \p *this under the function mapping variable \p var into the
    affine expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is substituted.

    \param expr
    The numerator of the affine expression.

    \param denominator
    The denominator of the affine expression.

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this
    are dimension-incompatible or if \p var is not a dimension of \p *this.
  */
  void affine_preimage(Variable var,
                       const Linear_Expression& expr,
                       Coefficient_traits::const_reference denominator
                       = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine transfer function"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym.

    \param var
    The left hand side variable of the generalized affine transfer function.

    \param relsym
    The relation symbol.

    \param expr
    The numerator of the right hand side affine expression.

    \param denominator
    The denominator of the right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this
    are dimension-incompatible or if \p var is not a dimension of \p *this
    or if \p relsym is a strict relation symbol.
  */
  void generalized_affine_image(Variable var,
                                Relation_Symbol relsym,
                                const Linear_Expression& expr,
                                Coefficient_traits::const_reference denominator
                                = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine transfer function"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.

    \param lhs
    The left hand side affine expression.

    \param relsym
    The relation symbol.

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs
    or if \p relsym is a strict relation symbol.
  */
  void generalized_affine_image(const Linear_Expression& lhs,
                                Relation_Symbol relsym,
                                const Linear_Expression& rhs);

    /*!
    \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_image(Variable var,
                            const Linear_Expression& lb_expr,
                            const Linear_Expression& ub_expr,
                            Coefficient_traits::const_reference denominator
                            = Coefficient_one());

/*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "affine relation"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym.

    \param var
    The left hand side variable of the generalized affine transfer function.

    \param relsym
    The relation symbol.

    \param expr
    The numerator of the right hand side affine expression.

    \param denominator
    The denominator of the right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this
    are dimension-incompatible or if \p var is not a dimension
    of \p *this or if \p relsym is a strict relation symbol.
  */
  void generalized_affine_preimage(Variable var,
                                   Relation_Symbol relsym,
                                   const Linear_Expression& expr,
                                   Coefficient_traits::const_reference
                                   denominator = Coefficient_one());

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.

    \param lhs
    The left hand side affine expression;

    \param relsym
    The relation symbol;

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs
    or if \p relsym is a strict relation symbol.
  */
  void generalized_affine_preimage(const Linear_Expression& lhs,
                                   Relation_Symbol relsym,
                                   const Linear_Expression& rhs);

  /*!
    \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
         \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_preimage(Variable var,
                               const Linear_Expression& lb_expr,
                               const Linear_Expression& ub_expr,
                               Coefficient_traits::const_reference denominator
                               = Coefficient_one());

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref Time_Elapse_Operator "time-elapse" between \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void time_elapse_assign(const Octagonal_Shape& y);

  /*! \brief
    \ref Wrapping_Operator "Wraps" the specified dimensions of the
    vector space.

    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be wrapped.

    \param w
    The width of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param r
    The representation of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param o
    The overflow behavior of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param cs_p
    Possibly null pointer to a constraint system whose variables
    are contained in \p vars.  If <CODE>*cs_p</CODE> depends on
    variables not in \p vars, the behavior is undefined.
    When non-null, the pointed-to constraint system is assumed to
    represent the conditional or looping construct guard with respect
    to which wrapping is performed.  Since wrapping requires the
    computation of upper bounds and due to non-distributivity of
    constraint refinement over upper bounds, passing a constraint
    system in this way can be more precise than refining the result of
    the wrapping operation with the constraints in <CODE>*cs_p</CODE>.

    \param complexity_threshold
    A precision parameter of the \ref Wrapping_Operator "wrapping operator":
    higher values result in possibly improved precision.

    \param wrap_individually
    <CODE>true</CODE> if the dimensions should be wrapped individually
    (something that results in much greater efficiency to the detriment of
    precision).

    \exception std::invalid_argument
    Thrown if <CODE>*cs_p</CODE> is dimension-incompatible with
    \p vars, or if \p *this is dimension-incompatible \p vars or with
    <CODE>*cs_p</CODE>.
  */
  void wrap_assign(const Variables_Set& vars,
                   Bounded_Integer_Type_Width w,
                   Bounded_Integer_Type_Representation r,
                   Bounded_Integer_Type_Overflow o,
                   const Constraint_System* cs_p = 0,
                   unsigned complexity_threshold = 16,
                   bool wrap_individually = true);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates for the space dimensions corresponding to \p vars.

    \param vars
    Points with non-integer coordinates for these variables/space-dimensions
    can be discarded.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(const Variables_Set& vars,
                                    Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  //! Assigns to \p *this its topological closure.
  void topological_closure_assign();

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref CC76_extrapolation "CC76-extrapolation" between \p *this and \p y.

    \param y
    An OS that <EM>must</EM> be contained in \p *this.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void CC76_extrapolation_assign(const Octagonal_Shape& y, unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref CC76_extrapolation "CC76-extrapolation" between \p *this and \p y.

    \param y
    An OS that <EM>must</EM> be contained in \p *this.

    \param first
    An iterator that points to the first stop_point.

    \param last
    An iterator that points to the last stop_point.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  template <typename Iterator>
  void CC76_extrapolation_assign(const Octagonal_Shape& y,
                                 Iterator first, Iterator last,
                                 unsigned* tp = 0);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref BHMZ05_widening "BHMZ05-widening" between \p *this and \p y.

    \param y
    An OS that <EM>must</EM> be contained in \p *this.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void BHMZ05_widening_assign(const Octagonal_Shape& y, unsigned* tp = 0);

  //! Same as BHMZ05_widening_assign(y, tp).
  void widening_assign(const Octagonal_Shape& y, unsigned* tp = 0);

  /*! \brief
    Improves the result of the \ref BHMZ05_widening "BHMZ05-widening"
    computation by also enforcing those constraints in \p cs that are
    satisfied by all the points of \p *this.

    \param y
    An OS that <EM>must</EM> be contained in \p *this.

    \param cs
    The system of constraints used to improve the widened OS.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cs are dimension-incompatible or
    if there is in \p cs a strict inequality.
  */
  void limited_BHMZ05_extrapolation_assign(const Octagonal_Shape& y,
                                           const Constraint_System& cs,
                                           unsigned* tp = 0);

  /*! \brief
    Restores from \p y the constraints of \p *this, lost by
    \ref CC76_extrapolation "CC76-extrapolation" applications.

    \param y
    An OS that <EM>must</EM> contain \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void CC76_narrowing_assign(const Octagonal_Shape& y);

  /*! \brief
    Improves the result of the \ref CC76_extrapolation "CC76-extrapolation"
    computation by also enforcing those constraints in \p cs that are
    satisfied by all the points of \p *this.

    \param y
    An OS that <EM>must</EM> be contained in \p *this.

    \param cs
    The system of constraints used to improve the widened OS.

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this, \p y and \p cs are dimension-incompatible or
    if \p cs contains a strict inequality.
  */
  void limited_CC76_extrapolation_assign(const Octagonal_Shape& y,
                                         const Constraint_System& cs,
                                         unsigned* tp = 0);

  //@} Space-Dimension Preserving Member Functions that May Modify [...]

  //! \name Member Functions that May Modify the Dimension of the Vector Space
  //@{

  //! Adds \p m new dimensions and embeds the old OS into the new space.
  /*!
    \param m
    The number of dimensions to add.

    The new dimensions will be those having the highest indexes in the new OS,
    which is characterized by a system of constraints in which the variables
    running through the new dimensions are not constrained.
    For instance, when starting from the OS \f$\cO \sseq \Rset^2\f$
    and adding a third dimension, the result will be the OS
    \f[
      \bigl\{\,
        (x, y, z)^\transpose \in \Rset^3
      \bigm|
        (x, y)^\transpose \in \cO
      \,\bigr\}.
    \f]
  */
  void add_space_dimensions_and_embed(dimension_type m);

  /*! \brief
    Adds \p m new dimensions to the OS
    and does not embed it in the new space.

    \param m
    The number of dimensions to add.

    The new dimensions will be those having the highest indexes
    in the new OS, which is characterized by a system
    of constraints in which the variables running through
    the new dimensions are all constrained to be equal to 0.
    For instance, when starting from the OS \f$\cO \sseq \Rset^2\f$
    and adding a third dimension, the result will be the OS
    \f[
      \bigl\{\,
        (x, y, 0)^\transpose \in \Rset^3
      \bigm|
        (x, y)^\transpose \in \cO
      \,\bigr\}.
    \f]
  */
  void add_space_dimensions_and_project(dimension_type m);

  /*! \brief
    Assigns to \p *this the \ref Concatenating_Polyhedra "concatenation"
    of \p *this and \p y, taken in this order.

    \exception std::length_error
    Thrown if the concatenation would cause the vector space
    to exceed dimension <CODE>max_space_dimension()</CODE>.
  */
  void concatenate_assign(const Octagonal_Shape& y);

  //! Removes all the specified dimensions.
  /*!
    \param vars
    The set of Variable objects corresponding to the dimensions to be removed.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the Variable
    objects contained in \p vars.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  /*! \brief
    Removes the higher dimensions so that the resulting space
    will have dimension \p new_dimension.

    \exception std::invalid_argument
    Thrown if \p new_dimension is greater than the space dimension
    of \p *this.
  */
  void remove_higher_space_dimensions(dimension_type new_dimension);

  /*! \brief
    Remaps the dimensions of the vector space according to
    a \ref Mapping_the_Dimensions_of_the_Vector_Space "partial function".

    \param pfunc
    The partial function specifying the destiny of each dimension.

    The template type parameter Partial_Function must provide
    the following methods.
    \code
      bool has_empty_codomain() const
    \endcode
    returns <CODE>true</CODE> if and only if the represented partial
    function has an empty codomain (i.e., it is always undefined).
    The <CODE>has_empty_codomain()</CODE> method will always be called
    before the methods below.  However, if
    <CODE>has_empty_codomain()</CODE> returns <CODE>true</CODE>, none
    of the functions below will be called.
    \code
      dimension_type max_in_codomain() const
    \endcode
    returns the maximum value that belongs to the codomain
    of the partial function.
    \code
      bool maps(dimension_type i, dimension_type& j) const
    \endcode
    Let \f$f\f$ be the represented function and \f$k\f$ be the value
    of \p i.  If \f$f\f$ is defined in \f$k\f$, then \f$f(k)\f$ is
    assigned to \p j and <CODE>true</CODE> is returned.
    If \f$f\f$ is undefined in \f$k\f$, then <CODE>false</CODE> is
    returned.

    The result is undefined if \p pfunc does not encode a partial
    function with the properties described in the
    \ref Mapping_the_Dimensions_of_the_Vector_Space "specification of the mapping operator".
  */
  template <typename Partial_Function>
  void map_space_dimensions(const Partial_Function& pfunc);

  //! Creates \p m copies of the space dimension corresponding to \p var.
  /*!
    \param var
    The variable corresponding to the space dimension to be replicated;

    \param m
    The number of replicas to be created.

    \exception std::invalid_argument
    Thrown if \p var does not correspond to a dimension of the vector space.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the
    vector space to exceed dimension <CODE>max_space_dimension()</CODE>.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    and <CODE>var</CODE> has space dimension \f$k \leq n\f$,
    then the \f$k\f$-th space dimension is
    \ref expand_space_dimension "expanded" to \p m new space dimensions
    \f$n\f$, \f$n+1\f$, \f$\dots\f$, \f$n+m-1\f$.
  */
  void expand_space_dimension(Variable var, dimension_type m);

  //! Folds the space dimensions in \p vars into \p dest.
  /*!
    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be folded;

    \param dest
    The variable corresponding to the space dimension that is the
    destination of the folding operation.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p dest or with
    one of the Variable objects contained in \p vars.
    Also thrown if \p dest is contained in \p vars.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    <CODE>dest</CODE> has space dimension \f$k \leq n\f$,
    \p vars is a set of variables whose maximum space dimension
    is also less than or equal to \f$n\f$, and \p dest is not a member
    of \p vars, then the space dimensions corresponding to
    variables in \p vars are \ref fold_space_dimensions "folded"
    into the \f$k\f$-th space dimension.
  */
  void fold_space_dimensions(const Variables_Set& vars, Variable dest);

  //! Applies to \p dest the interval constraints embedded in \p *this.
  /*!
    \param dest
    The object to which the constraints will be added.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p dest.

    The template type parameter U must provide the following methods.
    \code
      dimension_type space_dimension() const
    \endcode
    returns the space dimension of the object.
    \code
      void set_empty()
    \endcode
    sets the object to an empty object.
    \code
      bool restrict_lower(dimension_type dim, const T& lb)
    \endcode
    restricts the object by applying the lower bound \p lb to the space
    dimension \p dim and returns <CODE>false</CODE> if and only if the
    object becomes empty.
    \code
      bool restrict_upper(dimension_type dim, const T& ub)
    \endcode
    restricts the object by applying the upper bound \p ub to the space
    dimension \p dim and returns <CODE>false</CODE> if and only if the
    object becomes empty.
  */
  template <typename U>
  void export_interval_constraints(U& dest) const;

  //! Refines \p store with the constraints defining \p *this.
  /*!
    \param store
    The interval floating point abstract store to refine.
  */
  template <typename Interval_Info>
  void refine_fp_interval_abstract_store(
                          Box< Interval<T, Interval_Info> >& store) const;

  //@} // Member Functions that May Modify the Dimension of the Vector Space

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns a 32-bit hash code for \p *this.

    If \p x and \p y are such that <CODE>x == y</CODE>,
    then <CODE>x.hash_code() == y.hash_code()</CODE>.
  */
  int32_t hash_code() const;

  friend bool
  operator==<T>(const Octagonal_Shape<T>& x, const Octagonal_Shape<T>& y);

  template <typename Temp, typename To, typename U>
  friend bool Parma_Polyhedra_Library
  ::rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                                const Octagonal_Shape<U>& x,
                                const Octagonal_Shape<U>& y,
                                const Rounding_Dir dir,
                                Temp& tmp0, Temp& tmp1, Temp& tmp2);
  template <typename Temp, typename To, typename U>
  friend bool Parma_Polyhedra_Library
  ::euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                              const Octagonal_Shape<U>& x,
                              const Octagonal_Shape<U>& y,
                              const Rounding_Dir dir,
                              Temp& tmp0, Temp& tmp1, Temp& tmp2);
  template <typename Temp, typename To, typename U>
  friend bool Parma_Polyhedra_Library
  ::l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                               const Octagonal_Shape<U>& x,
                               const Octagonal_Shape<U>& y,
                               const Rounding_Dir dir,
                               Temp& tmp0, Temp& tmp1, Temp& tmp2);

private:
  template <typename U> friend class Parma_Polyhedra_Library::Octagonal_Shape;
  template <typename Interval> friend class Parma_Polyhedra_Library::Box;

  //! The matrix that represents the octagonal shape.
  OR_Matrix<N> matrix;

  //! Dimension of the space of the octagonal shape.
  dimension_type space_dim;

  // Please, do not move the following include directive:
  // `Og_Status_idefs.hh' must be included exactly at this point.
  // And please do not remove the space separating `#' from `include':
  // this ensures that the directive will not be moved during the
  // procedure that automatically creates the library's include file
  // (see `Makefile.am' in the `src' directory).
#define PPL_IN_Octagonal_Shape_CLASS
/* Automatically generated from PPL source file ../src/Og_Status_idefs.hh line 1. */
/* Octagonal_Shape<T>::Status class declaration.
*/


#ifndef PPL_IN_Octagonal_Shape_CLASS
#error "Do not include Og_Status_idefs.hh directly; use Octagonal_Shape_defs.hh instead"
#endif

//! A conjunctive assertion about a Octagonal_Shape<T> object.
/*!
  The assertions supported are:
  - <EM>zero-dim universe</EM>: the polyhedron is the zero-dimensional
    vector space \f$\Rset^0 = \{\cdot\}\f$;
  - <EM>empty</EM>: the polyhedron is the empty set;
  - <EM>strongly closed</EM>: the Octagonal_Shape object is strongly
    closed, so that all the constraints are as tight as possible.

  Not all the conjunctions of these elementary assertions constitute
  a legal Status.  In fact:
  - <EM>zero-dim universe</EM> excludes any other assertion;
  - <EM>empty</EM>: excludes any other assertion.
*/
class Status {
public:
  //! By default Status is the <EM>zero-dim universe</EM> assertion.
  Status();

  //! \name Test, remove or add an individual assertion from the conjunction.
  //@{
  bool test_zero_dim_univ() const;
  void reset_zero_dim_univ();
  void set_zero_dim_univ();

  bool test_empty() const;
  void reset_empty();
  void set_empty();

  bool test_strongly_closed() const;
  void reset_strongly_closed();
  void set_strongly_closed();
  //@}

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  /*! \brief
    Writes to \p s an ASCII representation of the internal
    representation of \p *this.
  */
  void ascii_dump(std::ostream& s) const;

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

private:
  //! Status is implemented by means of a finite bitset.
  typedef unsigned int flags_t;

  //! \name Bitmasks for the individual assertions.
  //@{
  static const flags_t ZERO_DIM_UNIV   = 0U;
  static const flags_t EMPTY           = 1U << 0;
  static const flags_t STRONGLY_CLOSED = 1U << 1;
  //@}

  //! This holds the current bitset.
  flags_t flags;

  //! Construct from a bitmask.
  Status(flags_t mask);

  //! Check whether <EM>all</EM> bits in \p mask are set.
  bool test_all(flags_t mask) const;

  //! Check whether <EM>at least one</EM> bit in \p mask is set.
  bool test_any(flags_t mask) const;

  //! Set the bits in \p mask.
  void set(flags_t mask);

  //! Reset the bits in \p mask.
  void reset(flags_t mask);

};

/* Automatically generated from PPL source file ../src/Octagonal_Shape_defs.hh line 1929. */
#undef PPL_IN_Octagonal_Shape_CLASS

  //! The status flags to keep track of the internal state.
  Status status;

  //! Returns <CODE>true</CODE> if the OS is the zero-dimensional universe.
  bool marked_zero_dim_univ() const;

  //! Returns <CODE>true</CODE> if the OS is known to be empty.
  /*!
    The return value <CODE>false</CODE> does not necessarily
    implies that \p *this is non-empty.
  */
  bool marked_empty() const;

  /*! \brief
    Returns <CODE>true</CODE> if \c this->matrix is known to be
    strongly closed.

    The return value <CODE>false</CODE> does not necessarily
    implies that \c this->matrix is not strongly closed.
  */
  bool marked_strongly_closed() const;

  //! Turns \p *this into a zero-dimensional universe OS.
  void set_zero_dim_univ();

  //! Turns \p *this into an empty OS.
  void set_empty();

  //! Marks \p *this as strongly closed.
  void set_strongly_closed();

  //! Marks \p *this as possibly not strongly closed.
  void reset_strongly_closed();

  N& matrix_at(dimension_type i, dimension_type j);
  const N& matrix_at(dimension_type i, dimension_type j) const;

  /*! \brief
    Returns an upper bound for \p lf according to the constraints
    embedded in \p *this.

    \p lf must be a linear form on intervals with floating point coefficients.
    If all coefficients in \p lf are bounded, then \p result will become a
    correct overapproximation of the value of \p lf when variables in
    \p lf satisfy the constraints expressed by \p *this. Otherwise the
    behavior of the method is undefined.
  */
  template <typename Interval_Info>
  void linear_form_upper_bound(
                   const Linear_Form< Interval<T, Interval_Info> >& lf,
                   N& result) const;

  // FIXME: this function is currently not used. Consider removing it.
  static void interval_coefficient_upper_bound(const N& var_ub,
                                               const N& minus_var_ub,
                                               const N& int_ub, const N& int_lb,
                                               N& result);

  /*! \brief
    Uses the constraint \p c to refine \p *this.

    \param c
    The constraint to be added. Non-octagonal constraints are ignored.

    \warning
    If \p c and \p *this are dimension-incompatible,
    the behavior is undefined.
  */
  void refine_no_check(const Constraint& c);

  /*! \brief
    Uses the congruence \p cg to refine \p *this.

    \param cg
    The congruence to be added.
    Nontrivial proper congruences are ignored.
    Non-octagonal equalities are ignored.

    \warning
    If \p cg and \p *this are dimension-incompatible,
    the behavior is undefined.
  */
  void refine_no_check(const Congruence& cg);

  //! Adds the constraint <CODE>matrix[i][j] <= k</CODE>.
  void add_octagonal_constraint(dimension_type i,
                                dimension_type j,
                                const N& k);

  //! Adds the constraint <CODE>matrix[i][j] <= numer/denom</CODE>.
  void add_octagonal_constraint(dimension_type i,
                                dimension_type j,
                                Coefficient_traits::const_reference numer,
                                Coefficient_traits::const_reference denom);

  /*! \brief
    Adds to the Octagonal_Shape the constraint
    \f$\mathrm{var} \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$.

    Note that the coefficient of \p var in \p expr is null.
  */
  void refine(Variable var,
              Relation_Symbol relsym,
              const Linear_Expression& expr,
              Coefficient_traits::const_reference denominator
              = Coefficient_one());

  //! Removes all the constraints on variable \p v_id.
  void forget_all_octagonal_constraints(dimension_type v_id);

  //! Removes all binary constraints on variable \p v_id.
  void forget_binary_octagonal_constraints(dimension_type v_id);

  //! An helper function for the computation of affine relations.
  /*!
    For each variable index \c u_id (less than or equal to \p last_id
    and different from \p v_id), deduce constraints of the form
    <CODE>v - u \<= k</CODE> and <CODE>v + u \<= k</CODE>,
    starting from \p ub_v, which is an upper bound for \c v
    computed according to \p sc_expr and \p sc_denom.

    Strong-closure will be able to deduce the constraints
    <CODE>v - u \<= ub_v - lb_u</CODE> and <CODE>v + u \<= ub_v + ub_u</CODE>.
    We can be more precise if variable \c u played an active role in the
    computation of the upper bound for \c v.

    Namely, if the corresponding coefficient
    <CODE>q == sc_expr[u]/sc_denom</CODE> of \c u in \p sc_expr
    is greater than zero, we can improve the bound for <CODE>v - u</CODE>.
    In particular:
      - if <CODE>q \>= 1</CODE>, then <CODE>v - u \<= ub_v - ub_u</CODE>;
      - if <CODE>0 \< q \< 1</CODE>, then
        <CODE>v - u \<= ub_v - (q*ub_u + (1-q)*lb_u)</CODE>.

    Conversely, if \c q is less than zero, we can improve the bound for
    <CODE>v + u</CODE>. In particular:
      - if <CODE>q \<= -1</CODE>, then <CODE>v + u \<= ub_v + lb_u</CODE>;
      - if <CODE>-1 \< q \< 0</CODE>, then
        <CODE>v + u \<= ub_v + ((-q)*lb_u + (1+q)*ub_u)</CODE>.
  */
  void deduce_v_pm_u_bounds(dimension_type v_id,
                            dimension_type last_id,
                            const Linear_Expression& sc_expr,
                            Coefficient_traits::const_reference sc_denom,
                            const N& ub_v);

  //! An helper function for the computation of affine relations.
  /*!
    For each variable index \c u_id (less than or equal to \p last_id
    and different from \p v_id), deduce constraints of the form
    <CODE>-v + u \<= k</CODE> and <CODE>-v - u \<= k</CODE>,
    starting from \p minus_lb_v, which is the negation of a lower bound
    for \c v computed according to \p sc_expr and \p sc_denom.

    Strong-closure will be able to deduce the constraints
    <CODE>-v - u \<= -lb_v - lb_u</CODE> and
    <CODE>-v + u \<= -lb_v + ub_u</CODE>.
    We can be more precise if variable \c u played an active role in the
    computation of (the negation of) the lower bound for \c v.

    Namely, if the corresponding coefficient
    <CODE>q == sc_expr[u]/sc_denom</CODE> of \c u in \p sc_expr
    is greater than zero, we can improve the bound for <CODE>-v + u</CODE>.
    In particular:
      - if <CODE>q \>= 1</CODE>, then <CODE>-v + u \<= -lb_v + lb_u</CODE>;
      - if <CODE>0 \< q \< 1</CODE>, then
        <CODE>-v + u \<= -lb_v + (q*lb_u + (1-q)*ub_u)</CODE>.

    Conversely, if \c q is less than zero, we can improve the bound for
    <CODE>-v - u</CODE>. In particular:
      - if <CODE>q \<= -1</CODE>, then <CODE>-v - u \<= -lb_v - ub_u</CODE>;
      - if <CODE>-1 \< q \< 0</CODE>, then
        <CODE>-v - u \<= -lb_v - ((-q)*ub_u + (1+q)*lb_u)</CODE>.
  */
  void deduce_minus_v_pm_u_bounds(dimension_type v_id,
                                  dimension_type last_id,
                                  const Linear_Expression& sc_expr,
                                  Coefficient_traits::const_reference sc_denom,
                                  const N& minus_lb_v);

  /*! \brief
    Adds to \p limiting_octagon the octagonal differences in \p cs
    that are satisfied by \p *this.
  */
  void get_limiting_octagon(const Constraint_System& cs,
                            Octagonal_Shape& limiting_octagon) const;
  //! Compute the (zero-equivalence classes) successor relation.
  /*!
    It is assumed that the octagon is not empty and strongly closed.
  */
  void compute_successors(std::vector<dimension_type>& successor) const;

  //! Compute the leaders of zero-equivalence classes.
  /*!
    It is assumed that the OS is not empty and strongly closed.
  */
  void compute_leaders(std::vector<dimension_type>& successor,
                       std::vector<dimension_type>& no_sing_leaders,
                       bool& exist_sing_class,
                       dimension_type& sing_leader) const;

  //! Compute the leaders of zero-equivalence classes.
  /*!
    It is assumed that the OS is not empty and strongly closed.
  */
  void compute_leaders(std::vector<dimension_type>& leaders) const;

  /*! \brief
    Stores into \p non_redundant information about the matrix entries
    that are non-redundant (i.e., they will occur in the strongly
    reduced matrix).

    It is assumed that the OS is not empty and strongly closed;
    moreover, argument \p non_redundant is assumed to be empty.
  */
  void non_redundant_matrix_entries(std::vector<Bit_Row>& non_redundant) const;

  //! Removes the redundant constraints from \c this->matrix.
  void strong_reduction_assign() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \c this->matrix
    is strongly reduced.
  */
  bool is_strongly_reduced() const;

  /*! \brief
    Returns <CODE>true</CODE> if in the octagon taken two at a time
    unary constraints, there is also the constraint that represent their sum.
  */
  bool is_strong_coherent() const;

  bool tight_coherence_would_make_empty() const;

  //! Assigns to \c this->matrix its strong closure.
  /*!
    Strong closure is a necessary condition for the precision and/or
    the correctness of many methods. It explicitly records into \c matrix
    those constraints that are implicitly obtainable by the other ones,
    therefore obtaining a canonical representation for the OS.
  */
  void strong_closure_assign() const;

  //! Applies the strong-coherence step to \c this->matrix.
  void strong_coherence_assign();

  //! Assigns to \c this->matrix its tight closure.
  /*!
    \note
    This is \e not marked as a <code>const</code> method,
    as it may modify the rational-valued geometric shape by cutting away
    non-integral points. The method is only available if the template
    parameter \c T is bound to an integer data type.
  */
  void tight_closure_assign();

  /*! \brief
    Incrementally computes strong closure, assuming that only
    constraints affecting variable \p var need to be considered.

    \note
    It is assumed that \c *this, which was strongly closed, has only been
    modified by adding constraints affecting variable \p var. If this
    assumption is not satisfied, i.e., if a non-redundant constraint not
    affecting variable \p var has been added, the behavior is undefined.
    Worst-case complexity is \f$O(n^2)\f$.
  */
  void incremental_strong_closure_assign(Variable var) const;

  //! Checks if and how \p expr is bounded in \p *this.
  /*!
    Returns <CODE>true</CODE> if and only if \p from_above is
    <CODE>true</CODE> and \p expr is bounded from above in \p *this,
    or \p from_above is <CODE>false</CODE> and \p expr is bounded
    from below in \p *this.

    \param expr
    The linear expression to test;

    \param from_above
    <CODE>true</CODE> if and only if the boundedness of interest is
    "from above".

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds(const Linear_Expression& expr, bool from_above) const;

  //! Maximizes or minimizes \p expr subject to \p *this.
  /*!
    \param expr
    The linear expression to be maximized or minimized subject to \p
    *this;

    \param maximize
    <CODE>true</CODE> if maximization is what is wanted;

    \param ext_n
    The numerator of the extremum value;

    \param ext_d
    The denominator of the extremum value;

    \param included
    <CODE>true</CODE> if and only if the extremum of \p expr can
    actually be reached in \p * this;

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded in the appropriate
    direction, <CODE>false</CODE> is returned and \p ext_n, \p ext_d and
    \p included are left untouched.
  */
  bool max_min(const Linear_Expression& expr,
               bool maximize,
               Coefficient& ext_n, Coefficient& ext_d, bool& included) const;

  //! Maximizes or minimizes \p expr subject to \p *this.
  /*!
    \param expr
    The linear expression to be maximized or minimized subject to \p
    *this;

    \param maximize
    <CODE>true</CODE> if maximization is what is wanted;

    \param ext_n
    The numerator of the extremum value;

    \param ext_d
    The denominator of the extremum value;

    \param included
    <CODE>true</CODE> if and only if the extremum of \p expr can
    actually be reached in \p * this;

    \param g
    When maximization or minimization succeeds, will be assigned
    a point or closure point where \p expr reaches the
    corresponding extremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded in the appropriate
    direction, <CODE>false</CODE> is returned and \p ext_n, \p ext_d,
    \p included and \p g are left untouched.
  */
  bool max_min(const Linear_Expression& expr,
               bool maximize,
               Coefficient& ext_n, Coefficient& ext_d, bool& included,
               Generator& g) const;

  void drop_some_non_integer_points_helper(N& elem);

  friend std::ostream&
  Parma_Polyhedra_Library::IO_Operators
  ::operator<<<>(std::ostream& s, const Octagonal_Shape<T>& c);

  //! \name Exception Throwers
  //@{
  void throw_dimension_incompatible(const char* method,
                                    const Octagonal_Shape& y) const;

  void throw_dimension_incompatible(const char* method,
                                    dimension_type required_dim) const;

  void throw_dimension_incompatible(const char* method,
                                    const Constraint& c) const;

  void throw_dimension_incompatible(const char* method,
                                    const Congruence& cg) const;

  void throw_dimension_incompatible(const char* method,
                                    const Generator& g) const;

  void throw_dimension_incompatible(const char* method,
                                    const char* le_name,
                                    const Linear_Expression& le) const;

  template <typename C>
  void throw_dimension_incompatible(const char* method,
                                    const char* lf_name,
                                    const Linear_Form<C>& lf) const;

  static void throw_constraint_incompatible(const char* method);

  static void throw_expression_too_complex(const char* method,
                                           const Linear_Expression& le);

  static void throw_invalid_argument(const char* method, const char* reason);
  //@} // Exception Throwers
};

/* Automatically generated from PPL source file ../src/Og_Status_inlines.hh line 1. */
/* Octagonal_Shape<T>::Status class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

template <typename T>
inline
Octagonal_Shape<T>::Status::Status(flags_t mask)
  : flags(mask) {
}

template <typename T>
inline
Octagonal_Shape<T>::Status::Status()
  : flags(ZERO_DIM_UNIV) {
}

template <typename T>
inline bool
Octagonal_Shape<T>::Status::test_all(flags_t mask) const {
  return (flags & mask) == mask;
}

template <typename T>
inline bool
Octagonal_Shape<T>::Status::test_any(flags_t mask) const {
  return (flags & mask) != 0;
}

template <typename T>
inline void
Octagonal_Shape<T>::Status::set(flags_t mask) {
  flags |= mask;
}

template <typename T>
inline void
Octagonal_Shape<T>::Status::reset(flags_t mask) {
  flags &= ~mask;
}

template <typename T>
inline bool
Octagonal_Shape<T>::Status::test_zero_dim_univ() const {
  return flags == ZERO_DIM_UNIV;
}

template <typename T>
inline void
Octagonal_Shape<T>::Status::reset_zero_dim_univ() {
  // This is a no-op if the current status is not zero-dim.
  if (flags == ZERO_DIM_UNIV) {
    // In the zero-dim space, if it is not the universe it is empty.
    flags = EMPTY;
  }
}

template <typename T>
inline void
Octagonal_Shape<T>::Status::set_zero_dim_univ() {
  // Zero-dim universe is incompatible with anything else.
  flags = ZERO_DIM_UNIV;
}

template <typename T>
inline bool
Octagonal_Shape<T>::Status::test_empty() const {
  return test_any(EMPTY);
}

template <typename T>
inline void
Octagonal_Shape<T>::Status::reset_empty() {
  reset(EMPTY);
}

template <typename T>
inline void
Octagonal_Shape<T>::Status::set_empty() {
  flags = EMPTY;
}

template <typename T>
inline bool
Octagonal_Shape<T>::Status::test_strongly_closed() const {
  return test_any(STRONGLY_CLOSED);
}

template <typename T>
inline void
Octagonal_Shape<T>::Status::reset_strongly_closed() {
  reset(STRONGLY_CLOSED);
}

template <typename T>
inline void
Octagonal_Shape<T>::Status::set_strongly_closed() {
  set(STRONGLY_CLOSED);
}

template <typename T>
inline bool
Octagonal_Shape<T>::Status::OK() const {
  if (test_zero_dim_univ()) {
    // Zero-dim universe is OK.
    return true;
  }

  if (test_empty()) {
    Status copy = *this;
    copy.reset_empty();
    if (copy.test_zero_dim_univ()) {
      return true;
    }
    else {
#ifndef NDEBUG
      std::cerr << "The empty flag is incompatible with any other one."
                << std::endl;
#endif
      return false;
    }
  }

  // Any other case is OK.
  return true;
}


namespace Implementation {

namespace Octagonal_Shapes {
// These are the keywords that indicate the individual assertions.
extern const char* zero_dim_univ;
extern const char* empty;
extern const char* strong_closed;
const char yes = '+';
const char no = '-';
const char separator = ' ';

/*! \relates Parma_Polyhedra_Library::Octagonal_Shape::Status
  Reads a keyword and its associated on/off flag from \p s.
  Returns <CODE>true</CODE> if the operation is successful,
  returns <CODE>false</CODE> otherwise.
  When successful, \p positive is set to <CODE>true</CODE> if the flag
  is on; it is set to <CODE>false</CODE> otherwise.
*/
inline bool
get_field(std::istream& s, const char* keyword, bool& positive) {
  std::string str;
  if (!(s >> str)
      || (str[0] != yes && str[0] != no)
      || str.substr(1) != keyword) {
    return false;
  }
  positive = (str[0] == yes);
  return true;
}

} // namespace Octagonal_Shapes

} // namespace Implementation

template <typename T>
inline void
Octagonal_Shape<T>::Status::ascii_dump(std::ostream& s) const {
  using namespace Implementation::Octagonal_Shapes;
  s << (test_zero_dim_univ() ? yes : no) << zero_dim_univ
    << separator
    << (test_empty() ? yes : no) << empty
    << separator
    << separator
    << (test_strongly_closed() ? yes : no) << strong_closed
    << separator;
}

template <typename T>
inline bool
Octagonal_Shape<T>::Status::ascii_load(std::istream& s) {
  using namespace Implementation::Octagonal_Shapes;
  PPL_UNINITIALIZED(bool, positive);

  if (!get_field(s, zero_dim_univ, positive)) {
    return false;
  }
  if (positive) {
    set_zero_dim_univ();
  }

  if (!get_field(s, empty, positive)) {
    return false;
  }

  if (positive) {
    set_empty();
  }

  if (!get_field(s, strong_closed, positive)) {
    return false;
  }
  if (positive) {
    set_strongly_closed();
  }
  else {
    reset_strongly_closed();
  }
  // Check invariants.
  PPL_ASSERT(OK());
  return true;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Octagonal_Shape_inlines.hh line 1. */
/* Octagonal_Shape class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/wrap_assign.hh line 1. */
/* Generic implementation of the wrap_assign() function.
*/


/* Automatically generated from PPL source file ../src/wrap_assign.hh line 32. */

namespace Parma_Polyhedra_Library {

namespace Implementation {

struct Wrap_Dim_Translations {
  Variable var;
  Coefficient first_quadrant;
  Coefficient last_quadrant;
  Wrap_Dim_Translations(Variable v,
                        Coefficient_traits::const_reference f,
                        Coefficient_traits::const_reference l)
    : var(v), first_quadrant(f), last_quadrant(l) {
  }
};

typedef std::vector<Wrap_Dim_Translations> Wrap_Translations;

template <typename PSET>
void
wrap_assign_ind(PSET& pointset,
                Variables_Set& vars,
                Wrap_Translations::const_iterator first,
                Wrap_Translations::const_iterator end,
                Bounded_Integer_Type_Width w,
                Coefficient_traits::const_reference min_value,
                Coefficient_traits::const_reference max_value,
                const Constraint_System& cs,
                Coefficient& tmp1,
                Coefficient& tmp2) {
  const dimension_type space_dim = pointset.space_dimension();
  for (Wrap_Translations::const_iterator i = first; i != end; ++i) {
    const Wrap_Dim_Translations& wrap_dim_translations = *i;
    const Variable x(wrap_dim_translations.var);
    const Coefficient& first_quadrant = wrap_dim_translations.first_quadrant;
    const Coefficient& last_quadrant = wrap_dim_translations.last_quadrant;
    Coefficient& quadrant = tmp1;
    Coefficient& shift = tmp2;
    PSET hull(space_dim, EMPTY);
    for (quadrant = first_quadrant; quadrant <= last_quadrant; ++quadrant) {
      PSET p(pointset);
      if (quadrant != 0) {
        mul_2exp_assign(shift, quadrant, w);
        p.affine_image(x, x - shift, 1);
      }
      // `x' has just been wrapped.
      vars.erase(x.id());

      // Refine `p' with all the constraints in `cs' not depending
      // on variables in `vars'.
      if (vars.empty()) {
        p.refine_with_constraints(cs);
      }
      else {
        for (Constraint_System::const_iterator j = cs.begin(),
               cs_end = cs.end(); j != cs_end; ++j) {
          if (j->expression().all_zeroes(vars)) {
            // `*j' does not depend on variables in `vars'.
            p.refine_with_constraint(*j);
          }
        }
      }
      p.refine_with_constraint(min_value <= x);
      p.refine_with_constraint(x <= max_value);
      hull.upper_bound_assign(p);
    }
    pointset.m_swap(hull);
  }
}

template <typename PSET>
void
wrap_assign_col(PSET& dest,
                const PSET& src,
                const Variables_Set& vars,
                Wrap_Translations::const_iterator first,
                Wrap_Translations::const_iterator end,
                Bounded_Integer_Type_Width w,
                Coefficient_traits::const_reference min_value,
                Coefficient_traits::const_reference max_value,
                const Constraint_System* cs_p,
                Coefficient& tmp) {
  if (first == end) {
    PSET p(src);
    if (cs_p != 0) {
      p.refine_with_constraints(*cs_p);
    }
    for (Variables_Set::const_iterator i = vars.begin(),
           vars_end = vars.end(); i != vars_end; ++i) {
      const Variable x(*i);
      p.refine_with_constraint(min_value <= x);
      p.refine_with_constraint(x <= max_value);
    }
    dest.upper_bound_assign(p);
  }
  else {
    const Wrap_Dim_Translations& wrap_dim_translations = *first;
    const Variable x(wrap_dim_translations.var);
    const Coefficient& first_quadrant = wrap_dim_translations.first_quadrant;
    const Coefficient& last_quadrant = wrap_dim_translations.last_quadrant;
    Coefficient& shift = tmp;
    PPL_DIRTY_TEMP_COEFFICIENT(quadrant);
    for (quadrant = first_quadrant; quadrant <= last_quadrant; ++quadrant) {
      if (quadrant != 0) {
        mul_2exp_assign(shift, quadrant, w);
        PSET p(src);
        p.affine_image(x, x - shift, 1);
        wrap_assign_col(dest, p, vars, first+1, end, w, min_value, max_value,
                        cs_p, tmp);
      }
      else {
        wrap_assign_col(dest, src, vars, first+1, end, w, min_value, max_value,
                        cs_p, tmp);
      }
    }
  }
}

template <typename PSET>
void
wrap_assign(PSET& pointset,
            const Variables_Set& vars,
            const Bounded_Integer_Type_Width w,
            const Bounded_Integer_Type_Representation r,
            const Bounded_Integer_Type_Overflow o,
            const Constraint_System* cs_p,
            const unsigned complexity_threshold,
            const bool wrap_individually,
            const char* class_name) {
  // We must have cs_p->space_dimension() <= vars.space_dimension()
  //         and  vars.space_dimension() <= pointset.space_dimension().

  // Dimension-compatibility check of `*cs_p', if any.
  if (cs_p != 0) {
    const dimension_type vars_space_dim = vars.space_dimension();
    if (cs_p->space_dimension() > vars_space_dim) {
      std::ostringstream s;
      s << "PPL::" << class_name << "::wrap_assign(..., cs_p, ...):"
        << std::endl
        << "vars.space_dimension() == " << vars_space_dim
        << ", cs_p->space_dimension() == " << cs_p->space_dimension() << ".";
      throw std::invalid_argument(s.str());
    }

#ifndef NDEBUG
    // Check that all variables upon which `*cs_p' depends are in `vars'.
    // An assertion is violated otherwise.
    const Constraint_System cs = *cs_p;
    const dimension_type cs_space_dim = cs.space_dimension();
    Variables_Set::const_iterator vars_end = vars.end();
    for (Constraint_System::const_iterator i = cs.begin(),
           cs_end = cs.end(); i != cs_end; ++i) {
      const Constraint& c = *i;
      for (dimension_type d = cs_space_dim; d-- > 0; ) {
        PPL_ASSERT(c.coefficient(Variable(d)) == 0
                   || vars.find(d) != vars_end);
      }
    }
#endif
  }

  // Wrapping no variable only requires refining with *cs_p, if any.
  if (vars.empty()) {
    if (cs_p != 0) {
      pointset.refine_with_constraints(*cs_p);
    }
    return;
  }

  // Dimension-compatibility check of `vars'.
  const dimension_type space_dim = pointset.space_dimension();
  if (vars.space_dimension() > space_dim) {
    std::ostringstream s;
    s << "PPL::" << class_name << "::wrap_assign(vs, ...):" << std::endl
      << "this->space_dimension() == " << space_dim
      << ", required space dimension == " << vars.space_dimension() << ".";
    throw std::invalid_argument(s.str());
  }

  // Wrapping an empty polyhedron is a no-op.
  if (pointset.is_empty()) {
    return;
  }
  // Set `min_value' and `max_value' to the minimum and maximum values
  // a variable of width `w' and signedness `s' can take.
  PPL_DIRTY_TEMP_COEFFICIENT(min_value);
  PPL_DIRTY_TEMP_COEFFICIENT(max_value);
  if (r == UNSIGNED) {
    min_value = 0;
    mul_2exp_assign(max_value, Coefficient_one(), w);
    --max_value;
  }
  else {
    PPL_ASSERT(r == SIGNED_2_COMPLEMENT);
    mul_2exp_assign(max_value, Coefficient_one(), w-1);
    neg_assign(min_value, max_value);
    --max_value;
  }

  // If we are wrapping variables collectively, the ranges for the
  // required translations are saved in `translations' instead of being
  // immediately applied.
  Wrap_Translations translations;

  // Dimensions subject to translation are added to this set if we are
  // wrapping collectively or if `cs_p' is non null.
  Variables_Set dimensions_to_be_translated;

  // This will contain a lower bound to the number of abstractions
  // to be joined in order to obtain the collective wrapping result.
  // As soon as this exceeds `complexity_threshold', counting will be
  // interrupted and the full range will be the result of wrapping
  // any dimension that is not fully contained in quadrant 0.
  unsigned collective_wrap_complexity = 1;

  // This flag signals that the maximum complexity for collective
  // wrapping as been exceeded.
  bool collective_wrap_too_complex = false;

  if (!wrap_individually) {
    translations.reserve(space_dim);
  }

  // We use `full_range_bounds' to delay conversions whenever
  // this delay does not negatively affect precision.
  Constraint_System full_range_bounds;

  PPL_DIRTY_TEMP_COEFFICIENT(l_n);
  PPL_DIRTY_TEMP_COEFFICIENT(l_d);
  PPL_DIRTY_TEMP_COEFFICIENT(u_n);
  PPL_DIRTY_TEMP_COEFFICIENT(u_d);

  for (Variables_Set::const_iterator i = vars.begin(),
         vars_end = vars.end(); i != vars_end; ++i) {

    const Variable x(*i);

    bool extremum;

    if (!pointset.minimize(x, l_n, l_d, extremum)) {
    set_full_range:
      pointset.unconstrain(x);
      full_range_bounds.insert(min_value <= x);
      full_range_bounds.insert(x <= max_value);
      continue;
    }

    if (!pointset.maximize(x, u_n, u_d, extremum)) {
      goto set_full_range;
    }

    div_assign_r(l_n, l_n, l_d, ROUND_DOWN);
    div_assign_r(u_n, u_n, u_d, ROUND_DOWN);
    l_n -= min_value;
    u_n -= min_value;
    div_2exp_assign_r(l_n, l_n, w, ROUND_DOWN);
    div_2exp_assign_r(u_n, u_n, w, ROUND_DOWN);
    Coefficient& first_quadrant = l_n;
    const Coefficient& last_quadrant = u_n;

    // Special case: this variable does not need wrapping.
    if (first_quadrant == 0 && last_quadrant == 0) {
      continue;
    }

    // If overflow is impossible, try not to add useless constraints.
    if (o == OVERFLOW_IMPOSSIBLE) {
      if (first_quadrant < 0) {
        full_range_bounds.insert(min_value <= x);
      }
      if (last_quadrant > 0) {
        full_range_bounds.insert(x <= max_value);
      }
      continue;
    }

    if (o == OVERFLOW_UNDEFINED || collective_wrap_too_complex) {
      goto set_full_range;
    }

    Coefficient& quadrants = u_d;
    quadrants = last_quadrant - first_quadrant + 1;

    PPL_UNINITIALIZED(unsigned, extension);
    Result res = assign_r(extension, quadrants, ROUND_IGNORE);
    if (result_overflow(res) != 0 || extension > complexity_threshold) {
      goto set_full_range;
    }

    if (!wrap_individually && !collective_wrap_too_complex) {
      res = mul_assign_r(collective_wrap_complexity,
                         collective_wrap_complexity, extension, ROUND_IGNORE);
      if (result_overflow(res) != 0
          || collective_wrap_complexity > complexity_threshold) {
        collective_wrap_too_complex = true;
      }
      if (collective_wrap_too_complex) {
        // Set all the dimensions in `translations' to full range.
        for (Wrap_Translations::const_iterator j = translations.begin(),
               translations_end = translations.end();
             j != translations_end;
             ++j) {
          const Variable y(j->var);
          pointset.unconstrain(y);
          full_range_bounds.insert(min_value <= y);
          full_range_bounds.insert(y <= max_value);
        }
      }
    }

    if (wrap_individually && cs_p == 0) {
      Coefficient& quadrant = first_quadrant;
      // Temporary variable holding the shifts to be applied in order
      // to implement the translations.
      Coefficient& shift = l_d;
      PSET hull(space_dim, EMPTY);
      for ( ; quadrant <= last_quadrant; ++quadrant) {
        PSET p(pointset);
        if (quadrant != 0) {
          mul_2exp_assign(shift, quadrant, w);
          p.affine_image(x, x - shift, 1);
        }
        p.refine_with_constraint(min_value <= x);
        p.refine_with_constraint(x <= max_value);
        hull.upper_bound_assign(p);
      }
      pointset.m_swap(hull);
    }
    else if (wrap_individually || !collective_wrap_too_complex) {
      PPL_ASSERT(!wrap_individually || cs_p != 0);
      dimensions_to_be_translated.insert(x);
      translations
        .push_back(Wrap_Dim_Translations(x, first_quadrant, last_quadrant));
    }
  }

  if (!translations.empty()) {
    if (wrap_individually) {
      PPL_ASSERT(cs_p != 0);
      wrap_assign_ind(pointset, dimensions_to_be_translated,
                      translations.begin(), translations.end(),
                      w, min_value, max_value, *cs_p, l_n, l_d);
    }
    else {
      PSET hull(space_dim, EMPTY);
      wrap_assign_col(hull, pointset, dimensions_to_be_translated,
                      translations.begin(), translations.end(),
                      w, min_value, max_value, cs_p, l_n);
      pointset.m_swap(hull);
    }
  }

  if (cs_p != 0) {
    pointset.refine_with_constraints(*cs_p);
  }
  pointset.refine_with_constraints(full_range_bounds);
}

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Octagonal_Shape_inlines.hh line 36. */
#include <algorithm>

namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Octagonal_Shapes {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Returns the index coherent to \p i.
/*! \relates Parma_Polyhedra_Library::Octagonal_Shape */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
inline dimension_type
coherent_index(const dimension_type i) {
  return (i % 2 != 0) ? (i-1) : (i+1);
}

} // namespace Octagonal_Shapes

} // namespace Implementation

template <typename T>
inline dimension_type
Octagonal_Shape<T>::max_space_dimension() {
  return OR_Matrix<N>::max_num_rows()/2;
}

template <typename T>
inline bool
Octagonal_Shape<T>::marked_zero_dim_univ() const {
  return status.test_zero_dim_univ();
}

template <typename T>
inline bool
Octagonal_Shape<T>::marked_strongly_closed() const {
  return status.test_strongly_closed();
}

template <typename T>
inline bool
Octagonal_Shape<T>::marked_empty() const {
  return status.test_empty();
}

template <typename T>
inline void
Octagonal_Shape<T>::set_zero_dim_univ() {
  status.set_zero_dim_univ();
}

template <typename T>
inline void
Octagonal_Shape<T>::set_empty() {
  status.set_empty();
}

template <typename T>
inline void
Octagonal_Shape<T>::set_strongly_closed() {
  status.set_strongly_closed();
}

template <typename T>
inline void
Octagonal_Shape<T>::reset_strongly_closed() {
  status.reset_strongly_closed();
}

template <typename T>
inline
Octagonal_Shape<T>::Octagonal_Shape(const dimension_type num_dimensions,
                                    const Degenerate_Element kind)
  : matrix(num_dimensions), space_dim(num_dimensions), status() {
  if (kind == EMPTY) {
    set_empty();
  }
  else if (num_dimensions > 0) {
    // A (non zero-dim) universe octagon is strongly closed.
    set_strongly_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
inline
Octagonal_Shape<T>::Octagonal_Shape(const Octagonal_Shape& y, Complexity_Class)
  : matrix(y.matrix), space_dim(y.space_dim), status(y.status) {
}

template <typename T>
template <typename U>
inline
Octagonal_Shape<T>::Octagonal_Shape(const Octagonal_Shape<U>& y,
                                    Complexity_Class)
  // For maximum precision, enforce shortest-path closure
  // before copying the DB matrix.
  : matrix((y.strong_closure_assign(), y.matrix)),
    space_dim(y.space_dim),
    status() {
  // TODO: handle flags properly, possibly taking special cases into account.
  if (y.marked_empty()) {
    set_empty();
  }
  else if (y.marked_zero_dim_univ()) {
    set_zero_dim_univ();
  }
}

template <typename T>
inline
Octagonal_Shape<T>::Octagonal_Shape(const Constraint_System& cs)
  : matrix(cs.space_dimension()),
    space_dim(cs.space_dimension()),
    status() {
  if (cs.space_dimension() > 0) {
    // A (non zero-dim) universe octagon is strongly closed.
    set_strongly_closed();
  }
  add_constraints(cs);
}

template <typename T>
inline
Octagonal_Shape<T>::Octagonal_Shape(const Congruence_System& cgs)
  : matrix(cgs.space_dimension()),
    space_dim(cgs.space_dimension()),
    status() {
  if (cgs.space_dimension() > 0) {
    // A (non zero-dim) universe octagon is strongly closed.
    set_strongly_closed();
  }
  add_congruences(cgs);
}

template <typename T>
template <typename Interval>
inline
Octagonal_Shape<T>::Octagonal_Shape(const Box<Interval>& box,
                                    Complexity_Class)
  : matrix(box.space_dimension()),
    space_dim(box.space_dimension()),
    status() {
  // Check for emptiness for maximum precision.
  if (box.is_empty()) {
    set_empty();
  }
  else if (box.space_dimension() > 0) {
    // A (non zero-dim) universe OS is strongly closed.
    set_strongly_closed();
    refine_with_constraints(box.constraints());
  }
}

template <typename T>
inline
Octagonal_Shape<T>::Octagonal_Shape(const Grid& grid,
                                    Complexity_Class)
  : matrix(grid.space_dimension()),
    space_dim(grid.space_dimension()),
    status() {
  if (grid.space_dimension() > 0) {
    // A (non zero-dim) universe OS is strongly closed.
    set_strongly_closed();
  }
  // Taking minimized congruences ensures maximum precision.
  refine_with_congruences(grid.minimized_congruences());
}

template <typename T>
template <typename U>
inline
Octagonal_Shape<T>::Octagonal_Shape(const BD_Shape<U>& bd,
                                    Complexity_Class)
  : matrix(bd.space_dimension()),
    space_dim(bd.space_dimension()),
    status() {
  // Check for emptiness for maximum precision.
  if (bd.is_empty()) {
    set_empty();
  }
  else if (bd.space_dimension() > 0) {
    // A (non zero-dim) universe OS is strongly closed.
    set_strongly_closed();
    refine_with_constraints(bd.constraints());
  }
}

template <typename T>
inline Congruence_System
Octagonal_Shape<T>::congruences() const {
  return minimized_congruences();
}

template <typename T>
inline Octagonal_Shape<T>&
Octagonal_Shape<T>::operator=(const Octagonal_Shape& y) {
  matrix = y.matrix;
  space_dim = y.space_dim;
  status = y.status;
  return *this;
}

template <typename T>
inline
Octagonal_Shape<T>::~Octagonal_Shape() {
}

template <typename T>
inline void
Octagonal_Shape<T>::m_swap(Octagonal_Shape& y) {
  using std::swap;
  swap(matrix, y.matrix);
  swap(space_dim, y.space_dim);
  swap(status, y.status);
}

template <typename T>
inline dimension_type
Octagonal_Shape<T>::space_dimension() const {
  return space_dim;
}

template <typename T>
inline bool
Octagonal_Shape<T>::is_discrete() const {
  return affine_dimension() == 0;
}

template <typename T>
inline bool
Octagonal_Shape<T>::is_empty() const {
  strong_closure_assign();
  return marked_empty();
}

template <typename T>
inline bool
Octagonal_Shape<T>::bounds_from_above(const Linear_Expression& expr) const {
  return bounds(expr, true);
}

template <typename T>
inline bool
Octagonal_Shape<T>::bounds_from_below(const Linear_Expression& expr) const {
  return bounds(expr, false);
}

template <typename T>
inline bool
Octagonal_Shape<T>::maximize(const Linear_Expression& expr,
                             Coefficient& sup_n, Coefficient& sup_d,
                             bool& maximum) const {
  return max_min(expr, true, sup_n, sup_d, maximum);
}

template <typename T>
inline bool
Octagonal_Shape<T>::maximize(const Linear_Expression& expr,
                             Coefficient& sup_n, Coefficient& sup_d,
                             bool& maximum,
                             Generator& g) const {
  return max_min(expr, true, sup_n, sup_d, maximum, g);
}

template <typename T>
inline bool
Octagonal_Shape<T>::minimize(const Linear_Expression& expr,
                             Coefficient& inf_n, Coefficient& inf_d,
                             bool& minimum) const {
  return max_min(expr, false, inf_n, inf_d, minimum);
}

template <typename T>
inline bool
Octagonal_Shape<T>::minimize(const Linear_Expression& expr,
                             Coefficient& inf_n, Coefficient& inf_d,
                             bool& minimum,
                             Generator& g) const {
  return max_min(expr, false, inf_n, inf_d, minimum, g);
}

template <typename T>
inline bool
Octagonal_Shape<T>::is_topologically_closed() const {
  return true;
}

template <typename T>
inline void
Octagonal_Shape<T>::topological_closure_assign() {
}

/*! \relates Octagonal_Shape */
template <typename T>
inline bool
operator==(const Octagonal_Shape<T>& x, const Octagonal_Shape<T>& y) {
  if (x.space_dim != y.space_dim) {
    // Dimension-incompatible OSs are different.
    return false;
  }
  // Zero-dim OSs are equal if and only if they are both empty or universe.
  if (x.space_dim == 0) {
    if (x.marked_empty()) {
      return y.marked_empty();
    }
    else {
      return !y.marked_empty();
    }
  }

  x.strong_closure_assign();
  y.strong_closure_assign();
  // If one of two octagons is empty, then they are equal if and only if
  // the other octagon is empty too.
  if (x.marked_empty()) {
    return y.marked_empty();
  }
  if (y.marked_empty()) {
    return false;
  }
  // Strong closure is a canonical form.
  return x.matrix == y.matrix;
}

/*! \relates Octagonal_Shape */
template <typename T>
inline bool
operator!=(const Octagonal_Shape<T>& x, const Octagonal_Shape<T>& y) {
  return !(x == y);
}

template <typename T>
inline const typename Octagonal_Shape<T>::coefficient_type&
Octagonal_Shape<T>::matrix_at(const dimension_type i,
                              const dimension_type j) const {
  PPL_ASSERT(i < matrix.num_rows() && j < matrix.num_rows());
  using namespace Implementation::Octagonal_Shapes;
  return (j < matrix.row_size(i))
    ? matrix[i][j]
    : matrix[coherent_index(j)][coherent_index(i)];
}

template <typename T>
inline typename Octagonal_Shape<T>::coefficient_type&
Octagonal_Shape<T>::matrix_at(const dimension_type i,
                              const dimension_type j) {
  PPL_ASSERT(i < matrix.num_rows() && j < matrix.num_rows());
  using namespace Implementation::Octagonal_Shapes;
  return (j < matrix.row_size(i))
    ? matrix[i][j]
    : matrix[coherent_index(j)][coherent_index(i)];
}

template <typename T>
inline Constraint_System
Octagonal_Shape<T>::minimized_constraints() const {
  strong_reduction_assign();
  return constraints();
}

template <typename T>
inline void
Octagonal_Shape<T>::add_octagonal_constraint(const dimension_type i,
                                             const dimension_type j,
                                             const N& k) {
  // Private method: the caller has to ensure the following.
#ifndef NDEBUG
  PPL_ASSERT(i < 2*space_dim && j < 2*space_dim && i != j);
  typename OR_Matrix<N>::row_iterator m_i = matrix.row_begin() + i;
  PPL_ASSERT(j < m_i.row_size());
#endif
  N& r_i_j = matrix[i][j];
  if (r_i_j > k) {
    r_i_j = k;
    if (marked_strongly_closed()) {
      reset_strongly_closed();
    }
  }
}

template <typename T>
inline void
Octagonal_Shape<T>
::add_octagonal_constraint(const dimension_type i,
                           const dimension_type j,
                           Coefficient_traits::const_reference numer,
                           Coefficient_traits::const_reference denom) {
#ifndef NDEBUG
  // Private method: the caller has to ensure the following.
  PPL_ASSERT(i < 2*space_dim && j < 2*space_dim && i != j);
  typename OR_Matrix<N>::row_iterator m_i = matrix.row_begin() + i;
  PPL_ASSERT(j < m_i.row_size());
  PPL_ASSERT(denom != 0);
#endif
  PPL_DIRTY_TEMP(N, k);
  div_round_up(k, numer, denom);
  add_octagonal_constraint(i, j, k);
}

template <typename T>
inline void
Octagonal_Shape<T>::add_constraints(const Constraint_System& cs) {
  for (Constraint_System::const_iterator i = cs.begin(),
         i_end = cs.end(); i != i_end; ++i) {
    add_constraint(*i);
  }
}

template <typename T>
inline void
Octagonal_Shape<T>::add_recycled_constraints(Constraint_System& cs) {
  add_constraints(cs);
}

template <typename T>
inline void
Octagonal_Shape<T>::add_recycled_congruences(Congruence_System& cgs) {
  add_congruences(cgs);
}

template <typename T>
inline void
Octagonal_Shape<T>::add_congruences(const Congruence_System& cgs) {
  for (Congruence_System::const_iterator i = cgs.begin(),
         cgs_end = cgs.end(); i != cgs_end; ++i) {
    add_congruence(*i);
  }
}

template <typename T>
inline void
Octagonal_Shape<T>::refine_with_constraint(const Constraint& c) {
  // Dimension-compatibility check.
  if (c.space_dimension() > space_dimension()) {
    throw_dimension_incompatible("refine_with_constraint(c)", c);
  }

  if (!marked_empty()) {
    refine_no_check(c);
  }
}

template <typename T>
inline void
Octagonal_Shape<T>::refine_with_constraints(const Constraint_System& cs) {
  // Dimension-compatibility check.
  if (cs.space_dimension() > space_dimension()) {
    throw_invalid_argument("refine_with_constraints(cs)",
                           "cs and *this are space-dimension incompatible");
  }

  for (Constraint_System::const_iterator i = cs.begin(),
         cs_end = cs.end(); !marked_empty() && i != cs_end; ++i) {
    refine_no_check(*i);
  }
}

template <typename T>
inline void
Octagonal_Shape<T>::refine_with_congruence(const Congruence& cg) {
  const dimension_type cg_space_dim = cg.space_dimension();
  // Dimension-compatibility check.
  if (cg_space_dim > space_dimension()) {
    throw_dimension_incompatible("refine_with_congruence(cg)", cg);
  }
  if (!marked_empty()) {
    refine_no_check(cg);
  }
}

template <typename T>
void
Octagonal_Shape<T>::refine_with_congruences(const Congruence_System& cgs) {
  // Dimension-compatibility check.
  if (cgs.space_dimension() > space_dimension()) {
    throw_invalid_argument("refine_with_congruences(cgs)",
                           "cgs and *this are space-dimension incompatible");
  }

  for (Congruence_System::const_iterator i = cgs.begin(),
         cgs_end = cgs.end(); !marked_empty() && i != cgs_end; ++i) {
    refine_no_check(*i);
  }
}

template <typename T>
inline void
Octagonal_Shape<T>::refine_no_check(const Congruence& cg) {
  PPL_ASSERT(!marked_empty());
  PPL_ASSERT(cg.space_dimension() <= space_dimension());

  if (cg.is_proper_congruence()) {
    if (cg.is_inconsistent()) {
      set_empty();
    }
    // Other proper congruences are just ignored.
    return;
  }

  PPL_ASSERT(cg.is_equality());
  Constraint c(cg);
  refine_no_check(c);
}

template <typename T>
inline bool
Octagonal_Shape<T>::can_recycle_constraint_systems() {
  return false;
}

template <typename T>
inline bool
Octagonal_Shape<T>::can_recycle_congruence_systems() {
  return false;
}

template <typename T>
inline void
Octagonal_Shape<T>
::remove_higher_space_dimensions(const dimension_type new_dimension) {
  // Dimension-compatibility check.
  if (new_dimension > space_dim) {
    throw_dimension_incompatible("remove_higher_space_dimension(nd)",
                                 new_dimension);
  }
  // The removal of no dimensions from any octagon is a no-op.
  // Note that this case also captures the only legal removal of
  // dimensions from an octagon in a 0-dim space.
  if (new_dimension == space_dim) {
    PPL_ASSERT(OK());
    return;
  }

  strong_closure_assign();
  matrix.shrink(new_dimension);
  // When we remove all dimensions from a non-empty octagon,
  // we obtain the zero-dimensional universe octagon.
  if (new_dimension == 0 && !marked_empty()) {
    set_zero_dim_univ();
  }
  space_dim = new_dimension;
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::wrap_assign(const Variables_Set& vars,
                                Bounded_Integer_Type_Width w,
                                Bounded_Integer_Type_Representation r,
                                Bounded_Integer_Type_Overflow o,
                                const Constraint_System* cs_p,
                                unsigned complexity_threshold,
                                bool wrap_individually) {
  Implementation::wrap_assign(*this,
                              vars, w, r, o, cs_p,
                              complexity_threshold, wrap_individually,
                              "Octagonal_Shape");
}

template <typename T>
inline void
Octagonal_Shape<T>::widening_assign(const Octagonal_Shape& y, unsigned* tp) {
  BHMZ05_widening_assign(y, tp);
}

template <typename T>
inline void
Octagonal_Shape<T>::CC76_extrapolation_assign(const Octagonal_Shape& y,
                                              unsigned* tp) {
  static N stop_points[] = {
    N(-2, ROUND_UP),
    N(-1, ROUND_UP),
    N( 0, ROUND_UP),
    N( 1, ROUND_UP),
    N( 2, ROUND_UP)
  };
  CC76_extrapolation_assign(y,
                            stop_points,
                            stop_points
                            + sizeof(stop_points)/sizeof(stop_points[0]),
                            tp);
}

template <typename T>
inline void
Octagonal_Shape<T>::time_elapse_assign(const Octagonal_Shape& y) {
  // Dimension-compatibility check.
  if (space_dimension() != y.space_dimension()) {
    throw_dimension_incompatible("time_elapse_assign(y)", y);
  }
  // Compute time-elapse on polyhedra.
  // TODO: provide a direct implementation.
  C_Polyhedron ph_x(constraints());
  C_Polyhedron ph_y(y.constraints());
  ph_x.time_elapse_assign(ph_y);
  Octagonal_Shape<T> x(ph_x);
  m_swap(x);
  PPL_ASSERT(OK());
}

template <typename T>
inline bool
Octagonal_Shape<T>::strictly_contains(const Octagonal_Shape& y) const {
  const Octagonal_Shape<T>& x = *this;
  return x.contains(y) && !y.contains(x);
}

template <typename T>
template <typename Interval_Info>
inline void
Octagonal_Shape<T>::generalized_refine_with_linear_form_inequality(
                    const Linear_Form< Interval<T, Interval_Info> >& left,
                    const Linear_Form< Interval<T, Interval_Info> >& right,
                    const Relation_Symbol relsym) {
  switch (relsym) {
  case EQUAL:
    // TODO: see if we can handle this case more efficiently.
    refine_with_linear_form_inequality(left, right);
    refine_with_linear_form_inequality(right, left);
    break;
  case LESS_THAN:
  case LESS_OR_EQUAL:
    refine_with_linear_form_inequality(left, right);
    break;
  case GREATER_THAN:
  case GREATER_OR_EQUAL:
    refine_with_linear_form_inequality(right, left);
    break;
  case NOT_EQUAL:
    break;
  default:
    PPL_UNREACHABLE;
    break;
  }
}

template <typename T>
template <typename Interval_Info>
inline void
Octagonal_Shape<T>::
refine_fp_interval_abstract_store(
          Box< Interval<T, Interval_Info> >& store) const {

  // Check that T is a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<T>::is_exact,
                     "Octagonal_Shape<T>::refine_fp_interval_abstract_store:"
                     " T not a floating point type.");

  typedef Interval<T, Interval_Info> FP_Interval_Type;
  store.intersection_assign(Box<FP_Interval_Type>(*this));

}

/*! \relates Octagonal_Shape */
template <typename Temp, typename To, typename T>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Octagonal_Shape<T>& x,
                            const Octagonal_Shape<T>& y,
                            const Rounding_Dir dir,
                            Temp& tmp0,
                            Temp& tmp1,
                            Temp& tmp2) {
  // Dimension-compatibility check.
  if (x.space_dim != y.space_dim) {
    return false;
  }
  // Zero-dim OSs are equal if and only if they are both empty or universe.
  if (x.space_dim == 0) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  // The distance computation requires strong closure.
  x.strong_closure_assign();
  y.strong_closure_assign();

  // If one of two OSs is empty, then they are equal if and only if
  // the other OS is empty too.
  if (x.marked_empty() ||  y.marked_empty()) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  return rectilinear_distance_assign(r, x.matrix, y.matrix, dir,
                                     tmp0, tmp1, tmp2);
}

/*! \relates Octagonal_Shape */
template <typename Temp, typename To, typename T>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Octagonal_Shape<T>& x,
                            const Octagonal_Shape<T>& y,
                            const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return rectilinear_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Octagonal_Shape */
template <typename To, typename T>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const Octagonal_Shape<T>& x,
                            const Octagonal_Shape<T>& y,
                            const Rounding_Dir dir) {
  return rectilinear_distance_assign<To, To, T>(r, x, y, dir);
}

/*! \relates Octagonal_Shape */
template <typename Temp, typename To, typename T>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Octagonal_Shape<T>& x,
                          const Octagonal_Shape<T>& y,
                          const Rounding_Dir dir,
                          Temp& tmp0,
                          Temp& tmp1,
                          Temp& tmp2) {
  // Dimension-compatibility check.
  if (x.space_dim != y.space_dim) {
    return false;
  }

  // Zero-dim OSs are equal if and only if they are both empty or universe.
  if (x.space_dim == 0) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  // The distance computation requires strong closure.
  x.strong_closure_assign();
  y.strong_closure_assign();

  // If one of two OSs is empty, then they are equal if and only if
  // the other OS is empty too.
  if (x.marked_empty() ||  y.marked_empty()) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  return euclidean_distance_assign(r, x.matrix, y.matrix, dir,
                                   tmp0, tmp1, tmp2);
}

/*! \relates Octagonal_Shape */
template <typename Temp, typename To, typename T>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Octagonal_Shape<T>& x,
                          const Octagonal_Shape<T>& y,
                          const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return euclidean_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Octagonal_Shape */
template <typename To, typename T>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const Octagonal_Shape<T>& x,
                          const Octagonal_Shape<T>& y,
                          const Rounding_Dir dir) {
  return euclidean_distance_assign<To, To, T>(r, x, y, dir);
}

/*! \relates Octagonal_Shape */
template <typename Temp, typename To, typename T>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Octagonal_Shape<T>& x,
                           const Octagonal_Shape<T>& y,
                           const Rounding_Dir dir,
                           Temp& tmp0,
                           Temp& tmp1,
                           Temp& tmp2) {
  // Dimension-compatibility check.
  if (x.space_dim != y.space_dim) {
    return false;
  }
  // Zero-dim OSs are equal if and only if they are both empty or universe.
  if (x.space_dim == 0) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  // The distance computation requires strong closure.
  x.strong_closure_assign();
  y.strong_closure_assign();

  // If one of two OSs is empty, then they are equal if and only if
  // the other OS is empty too.
  if (x.marked_empty() ||  y.marked_empty()) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  return l_infinity_distance_assign(r, x.matrix, y.matrix, dir,
                                    tmp0, tmp1, tmp2);
}

/*! \relates Octagonal_Shape */
template <typename Temp, typename To, typename T>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Octagonal_Shape<T>& x,
                           const Octagonal_Shape<T>& y,
                           const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return l_infinity_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates Octagonal_Shape */
template <typename To, typename T>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const Octagonal_Shape<T>& x,
                           const Octagonal_Shape<T>& y,
                           const Rounding_Dir dir) {
  return l_infinity_distance_assign<To, To, T>(r, x, y, dir);
}

template <typename T>
inline memory_size_type
Octagonal_Shape<T>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename T>
inline int32_t
Octagonal_Shape<T>::hash_code() const {
  return hash_code_from_dimension(space_dimension());
}

template <typename T>
inline void
Octagonal_Shape<T>::drop_some_non_integer_points_helper(N& elem) {
  if (!is_integer(elem)) {
#ifndef NDEBUG
    Result r =
#endif
    floor_assign_r(elem, elem, ROUND_DOWN);
    PPL_ASSERT(r == V_EQ);
    reset_strongly_closed();
  }
}

/*! \relates Octagonal_Shape */
template <typename T>
inline void
swap(Octagonal_Shape<T>& x, Octagonal_Shape<T>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Octagonal_Shape_templates.hh line 1. */
/* Octagonal_Shape class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/Octagonal_Shape_templates.hh line 35. */
#include <vector>
#include <deque>
#include <string>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <algorithm>

namespace Parma_Polyhedra_Library {

template <typename T>
Octagonal_Shape<T>::Octagonal_Shape(const Polyhedron& ph,
                                    const Complexity_Class complexity)
  : matrix(0), space_dim(0), status() {
  const dimension_type num_dimensions = ph.space_dimension();

  if (ph.marked_empty()) {
    *this = Octagonal_Shape(num_dimensions, EMPTY);
    return;
  }

  if (num_dimensions == 0) {
    *this = Octagonal_Shape(num_dimensions, UNIVERSE);
    return;
  }

  // Build from generators when we do not care about complexity
  // or when the process has polynomial complexity.
  if (complexity == ANY_COMPLEXITY
      || (!ph.has_pending_constraints() && ph.generators_are_up_to_date())) {
    *this = Octagonal_Shape(ph.generators());
    return;
  }

  // We cannot afford exponential complexity, we do not have a complete set
  // of generators for the polyhedron, and the polyhedron is not trivially
  // empty or zero-dimensional.  Constraints, however, are up to date.
  PPL_ASSERT(ph.constraints_are_up_to_date());

  if (!ph.has_something_pending() && ph.constraints_are_minimized()) {
    // If the constraint system of the polyhedron is minimized,
    // the test `is_universe()' has polynomial complexity.
    if (ph.is_universe()) {
      *this = Octagonal_Shape(num_dimensions, UNIVERSE);
      return;
    }
  }

  // See if there is at least one inconsistent constraint in `ph.con_sys'.
  for (Constraint_System::const_iterator i = ph.con_sys.begin(),
         cs_end = ph.con_sys.end(); i != cs_end; ++i) {
    if (i->is_inconsistent()) {
      *this = Octagonal_Shape(num_dimensions, EMPTY);
      return;
    }
  }

  // If `complexity' allows it, use simplex to derive the exact (modulo
  // the fact that our OSs are topologically closed) variable bounds.
  if (complexity == SIMPLEX_COMPLEXITY) {
    MIP_Problem lp(num_dimensions);
    lp.set_optimization_mode(MAXIMIZATION);

    const Constraint_System& ph_cs = ph.constraints();
    if (!ph_cs.has_strict_inequalities()) {
      lp.add_constraints(ph_cs);
    }
    else {
      // Adding to `lp' a topologically closed version of `ph_cs'.
      for (Constraint_System::const_iterator i = ph_cs.begin(),
             ph_cs_end = ph_cs.end(); i != ph_cs_end; ++i) {
        const Constraint& c = *i;
        if (c.is_strict_inequality()) {
          Linear_Expression expr(c.expression());
          lp.add_constraint(expr >= 0);
        }
        else {
          lp.add_constraint(c);
        }
      }
    }

    // Check for unsatisfiability.
    if (!lp.is_satisfiable()) {
      *this = Octagonal_Shape<T>(num_dimensions, EMPTY);
      return;
    }

    // Start with a universe OS that will be refined by the simplex.
    *this = Octagonal_Shape<T>(num_dimensions, UNIVERSE);
    // Get all the upper bounds.
    Generator g(point());
    PPL_DIRTY_TEMP_COEFFICIENT(numer);
    PPL_DIRTY_TEMP_COEFFICIENT(denom);
    for (dimension_type i = 0; i < num_dimensions; ++i) {
      Variable x(i);
      // Evaluate optimal upper bound for `x <= ub'.
      lp.set_objective_function(x);
      if (lp.solve() == OPTIMIZED_MIP_PROBLEM) {
        g = lp.optimizing_point();
        lp.evaluate_objective_function(g, numer, denom);
        numer *= 2;
        div_round_up(matrix[2*i + 1][2*i], numer, denom);
      }
      // Evaluate optimal upper bounds for `x + y <= ub'.
      for (dimension_type j = 0; j < i; ++j) {
        Variable y(j);
        lp.set_objective_function(x + y);
        if (lp.solve() == OPTIMIZED_MIP_PROBLEM) {
          g = lp.optimizing_point();
          lp.evaluate_objective_function(g, numer, denom);
          div_round_up(matrix[2*i + 1][2*j], numer, denom);
        }
      }
      // Evaluate optimal upper bound for `x - y <= ub'.
      for (dimension_type j = 0; j < num_dimensions; ++j) {
        if (i == j) {
          continue;
        }
        Variable y(j);
        lp.set_objective_function(x - y);
        if (lp.solve() == OPTIMIZED_MIP_PROBLEM) {
          g = lp.optimizing_point();
          lp.evaluate_objective_function(g, numer, denom);
          div_round_up(((i < j) ?
                        matrix[2*j][2*i]
                        : matrix[2*i + 1][2*j + 1]),
                       numer, denom);
        }
      }
      // Evaluate optimal upper bound for `y - x <= ub'.
      for (dimension_type j = 0; j < num_dimensions; ++j) {
        if (i == j) {
          continue;
        }
        Variable y(j);
        lp.set_objective_function(x - y);
        if (lp.solve() == OPTIMIZED_MIP_PROBLEM) {
          g = lp.optimizing_point();
          lp.evaluate_objective_function(g, numer, denom);
          div_round_up(((i < j)
                        ? matrix[2*j][2*i]
                        : matrix[2*i + 1][2*j + 1]),
                       numer, denom);
        }
      }
      // Evaluate optimal upper bound for `-x - y <= ub'.
      for (dimension_type j = 0; j < i; ++j) {
        Variable y(j);
        lp.set_objective_function(-x - y);
        if (lp.solve() == OPTIMIZED_MIP_PROBLEM) {
          g = lp.optimizing_point();
          lp.evaluate_objective_function(g, numer, denom);
          div_round_up(matrix[2*i][2*j + 1], numer, denom);
        }
      }
      // Evaluate optimal upper bound for `-x <= ub'.
      lp.set_objective_function(-x);
      if (lp.solve() == OPTIMIZED_MIP_PROBLEM) {
        g = lp.optimizing_point();
        lp.evaluate_objective_function(g, numer, denom);
        numer *= 2;
        div_round_up(matrix[2*i][2*i + 1], numer, denom);
      }
    }
    set_strongly_closed();
    PPL_ASSERT(OK());
    return;
  }

  // Extract easy-to-find bounds from constraints.
  PPL_ASSERT(complexity == POLYNOMIAL_COMPLEXITY);
  *this = Octagonal_Shape(num_dimensions, UNIVERSE);
  refine_with_constraints(ph.constraints());
}

template <typename T>
Octagonal_Shape<T>::Octagonal_Shape(const Generator_System& gs)
  : matrix(gs.space_dimension()),
    space_dim(gs.space_dimension()),
    status() {
  const Generator_System::const_iterator gs_begin = gs.begin();
  const Generator_System::const_iterator gs_end = gs.end();
  if (gs_begin == gs_end) {
    // An empty generator system defines the empty polyhedron.
    set_empty();
    return;
  }

  typedef typename OR_Matrix<N>::row_reference_type row_reference;
  typename OR_Matrix<N>::row_iterator mat_begin = matrix.row_begin();

  PPL_DIRTY_TEMP(N, tmp);
  bool mat_initialized = false;
  bool point_seen = false;
  // Going through all the points and closure points.
  for (Generator_System::const_iterator k = gs_begin; k != gs_end; ++k) {
    const Generator& g = *k;
    switch (g.type()) {
    case Generator::POINT:
      point_seen = true;
      // Intentionally fall through.
    case Generator::CLOSURE_POINT:
      if (!mat_initialized) {
        // When handling the first (closure) point, we initialize the matrix.
        mat_initialized = true;
        const Coefficient& d = g.divisor();
        // TODO: This can be optimized more, if needed, exploiting the
        // (possible) sparseness of g. Also consider if OR_Matrix should be
        // sparse, too.
        for (dimension_type i = 0; i < space_dim; ++i) {
          const Coefficient& g_i = g.coefficient(Variable(i));
          const dimension_type di = 2*i;
          row_reference x_i = *(mat_begin + di);
          row_reference x_ii = *(mat_begin + (di + 1));
          for (dimension_type j = 0; j < i; ++j) {
            const Coefficient& g_j = g.coefficient(Variable(j));
            const dimension_type dj = 2*j;
            // Set for any point the hyperplanes passing in the point
            // and having the octagonal gradient.
            // Let be P = [P_1, P_2, ..., P_n] point.
            // Hyperplanes: X_i - X_j = P_i - P_j.
            div_round_up(x_i[dj], g_j - g_i, d);
            div_round_up(x_ii[dj + 1], g_i - g_j, d);
            // Hyperplanes: X_i + X_j = P_i + P_j.
            div_round_up(x_i[dj + 1], -g_j - g_i, d);
            div_round_up(x_ii[dj], g_i + g_j, d);
          }
          // Hyperplanes: X_i = P_i.
          div_round_up(x_i[di + 1], -g_i - g_i, d);
          div_round_up(x_ii[di], g_i + g_i, d);
        }
      }
      else {
        // This is not the first point: the matrix already contains
        // valid values and we must compute maxima.
        const Coefficient& d = g.divisor();
        // TODO: This can be optimized more, if needed, exploiting the
        // (possible) sparseness of g. Also consider if OR_Matrix should be
        // sparse, too.
        for (dimension_type i = 0; i < space_dim; ++i) {
          const Coefficient& g_i = g.coefficient(Variable(i));
          const dimension_type di = 2*i;
          row_reference x_i = *(mat_begin + di);
          row_reference x_ii = *(mat_begin + (di + 1));
          for (dimension_type j = 0; j < i; ++j) {
            const Coefficient& g_j = g.coefficient(Variable(j));
            const dimension_type dj = 2*j;
            // Set for any point the straight lines passing in the point
            // and having the octagonal gradient; compute maxima values.
            // Let be P = [P_1, P_2, ..., P_n] point.
            // Hyperplane: X_i - X_j = max (P_i - P_j, const).
            div_round_up(tmp, g_j - g_i, d);
            max_assign(x_i[dj], tmp);
            div_round_up(tmp, g_i - g_j, d);
            max_assign(x_ii[dj + 1], tmp);
            // Hyperplane: X_i + X_j = max (P_i + P_j, const).
            div_round_up(tmp, -g_j - g_i, d);
            max_assign(x_i[dj + 1], tmp);
            div_round_up(tmp, g_i + g_j, d);
            max_assign(x_ii[dj], tmp);
          }
          // Hyperplane: X_i = max (P_i, const).
          div_round_up(tmp, -g_i - g_i, d);
          max_assign(x_i[di + 1], tmp);
          div_round_up(tmp, g_i + g_i, d);
          max_assign(x_ii[di], tmp);
        }
      }
      break;
    default:
      // Lines and rays temporarily ignored.
      break;
    }
  }

  if (!point_seen) {
    // The generator system is not empty, but contains no points.
    throw_invalid_argument("Octagonal_Shape(gs)",
                           "the non-empty generator system gs "
                           "contains no points.");
  }

  // Going through all the lines and rays.
  for (Generator_System::const_iterator k = gs_begin; k != gs_end; ++k) {
    const Generator& g = *k;
    switch (g.type()) {
    case Generator::LINE:
        // TODO: This can be optimized more, if needed, exploiting the
        // (possible) sparseness of g. Also consider if OR_Matrix should be
        // sparse, too.
        for (dimension_type i = 0; i < space_dim; ++i) {
          const Coefficient& g_i = g.coefficient(Variable(i));
          const dimension_type di = 2*i;
          row_reference x_i = *(mat_begin + di);
          row_reference x_ii = *(mat_begin + (di + 1));
          for (dimension_type j = 0; j < i; ++j) {
            const Coefficient& g_j = g.coefficient(Variable(j));
            const dimension_type dj = 2*j;
            // Set for any line the right limit.
            if (g_i != g_j) {
              // Hyperplane: X_i - X_j <=/>= +Inf.
              assign_r(x_i[dj], PLUS_INFINITY, ROUND_NOT_NEEDED);
              assign_r(x_ii[dj + 1], PLUS_INFINITY, ROUND_NOT_NEEDED);
            }
            if (g_i != -g_j) {
              // Hyperplane: X_i + X_j <=/>= +Inf.
              assign_r(x_i[dj + 1], PLUS_INFINITY, ROUND_NOT_NEEDED);
              assign_r(x_ii[dj], PLUS_INFINITY, ROUND_NOT_NEEDED);
            }
          }
          if (g_i != 0) {
            // Hyperplane: X_i <=/>= +Inf.
            assign_r(x_i[di + 1], PLUS_INFINITY, ROUND_NOT_NEEDED);
            assign_r(x_ii[di], PLUS_INFINITY, ROUND_NOT_NEEDED);
          }
        }
      break;
    case Generator::RAY:
        // TODO: This can be optimized more, if needed, exploiting the
        // (possible) sparseness of g. Also consider if OR_Matrix should be
        // sparse, too.
        for (dimension_type i = 0; i < space_dim; ++i) {
          const Coefficient& g_i = g.coefficient(Variable(i));
          const dimension_type di = 2*i;
          row_reference x_i = *(mat_begin + di);
          row_reference x_ii = *(mat_begin + (di + 1));
          for (dimension_type j = 0; j < i; ++j) {
            const Coefficient& g_j = g.coefficient(Variable(j));
            const dimension_type dj = 2*j;
            // Set for any ray the right limit in the case
            // of the binary constraints.
            if (g_i < g_j) {
              // Hyperplane: X_i - X_j >= +Inf.
              assign_r(x_i[dj], PLUS_INFINITY, ROUND_NOT_NEEDED);
            }
            if (g_i > g_j) {
              // Hyperplane: X_i - X_j <= +Inf.
              assign_r(x_ii[dj + 1], PLUS_INFINITY, ROUND_NOT_NEEDED);
            }
            if (g_i < -g_j) {
              // Hyperplane: X_i + X_j >= +Inf.
              assign_r(x_i[dj + 1], PLUS_INFINITY, ROUND_NOT_NEEDED);
            }
            if (g_i > -g_j) {
              // Hyperplane: X_i + X_j <= +Inf.
              assign_r(x_ii[dj], PLUS_INFINITY, ROUND_NOT_NEEDED);
            }
          }
          // Case: unary constraints.
          if (g_i < 0) {
            // Hyperplane: X_i  = +Inf.
            assign_r(x_i[di + 1], PLUS_INFINITY, ROUND_NOT_NEEDED);
          }
          if (g_i > 0) {
            // Hyperplane: X_i  = +Inf.
            assign_r(x_ii[di], PLUS_INFINITY, ROUND_NOT_NEEDED);
          }
        }
      break;
    default:
      // Points and closure points already dealt with.
      break;
    }
  }
  set_strongly_closed();
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::add_constraint(const Constraint& c) {
  const dimension_type c_space_dim = c.space_dimension();
  // Dimension-compatibility check.
  if (c_space_dim > space_dim) {
    throw_dimension_incompatible("add_constraint(c)", c);
  }
  // Get rid of strict inequalities.
  if (c.is_strict_inequality()) {
    if (c.is_inconsistent()) {
      set_empty();
      return;
    }
    if (c.is_tautological()) {
      return;
    }
    // Nontrivial strict inequalities are not allowed.
    throw_invalid_argument("add_constraint(c)",
                           "strict inequalities are not allowed");
  }

  dimension_type num_vars = 0;
  dimension_type i = 0;
  dimension_type j = 0;
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  PPL_DIRTY_TEMP_COEFFICIENT(term);
  // Constraints that are not octagonal differences are not allowed.
  if (!Octagonal_Shape_Helper
    ::extract_octagonal_difference(c, c_space_dim, num_vars,
                                   i, j, coeff, term)) {
    throw_invalid_argument("add_constraint(c)",
                           "c is not an octagonal constraint");
  }

  if (num_vars == 0) {
    // Dealing with a trivial constraint (not a strict inequality).
    if (c.inhomogeneous_term() < 0
        || (c.is_equality() && c.inhomogeneous_term() != 0)) {
      set_empty();
    }
    return;
  }

  // Select the cell to be modified for the "<=" part of constraint.
  typename OR_Matrix<N>::row_iterator i_iter = matrix.row_begin() + i;
  typename OR_Matrix<N>::row_reference_type m_i = *i_iter;
  N& m_i_j = m_i[j];
  // Set `coeff' to the absolute value of itself.
  if (coeff < 0) {
    neg_assign(coeff);
  }

  bool is_oct_changed = false;
  // Compute the bound for `m_i_j', rounding towards plus infinity.
  PPL_DIRTY_TEMP(N, d);
  div_round_up(d, term, coeff);
  if (m_i_j > d) {
    m_i_j = d;
    is_oct_changed = true;
  }

  if (c.is_equality()) {
    // Select the cell to be modified for the ">=" part of constraint.
    if (i % 2 == 0) {
      ++i_iter;
    }
    else {
      --i_iter;
    }

    typename OR_Matrix<N>::row_reference_type m_ci = *i_iter;
    using namespace Implementation::Octagonal_Shapes;
    dimension_type cj = coherent_index(j);
    N& m_ci_cj = m_ci[cj];
    // Also compute the bound for `m_ci_cj', rounding towards plus infinity.
    neg_assign(term);
    div_round_up(d, term, coeff);
    if (m_ci_cj > d) {
      m_ci_cj = d;
      is_oct_changed = true;
    }
  }

  // This method does not preserve closure.
  if (is_oct_changed && marked_strongly_closed()) {
    reset_strongly_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::add_congruence(const Congruence& cg) {
  const dimension_type cg_space_dim = cg.space_dimension();
  // Dimension-compatibility check:
  // the dimension of `cg' can not be greater than space_dim.
  if (space_dimension() < cg_space_dim) {
    throw_dimension_incompatible("add_congruence(cg)", cg);
  }

  // Handle the case of proper congruences first.
  if (cg.is_proper_congruence()) {
    if (cg.is_tautological()) {
      return;
    }
    if (cg.is_inconsistent()) {
      set_empty();
      return;
    }
    // Non-trivial and proper congruences are not allowed.
    throw_invalid_argument("add_congruence(cg)",
                           "cg is a non-trivial, proper congruence");
  }

  PPL_ASSERT(cg.is_equality());
  Constraint c(cg);
  add_constraint(c);
}

template <typename T>
template <typename Interval_Info>
void
Octagonal_Shape<T>::refine_with_linear_form_inequality(
                    const Linear_Form< Interval<T, Interval_Info> >& left,
                    const Linear_Form< Interval<T, Interval_Info> >& right) {

  // Check that T is a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<T>::is_exact,
                     "Octagonal_Shape<T>::refine_with_linear_form_inequality:"
                     " T not a floating point type.");

  // We assume that the analyzer will not try to apply an unreachable filter.
  PPL_ASSERT(!marked_empty());

  // Dimension-compatibility checks.
  // The dimensions of `left' and `right' should not be greater than the
  // dimension of `*this'.
  const dimension_type left_space_dim = left.space_dimension();
  if (space_dim < left_space_dim) {
    throw_dimension_incompatible(
          "refine_with_linear_form_inequality(left, right)", "left", left);
  }

  const dimension_type right_space_dim = right.space_dimension();
  if (space_dim < right_space_dim) {
    throw_dimension_incompatible(
          "refine_with_linear_form_inequality(left, right)", "right", right);
  }
  // Number of non-zero coefficients in `left': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type left_t = 0;
  // Variable-index of the last non-zero coefficient in `left', if any.
  dimension_type left_w_id = 0;
  // Number of non-zero coefficients in `right': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type right_t = 0;
  // Variable-index of the last non-zero coefficient in `right', if any.
  dimension_type right_w_id = 0;

  // Get information about the number of non-zero coefficients in `left'.
  for (dimension_type i = left_space_dim; i-- > 0; ) {
    if (left.coefficient(Variable(i)) != 0) {
      if (left_t++ == 1) {
        break;
      }
      else {
        left_w_id = i;
      }
    }
  }

  // Get information about the number of non-zero coefficients in `right'.
  for (dimension_type i = right_space_dim; i-- > 0; ) {
    if (right.coefficient(Variable(i)) != 0) {
      if (right_t++ == 1) {
        break;
      }
      else {
        right_w_id = i;
      }
    }
  }

  typedef typename OR_Matrix<N>::row_iterator row_iterator;
  typedef typename OR_Matrix<N>::row_reference_type row_reference;
  typedef Interval<T, Interval_Info> FP_Interval_Type;

  // FIXME: there is plenty of duplicate code in the following lines. We could
  // shorten it at the expense of a bit of efficiency.

  if (left_t == 0) {
    if (right_t == 0) {
      // The constraint involves constants only. Ignore it: it is up to
      // the analyzer to handle it.
      PPL_ASSERT(OK());
      return;
    }

    if (right_t == 1) {
      // The constraint has the form [a-, a+] <= [b-, b+] + [c-, c+] * x.
      // Reduce it to the constraint +/-x <= b+ - a- if [c-, c+] = +/-[1, 1].
      const FP_Interval_Type& right_w_coeff =
                              right.coefficient(Variable(right_w_id));
      if (right_w_coeff == 1) {
        const dimension_type n_right = right_w_id * 2;
        PPL_DIRTY_TEMP(N, b_plus_minus_a_minus);
        const FP_Interval_Type& left_a = left.inhomogeneous_term();
        const FP_Interval_Type& right_b = right.inhomogeneous_term();
        sub_assign_r(b_plus_minus_a_minus, right_b.upper(), left_a.lower(),
                     ROUND_UP);
        mul_2exp_assign_r(b_plus_minus_a_minus, b_plus_minus_a_minus, 1,
                          ROUND_UP);
        add_octagonal_constraint(n_right, n_right + 1, b_plus_minus_a_minus);
        PPL_ASSERT(OK());
        return;
      }

      if (right_w_coeff == -1) {
        const dimension_type n_right = right_w_id * 2;
        PPL_DIRTY_TEMP(N, b_plus_minus_a_minus);
        const FP_Interval_Type& left_a = left.inhomogeneous_term();
        const FP_Interval_Type& right_b = right.inhomogeneous_term();
        sub_assign_r(b_plus_minus_a_minus, right_b.upper(), left_a.lower(),
                     ROUND_UP);
        mul_2exp_assign_r(b_plus_minus_a_minus, b_plus_minus_a_minus, 1,
                          ROUND_UP);
        add_octagonal_constraint(n_right + 1, n_right, b_plus_minus_a_minus);
        PPL_ASSERT(OK());
        return;
      }
    }
  }
  else if (left_t == 1) {
    if (right_t == 0) {
      // The constraint has the form [b-, b+] + [c-, c+] * x <= [a-, a+]
      // Reduce it to the constraint +/-x <= a+ - b- if [c-, c+] = +/-[1, 1].
      const FP_Interval_Type& left_w_coeff =
                              left.coefficient(Variable(left_w_id));
      if (left_w_coeff == 1) {
        const dimension_type n_left = left_w_id * 2;
        PPL_DIRTY_TEMP(N, a_plus_minus_b_minus);
        const FP_Interval_Type& left_b = left.inhomogeneous_term();
        const FP_Interval_Type& right_a = right.inhomogeneous_term();
        sub_assign_r(a_plus_minus_b_minus, right_a.upper(), left_b.lower(),
                     ROUND_UP);
        mul_2exp_assign_r(a_plus_minus_b_minus, a_plus_minus_b_minus, 1,
                          ROUND_UP);
        add_octagonal_constraint(n_left + 1, n_left, a_plus_minus_b_minus);
        PPL_ASSERT(OK());
        return;
      }

      if (left_w_coeff == -1) {
        const dimension_type n_left = left_w_id * 2;
        PPL_DIRTY_TEMP(N, a_plus_minus_b_minus);
        const FP_Interval_Type& left_b = left.inhomogeneous_term();
        const FP_Interval_Type& right_a = right.inhomogeneous_term();
        sub_assign_r(a_plus_minus_b_minus, right_a.upper(), left_b.lower(),
                     ROUND_UP);
        mul_2exp_assign_r(a_plus_minus_b_minus, a_plus_minus_b_minus, 1,
                          ROUND_UP);
        add_octagonal_constraint(n_left, n_left + 1, a_plus_minus_b_minus);
        PPL_ASSERT(OK());
        return;
      }
    }

    if (right_t == 1) {
      // The constraint has the form
      // [a-, a+] + [b-, b+] * x <= [c-, c+] + [d-, d+] * y.
      // Reduce it to the constraint +/-x +/-y <= c+ - a-
      // if [b-, b+] = +/-[1, 1] and [d-, d+] = +/-[1, 1].
      const FP_Interval_Type& left_w_coeff =
                              left.coefficient(Variable(left_w_id));
      const FP_Interval_Type& right_w_coeff =
                              right.coefficient(Variable(right_w_id));
      bool is_left_coeff_one = (left_w_coeff == 1);
      bool is_left_coeff_minus_one = (left_w_coeff == -1);
      bool is_right_coeff_one = (right_w_coeff == 1);
      bool is_right_coeff_minus_one = (right_w_coeff == -1);
      if (left_w_id == right_w_id) {
        if ((is_left_coeff_one && is_right_coeff_one)
            || (is_left_coeff_minus_one && is_right_coeff_minus_one)) {
          // Here we have an identity or a constants-only constraint.
          PPL_ASSERT(OK());
          return;
        }
        if (is_left_coeff_one && is_right_coeff_minus_one) {
          // We fall back to a previous case
          // (but we do not need to multiply the result by two).
          const dimension_type n_left = left_w_id * 2;
          PPL_DIRTY_TEMP(N, a_plus_minus_b_minus);
          const FP_Interval_Type& left_b = left.inhomogeneous_term();
          const FP_Interval_Type& right_a = right.inhomogeneous_term();
          sub_assign_r(a_plus_minus_b_minus, right_a.upper(), left_b.lower(),
                       ROUND_UP);
          add_octagonal_constraint(n_left + 1, n_left, a_plus_minus_b_minus);
          PPL_ASSERT(OK());
          return;
        }
        if (is_left_coeff_minus_one && is_right_coeff_one) {
          // We fall back to a previous case
          // (but we do not need to multiply the result by two).
          const dimension_type n_left = left_w_id * 2;
          PPL_DIRTY_TEMP(N, a_plus_minus_b_minus);
          const FP_Interval_Type& left_b = left.inhomogeneous_term();
          const FP_Interval_Type& right_a = right.inhomogeneous_term();
          sub_assign_r(a_plus_minus_b_minus, right_a.upper(), left_b.lower(),
                       ROUND_UP);
          add_octagonal_constraint(n_left, n_left + 1, a_plus_minus_b_minus);
          PPL_ASSERT(OK());
          return;
        }
      }
      else if (is_left_coeff_one && is_right_coeff_one) {
        const dimension_type n_left = left_w_id * 2;
        const dimension_type n_right = right_w_id * 2;
        PPL_DIRTY_TEMP(N, c_plus_minus_a_minus);
        const FP_Interval_Type& left_a = left.inhomogeneous_term();
        const FP_Interval_Type& right_c = right.inhomogeneous_term();
        sub_assign_r(c_plus_minus_a_minus, right_c.upper(), left_a.lower(),
                     ROUND_UP);
        if (left_w_id < right_w_id) {
          add_octagonal_constraint(n_right, n_left, c_plus_minus_a_minus);
        }
        else {
          add_octagonal_constraint(n_left + 1, n_right + 1,
                                   c_plus_minus_a_minus);
        }
        PPL_ASSERT(OK());
        return;
      }
      if (is_left_coeff_one && is_right_coeff_minus_one) {
        const dimension_type n_left = left_w_id * 2;
        const dimension_type n_right = right_w_id * 2;
        PPL_DIRTY_TEMP(N, c_plus_minus_a_minus);
        const FP_Interval_Type& left_a = left.inhomogeneous_term();
        const FP_Interval_Type& right_c = right.inhomogeneous_term();
        sub_assign_r(c_plus_minus_a_minus, right_c.upper(), left_a.lower(),
                     ROUND_UP);
        if (left_w_id < right_w_id) {
          add_octagonal_constraint(n_right + 1, n_left, c_plus_minus_a_minus);
        }
        else {
          add_octagonal_constraint(n_left + 1, n_right, c_plus_minus_a_minus);
        }
        PPL_ASSERT(OK());
        return;
      }
      if (is_left_coeff_minus_one && is_right_coeff_one) {
        const dimension_type n_left = left_w_id * 2;
        const dimension_type n_right = right_w_id * 2;
        PPL_DIRTY_TEMP(N, c_plus_minus_a_minus);
        const FP_Interval_Type& left_a = left.inhomogeneous_term();
        const FP_Interval_Type& right_c = right.inhomogeneous_term();
        sub_assign_r(c_plus_minus_a_minus, right_c.upper(), left_a.lower(),
                     ROUND_UP);
        if (left_w_id < right_w_id) {
          add_octagonal_constraint(n_right, n_left + 1, c_plus_minus_a_minus);
        }
        else {
          add_octagonal_constraint(n_left, n_right + 1, c_plus_minus_a_minus);
        }
        PPL_ASSERT(OK());
        return;
      }
      if (is_left_coeff_minus_one && is_right_coeff_minus_one) {
        const dimension_type n_left = left_w_id * 2;
        const dimension_type n_right = right_w_id * 2;
        PPL_DIRTY_TEMP(N, c_plus_minus_a_minus);
        const FP_Interval_Type& left_a = left.inhomogeneous_term();
        const FP_Interval_Type& right_c = right.inhomogeneous_term();
        sub_assign_r(c_plus_minus_a_minus, right_c.upper(), left_a.lower(),
                     ROUND_UP);
        if (left_w_id < right_w_id) {
          add_octagonal_constraint(n_right + 1, n_left + 1,
                                   c_plus_minus_a_minus);
        }
        else {
          add_octagonal_constraint(n_left, n_right, c_plus_minus_a_minus);
        }
        PPL_ASSERT(OK());
        return;
      }
    }
  }

  // General case.

  // FIRST, update the binary constraints for each pair of DIFFERENT variables
  // in `left' and `right'.

  // Declare temporaries outside of the loop.
  PPL_DIRTY_TEMP(N, low_coeff);
  PPL_DIRTY_TEMP(N, high_coeff);
  PPL_DIRTY_TEMP(N, upper_bound);

  Linear_Form<FP_Interval_Type> right_minus_left(right);
  right_minus_left -= left;

  dimension_type max_w_id = std::max(left_w_id, right_w_id);
  for (dimension_type first_v = 0; first_v < max_w_id; ++first_v) {
    for (dimension_type second_v = first_v + 1;
         second_v <= max_w_id; ++second_v) {
      const FP_Interval_Type& lfv_coefficient =
                        left.coefficient(Variable(first_v));
      const FP_Interval_Type& lsv_coefficient =
                        left.coefficient(Variable(second_v));
      const FP_Interval_Type& rfv_coefficient =
                        right.coefficient(Variable(first_v));
      const FP_Interval_Type& rsv_coefficient =
                        right.coefficient(Variable(second_v));
      // We update the constraints only when both variables appear in at
      // least one argument.
      bool do_update = false;
      assign_r(low_coeff, lfv_coefficient.lower(), ROUND_NOT_NEEDED);
      assign_r(high_coeff, lfv_coefficient.upper(), ROUND_NOT_NEEDED);
      if (low_coeff != 0 || high_coeff != 0) {
        assign_r(low_coeff, lsv_coefficient.lower(), ROUND_NOT_NEEDED);
        assign_r(high_coeff, lsv_coefficient.upper(), ROUND_NOT_NEEDED);
        if (low_coeff != 0 || high_coeff != 0) {
          do_update = true;
        }
        else {
          assign_r(low_coeff, rsv_coefficient.lower(), ROUND_NOT_NEEDED);
          assign_r(high_coeff, rsv_coefficient.upper(), ROUND_NOT_NEEDED);
          if (low_coeff != 0 || high_coeff != 0) {
            do_update = true;
          }
        }
      }
      else {
        assign_r(low_coeff, rfv_coefficient.lower(), ROUND_NOT_NEEDED);
        assign_r(high_coeff, rfv_coefficient.upper(), ROUND_NOT_NEEDED);
        if (low_coeff != 0 || high_coeff != 0) {
          assign_r(low_coeff, lsv_coefficient.lower(), ROUND_NOT_NEEDED);
          assign_r(high_coeff, lsv_coefficient.upper(), ROUND_NOT_NEEDED);
          if (low_coeff != 0 || high_coeff != 0) {
            do_update = true;
          }
          else {
            assign_r(low_coeff, rsv_coefficient.lower(), ROUND_NOT_NEEDED);
            assign_r(high_coeff, rsv_coefficient.upper(), ROUND_NOT_NEEDED);
            if (low_coeff != 0 || high_coeff != 0) {
              do_update = true;
            }
          }
        }
      }

      if (do_update) {
        Variable first(first_v);
        Variable second(second_v);
        dimension_type n_first_var = first_v * 2;
        dimension_type n_second_var = second_v * 2;
        linear_form_upper_bound(right_minus_left - first + second,
                                upper_bound);
        add_octagonal_constraint(n_second_var + 1, n_first_var + 1,
                                 upper_bound);
        linear_form_upper_bound(right_minus_left + first + second,
                                upper_bound);
        add_octagonal_constraint(n_second_var + 1, n_first_var, upper_bound);
        linear_form_upper_bound(right_minus_left - first - second,
                                upper_bound);
        add_octagonal_constraint(n_second_var, n_first_var + 1, upper_bound);
        linear_form_upper_bound(right_minus_left + first - second,
                                upper_bound);
        add_octagonal_constraint(n_second_var, n_first_var, upper_bound);
      }
    }
  }

  // Finally, update the unary constraints.
  for (dimension_type v = 0; v <= max_w_id; ++v) {
    const FP_Interval_Type& lv_coefficient =
                        left.coefficient(Variable(v));
    const FP_Interval_Type& rv_coefficient =
                        right.coefficient(Variable(v));
    // We update the constraints only if v appears in at least one of the
    // two arguments.
    bool do_update = false;
    assign_r(low_coeff, lv_coefficient.lower(), ROUND_NOT_NEEDED);
    assign_r(high_coeff, lv_coefficient.upper(), ROUND_NOT_NEEDED);
    if (low_coeff != 0 || high_coeff != 0) {
      do_update = true;
    }
    else {
      assign_r(low_coeff, rv_coefficient.lower(), ROUND_NOT_NEEDED);
      assign_r(high_coeff, rv_coefficient.upper(), ROUND_NOT_NEEDED);
      if (low_coeff != 0 || high_coeff != 0) {
        do_update = true;
      }
    }

    if (do_update) {
      Variable var(v);
      dimension_type n_var = 2 * v;
      /*
        VERY DIRTY trick: since we need to keep the old unary constraints
        while computing the new ones, we momentarily keep the new coefficients
        in the main diagonal of the matrix. They will be moved later.
      */
      linear_form_upper_bound(right_minus_left + var, upper_bound);
      mul_2exp_assign_r(matrix[n_var + 1][n_var + 1], upper_bound, 1,
                        ROUND_UP);
      linear_form_upper_bound(right_minus_left - var, upper_bound);
      mul_2exp_assign_r(matrix[n_var][n_var], upper_bound, 1,
                        ROUND_UP);
    }
  }

  /*
    Now move the newly computed coefficients from the main diagonal to
    their proper place, and restore +infinity on the diagonal.
  */
  row_iterator m_ite = matrix.row_begin();
  row_iterator m_end = matrix.row_end();
  for (dimension_type i = 0; m_ite != m_end; i += 2) {
    row_reference upper = *m_ite;
    N& ul = upper[i];
    add_octagonal_constraint(i, i + 1, ul);
    assign_r(ul, PLUS_INFINITY, ROUND_NOT_NEEDED);
    ++m_ite;
    row_reference lower = *m_ite;
    N& lr = lower[i + 1];
    add_octagonal_constraint(i + 1, i, lr);
    assign_r(lr, PLUS_INFINITY, ROUND_NOT_NEEDED);
    ++m_ite;
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::refine_no_check(const Constraint& c) {
  PPL_ASSERT(!marked_empty());
  const dimension_type c_space_dim = c.space_dimension();
  PPL_ASSERT(c_space_dim <= space_dim);

  dimension_type num_vars = 0;
  dimension_type i = 0;
  dimension_type j = 0;
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  PPL_DIRTY_TEMP_COEFFICIENT(term);
  // Constraints that are not octagonal differences are ignored.
  if (!Octagonal_Shape_Helper
    ::extract_octagonal_difference(c, c_space_dim, num_vars,
                                   i, j, coeff, term)) {
    return;
  }

  if (num_vars == 0) {
    const Coefficient& c_inhomo = c.inhomogeneous_term();
    // Dealing with a trivial constraint (maybe a strict inequality).
    if (c_inhomo < 0
        || (c_inhomo != 0 && c.is_equality())
        || (c_inhomo == 0 && c.is_strict_inequality())) {
      set_empty();
    }
    return;
  }

  // Select the cell to be modified for the "<=" part of constraint.
  typename OR_Matrix<N>::row_iterator i_iter = matrix.row_begin() + i;
  typename OR_Matrix<N>::row_reference_type m_i = *i_iter;
  N& m_i_j = m_i[j];
  // Set `coeff' to the absolute value of itself.
  if (coeff < 0) {
    neg_assign(coeff);
  }

  bool is_oct_changed = false;
  // Compute the bound for `m_i_j', rounding towards plus infinity.
  PPL_DIRTY_TEMP(N, d);
  div_round_up(d, term, coeff);
  if (m_i_j > d) {
    m_i_j = d;
    is_oct_changed = true;
  }

  if (c.is_equality()) {
    // Select the cell to be modified for the ">=" part of constraint.
    if (i % 2 == 0) {
      ++i_iter;
    }
    else {
      --i_iter;
    }

    typename OR_Matrix<N>::row_reference_type m_ci = *i_iter;
    using namespace Implementation::Octagonal_Shapes;
    dimension_type cj = coherent_index(j);
    N& m_ci_cj = m_ci[cj];
    // Also compute the bound for `m_ci_cj', rounding towards plus infinity.
    neg_assign(term);
    div_round_up(d, term, coeff);
    if (m_ci_cj > d) {
      m_ci_cj = d;
      is_oct_changed = true;
    }
  }

  // This method does not preserve closure.
  if (is_oct_changed && marked_strongly_closed()) {
    reset_strongly_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
dimension_type
Octagonal_Shape<T>::affine_dimension() const {
  const dimension_type n_rows = matrix.num_rows();
  // A zero-space-dim shape always has affine dimension zero.
  if (n_rows == 0) {
    return 0;
  }

  // Strong closure is necessary to detect emptiness
  // and all (possibly implicit) equalities.
  strong_closure_assign();
  if (marked_empty()) {
    return 0;
  }

  // The vector `leaders' is used to represent non-singular
  // equivalence classes:
  // `leaders[i] == i' if and only if `i' is the leader of its
  // equivalence class (i.e., the minimum index in the class).
  std::vector<dimension_type> leaders;
  compute_leaders(leaders);

  // Due to the splitting of variables, the affine dimension is the
  // number of non-singular positive zero-equivalence classes.
  dimension_type affine_dim = 0;
  for (dimension_type i = 0; i < n_rows; i += 2) {
    // Note: disregard the singular equivalence class.
    if (leaders[i] == i && leaders[i + 1] == i + 1) {
      ++affine_dim;
    }
  }

  return affine_dim;
}

template <typename T>
Congruence_System
Octagonal_Shape<T>::minimized_congruences() const {
  // Strong closure is necessary to detect emptiness
  // and all (possibly implicit) equalities.
  strong_closure_assign();
  Congruence_System cgs(space_dim);

  if (space_dim == 0) {
    if (marked_empty()) {
      cgs = Congruence_System::zero_dim_empty();
    }
    return cgs;
  }

  if (marked_empty()) {
    cgs.insert(Congruence::zero_dim_false());
    return cgs;
  }

  // The vector `leaders' is used to represent equivalence classes:
  // `leaders[i] == i' if and only if `i' is the leader of its
  // equivalence class (i.e., the minimum index in the class).
  std::vector<dimension_type> leaders;
  compute_leaders(leaders);

  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);
  for (dimension_type i = 0, i_end = 2*space_dim; i != i_end; i += 2) {
    const dimension_type lead_i = leaders[i];
    if (i == lead_i) {
      if (leaders[i + 1] == i) {
        // `i' is the leader of the singular equivalence class.
        goto singular;
      }
      else {
        // `i' is the leader of a non-singular equivalence class.
        continue;
      }
    }
    else {
      // `i' is not a leader.
      if (leaders[i + 1] == lead_i) {
        // `i' belongs to the singular equivalence class.
        goto singular;
      }
      else {
        // `i' does not belong to the singular equivalence class.
        goto non_singular;
      }
    }

  singular:
    // `i' belongs to the singular equivalence class:
    // we have a unary equality constraint.
    {
      const Variable x(i/2);
      const N& c_ii_i = matrix[i + 1][i];
#ifndef NDEBUG
      const N& c_i_ii = matrix[i][i + 1];
      PPL_ASSERT(is_additive_inverse(c_i_ii, c_ii_i));
#endif
      numer_denom(c_ii_i, numer, denom);
      denom *= 2;
      cgs.insert(denom*x == numer);
    }
    continue;

  non_singular:
    // `i' does not belong to the singular equivalence class.
    // we have a binary equality constraint.
    {
      const N& c_i_li = matrix[i][lead_i];
#ifndef NDEBUG
      using namespace Implementation::Octagonal_Shapes;
      const N& c_ii_lii = matrix[i + 1][coherent_index(lead_i)];
      PPL_ASSERT(is_additive_inverse(c_ii_lii, c_i_li));
#endif
      const Variable x(lead_i/2);
      const Variable y(i/2);
      numer_denom(c_i_li, numer, denom);
      if (lead_i % 2 == 0) {
        cgs.insert(denom*x - denom*y == numer);
      }
      else {
        cgs.insert(denom*x + denom*y + numer == 0);
      }
    }
    continue;
  }
  return cgs;
}

template <typename T>
void
Octagonal_Shape<T>::concatenate_assign(const Octagonal_Shape& y) {
  // If `y' is an empty 0-dim space octagon, let `*this' become empty.
  // If `y' is an universal 0-dim space octagon, we simply return.
  if (y.space_dim == 0) {
    if (y.marked_empty()) {
      set_empty();
    }
    return;
  }

  // If `*this' is an empty 0-dim space octagon, then it is sufficient
  // to adjust the dimension of the vector space.
  if (space_dim == 0 && marked_empty()) {
    add_space_dimensions_and_embed(y.space_dim);
    return;
  }

  // This is the old number of rows in the matrix. It is equal to
  // the first index of columns to change.
  dimension_type old_num_rows = matrix.num_rows();
  // First we increase the space dimension of `*this' by adding
  // `y.space_dimension()' new dimensions.
  // The matrix for the new octagon is obtained
  // by leaving the old system of constraints in the upper left-hand side
  // (where they are at the present) and placing the constraints of `y' in the
  // lower right-hand side.
  add_space_dimensions_and_embed(y.space_dim);
  typename OR_Matrix<N>::const_element_iterator
    y_it = y.matrix.element_begin();
  for (typename OR_Matrix<N>::row_iterator
         i = matrix.row_begin() + old_num_rows,
         matrix_row_end = matrix.row_end(); i != matrix_row_end; ++i) {
    typename OR_Matrix<N>::row_reference_type r = *i;
    dimension_type rs_i = i.row_size();
    for (dimension_type j = old_num_rows; j < rs_i; ++j, ++y_it) {
      r[j] = *y_it;
    }
  }

  // The concatenation does not preserve the closure.
  if (marked_strongly_closed()) {
    reset_strongly_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
bool
Octagonal_Shape<T>::contains(const Octagonal_Shape& y) const {
  // Dimension-compatibility check.
  if (space_dim != y.space_dim) {
    throw_dimension_incompatible("contains(y)", y);
  }

  if (space_dim == 0) {
    // The zero-dimensional empty octagon only contains another
    // zero-dimensional empty octagon.
    // The zero-dimensional universe octagon contains any other
    // zero-dimensional octagon.
    return marked_empty() ? y.marked_empty() : true;
  }

  // `y' needs to be transitively closed.
  y.strong_closure_assign();
  // An empty octagon is in any other dimension-compatible octagons.
  if (y.marked_empty()) {
    return true;
  }

  // If `*this' is empty it can not contain `y' (which is not empty).
  if (is_empty()) {
    return false;
  }

  // `*this' contains `y' if and only if every element of `*this'
  // is greater than or equal to the correspondent one of `y'.
  for (typename OR_Matrix<N>::const_element_iterator
         i = matrix.element_begin(), j = y.matrix.element_begin(),
         matrix_element_end = matrix.element_end();
       i != matrix_element_end; ++i, ++j) {
    if (*i < *j) {
      return false;
    }
  }
  return true;
}

template <typename T>
bool
Octagonal_Shape<T>::is_disjoint_from(const Octagonal_Shape& y) const {
  // Dimension-compatibility check.
  if (space_dim != y.space_dim) {
    throw_dimension_incompatible("is_disjoint_from(y)", y);
  }

  // If one Octagonal_Shape is empty, the Octagonal_Shapes are disjoint.
  strong_closure_assign();
  if (marked_empty()) {
    return true;
  }
  y.strong_closure_assign();
  if (y.marked_empty()) {
    return true;
  }

  // Two Octagonal_Shapes are disjoint if and only if their
  // intersection is empty, i.e., if and only if there exists a
  // variable such that the upper bound of the constraint on that
  // variable in the first Octagonal_Shape is strictly less than the
  // lower bound of the corresponding constraint in the second
  // Octagonal_Shape or vice versa.

  const dimension_type n_rows = matrix.num_rows();

  typedef typename OR_Matrix<N>::const_row_iterator row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type row_reference;

  const row_iterator m_begin = matrix.row_begin();
  const row_iterator m_end = matrix.row_end();

  const row_iterator y_begin = y.matrix.row_begin();

  PPL_DIRTY_TEMP(N, neg_y_ci_cj);
  for (row_iterator i_iter = m_begin; i_iter != m_end; ++i_iter) {
    using namespace Implementation::Octagonal_Shapes;
    const dimension_type i = i_iter.index();
    const dimension_type ci = coherent_index(i);
    const dimension_type rs_i = i_iter.row_size();
    row_reference m_i = *i_iter;
    for (dimension_type j = 0; j < n_rows; ++j) {
      const dimension_type cj = coherent_index(j);
      row_reference m_cj = *(m_begin + cj);
      const N& m_i_j = (j < rs_i) ? m_i[j] : m_cj[ci];
      row_reference y_ci = *(y_begin + ci);
      row_reference y_j = *(y_begin + j);
      const N& y_ci_cj = (j < rs_i) ? y_ci[cj] : y_j[i];
      neg_assign_r(neg_y_ci_cj, y_ci_cj, ROUND_UP);
      if (m_i_j < neg_y_ci_cj) {
        return true;
      }
    }
  }
  return false;
}

template <typename T>
bool
Octagonal_Shape<T>::is_universe() const {
  // An empty octagon is not universe.
  if (marked_empty()) {
    return false;
  }

  // If the octagon is non-empty and zero-dimensional,
  // then it is necessarily the universe octagon.
  if (space_dim == 0) {
    return true;
  }

  // An universe octagon can only contains trivial  constraints.
  for (typename OR_Matrix<N>::const_element_iterator
         i = matrix.element_begin(), matrix_element_end = matrix.element_end();
       i != matrix_element_end;
       ++i) {
    if (!is_plus_infinity(*i)) {
      return false;
    }
  }

  return true;
}

template <typename T>
bool
Octagonal_Shape<T>::is_bounded() const {
  strong_closure_assign();
  // A zero-dimensional or empty octagon is bounded.
  if (marked_empty() || space_dim == 0) {
    return true;
  }

  // A bounded octagon never can contains trivial constraints.
  for (typename OR_Matrix<N>::const_row_iterator i = matrix.row_begin(),
         matrix_row_end = matrix.row_end(); i != matrix_row_end; ++i) {
    typename OR_Matrix<N>::const_row_reference_type x_i = *i;
    const dimension_type i_index = i.index();
    for (dimension_type j = i.row_size(); j-- > 0; ) {
      if (i_index != j) {
        if (is_plus_infinity(x_i[j])) {
          return false;
        }
      }
    }
  }

  return true;
}

template <typename T>
bool
Octagonal_Shape<T>::contains_integer_point() const {
  // Force strong closure.
  if (is_empty()) {
    return false;
  }
  if (space_dim == 0) {
    return true;
  }
  // A strongly closed and consistent Octagonal_Shape defined by
  // integer constraints can only be empty due to tight coherence.
  if (std::numeric_limits<T>::is_integer) {
    return !tight_coherence_would_make_empty();
  }

  // Build an integer Octagonal_Shape oct_z with bounds at least as
  // tight as those in *this and then recheck for emptiness, also
  // exploiting tight-coherence.
  Octagonal_Shape<mpz_class> oct_z(space_dim);
  oct_z.reset_strongly_closed();

  typedef Octagonal_Shape<mpz_class>::N Z;
  bool all_integers = true;
  typename OR_Matrix<N>::const_element_iterator x_i = matrix.element_begin();
  for (typename OR_Matrix<Z>::element_iterator
         z_i = oct_z.matrix.element_begin(),
         z_end = oct_z.matrix.element_end(); z_i != z_end; ++z_i, ++x_i) {
    const N& d = *x_i;
    if (is_plus_infinity(d)) {
      continue;
    }
    if (is_integer(d)) {
      assign_r(*z_i, d, ROUND_NOT_NEEDED);
    }
    else {
      all_integers = false;
      assign_r(*z_i, d, ROUND_DOWN);
    }
  }
  // Restore strong closure.
  if (all_integers) {
    // oct_z unchanged, so it is still strongly closed.
    oct_z.set_strongly_closed();
  }
  else {
    // oct_z changed: recompute strong closure.
    oct_z.strong_closure_assign();
    if (oct_z.marked_empty()) {
      return false;
    }
  }
  return !oct_z.tight_coherence_would_make_empty();
}

template <typename T>
bool
Octagonal_Shape<T>::frequency(const Linear_Expression& expr,
                              Coefficient& freq_n, Coefficient& freq_d,
                              Coefficient& val_n, Coefficient& val_d) const {
  // The dimension of `expr' must be at most the dimension of *this.
  if (space_dim < expr.space_dimension()) {
    throw_dimension_incompatible("frequency(e, ...)", "e", expr);
  }

  // Check if `expr' has a constant value.
  // If it is constant, set the frequency `freq_n' to 0
  // and return true. Otherwise the values for \p expr
  // are not discrete so return false.

  // Space dimension is 0: if empty, then return false;
  // otherwise the frequency is 0 and the value is the inhomogeneous term.
  if (space_dim == 0) {
    if (is_empty()) {
      return false;
    }
    freq_n = 0;
    freq_d = 1;
    val_n = expr.inhomogeneous_term();
    val_d = 1;
    return true;
  }

  strong_closure_assign();
  // For an empty Octagonal shape, we simply return false.
  if (marked_empty()) {
    return false;
  }

  // The Octagonal shape has at least 1 dimension and is not empty.
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  PPL_DIRTY_TEMP_COEFFICIENT(coeff_j);
  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);
  Linear_Expression le = expr;
  // Boolean to keep track of a variable `v' in expression `le'.
  // If we can replace `v' by an expression using variables other
  // than `v' and are already in `le', then this is set to true.
  bool constant_v = false;

  typedef typename OR_Matrix<N>::const_row_iterator row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type row_reference;

  const row_iterator m_begin = matrix.row_begin();
  const row_iterator m_end = matrix.row_end();

  PPL_DIRTY_TEMP_COEFFICIENT(val_denom);
  val_denom = 1;

  for (row_iterator i_iter = m_begin; i_iter != m_end; i_iter += 2) {
    constant_v = false;
    dimension_type i = i_iter.index();
    const Variable v(i/2);
    coeff = le.coefficient(v);
    if (coeff == 0) {
      constant_v = true;
      continue;
    }
    // We check the unary constraints.
    row_reference m_i = *i_iter;
    row_reference m_ii = *(i_iter + 1);
    const N& m_i_ii = m_i[i + 1];
    const N& m_ii_i = m_ii[i];
    if ((!is_plus_infinity(m_i_ii) && !is_plus_infinity(m_ii_i))
        && (is_additive_inverse(m_i_ii, m_ii_i))) {
      // If `v' is constant, replace it in `le' by the value.
      numer_denom(m_i_ii, numer, denom);
      denom *= 2;
      le -= coeff*v;
      le *= denom;
      le -= numer*coeff;
      val_denom *= denom;
      constant_v = true;
      continue;
    }
    // Check the octagonal constraints between `v' and the other dimensions
    // that have non-zero coefficient in `le'.
    else {
      PPL_ASSERT(!constant_v);
      using namespace Implementation::Octagonal_Shapes;
      const dimension_type ci = coherent_index(i);
      for (row_iterator j_iter = i_iter; j_iter != m_end; j_iter += 2) {
        dimension_type j = j_iter.index();
        const Variable vj(j/2);
        coeff_j = le.coefficient(vj);
        if (coeff_j == 0) {
          // The coefficient in `le' is 0, so do nothing.
          continue;
        }
        const dimension_type cj = coherent_index(j);
        const dimension_type cjj = coherent_index(j + 1);

        row_reference m_j = *(m_begin + j);
        row_reference m_cj = *(m_begin + cj);
        const N& m_j_i = m_j[i];
        const N& m_i_j = m_cj[ci];
        if ((!is_plus_infinity(m_i_j) && !is_plus_infinity(m_j_i))
            && (is_additive_inverse(m_i_j, m_j_i))) {
          // The coefficient for `vj' in `le' is not 0
          // and the constraint with `v' is an equality.
          // So apply this equality to eliminate `v' in `le'.
          numer_denom(m_i_j, numer, denom);
          le -= coeff*v;
          le += coeff*vj;
          le *= denom;
          le -= numer*coeff;
          val_denom *= denom;
          constant_v = true;
          break;
        }

        m_j = *(m_begin + (j + 1));
        m_cj = *(m_begin + cjj);
        const N& m_j_i1 = m_j[i];
        const N& m_i_j1 = m_cj[ci];
        if ((!is_plus_infinity(m_i_j1) && !is_plus_infinity(m_j_i1))
            && (is_additive_inverse(m_i_j1, m_j_i1))) {
          // The coefficient for `vj' in `le' is not 0
          // and the constraint with `v' is an equality.
          // So apply this equality to eliminate `v' in `le'.
          numer_denom(m_i_j1, numer, denom);
          le -= coeff*v;
          le -= coeff*vj;
          le *= denom;
          le -= numer*coeff;
          val_denom *= denom;
          constant_v = true;
          break;
        }
      }
      if (!constant_v) {
        // The expression `expr' is not constant.
        return false;
      }
    }
  }
  if (!constant_v) {
    // The expression `expr' is not constant.
    return false;
  }

  // The expression 'expr' is constant.
  freq_n = 0;
  freq_d = 1;

  // Reduce `val_n' and `val_d'.
  normalize2(le.inhomogeneous_term(), val_denom, val_n, val_d);
  return true;
}

template <typename T>
bool
Octagonal_Shape<T>::constrains(const Variable var) const {
  // `var' should be one of the dimensions of the octagonal shape.
  const dimension_type var_space_dim = var.space_dimension();
  if (space_dimension() < var_space_dim) {
    throw_dimension_incompatible("constrains(v)", "v", var);
  }

  // An octagon known to be empty constrains all variables.
  // (Note: do not force emptiness check _yet_)
  if (marked_empty()) {
    return true;
  }

  // Check whether `var' is syntactically constrained.
  const dimension_type n_v = 2*(var_space_dim - 1);
  typename OR_Matrix<N>::const_row_iterator m_iter = matrix.row_begin() + n_v;
  typename OR_Matrix<N>::const_row_reference_type r_v = *m_iter;
  typename OR_Matrix<N>::const_row_reference_type r_cv = *(++m_iter);
  for (dimension_type h = m_iter.row_size(); h-- > 0; ) {
    if (!is_plus_infinity(r_v[h]) || !is_plus_infinity(r_cv[h])) {
      return true;
    }
  }
  ++m_iter;
  for (typename OR_Matrix<N>::const_row_iterator m_end = matrix.row_end();
       m_iter != m_end; ++m_iter) {
    typename OR_Matrix<N>::const_row_reference_type r = *m_iter;
    if (!is_plus_infinity(r[n_v]) || !is_plus_infinity(r[n_v + 1])) {
      return true;
    }
  }

  // `var' is not syntactically constrained:
  // now force an emptiness check.
  return is_empty();
}

template <typename T>
bool
Octagonal_Shape<T>::is_strong_coherent() const {
  // This method is only used by method OK() so as to check if a
  // strongly closed matrix is also strong-coherent, as it must be.
  const dimension_type num_rows = matrix.num_rows();

  // Allocated here once and for all.
  PPL_DIRTY_TEMP(N, semi_sum);
  // The strong-coherence is: for every indexes i and j (and i != j)
  // matrix[i][j] <= (matrix[i][ci] + matrix[cj][j])/2
  // where ci = i + 1, if i is even number or
  //       ci = i - 1, if i is odd.
  // Ditto for cj.
  for (dimension_type i = num_rows; i-- > 0; ) {
    typename OR_Matrix<N>::const_row_iterator iter = matrix.row_begin() + i;
    typename OR_Matrix<N>::const_row_reference_type m_i = *iter;
    using namespace Implementation::Octagonal_Shapes;
    const N& m_i_ci = m_i[coherent_index(i)];
    for (dimension_type j = matrix.row_size(i); j-- > 0; ) {
      // Note: on the main diagonal only PLUS_INFINITY can occur.
      if (i != j) {
        const N& m_cj_j = matrix[coherent_index(j)][j];
        if (!is_plus_infinity(m_i_ci)
            && !is_plus_infinity(m_cj_j)) {
          // Compute (m_i_ci + m_cj_j)/2 into `semi_sum',
          // rounding the result towards plus infinity.
          add_assign_r(semi_sum, m_i_ci, m_cj_j, ROUND_UP);
          div_2exp_assign_r(semi_sum, semi_sum, 1, ROUND_UP);
          if (m_i[j] > semi_sum) {
            return false;
          }
        }
      }
    }
  }
  return true;
}

template <typename T>
bool
Octagonal_Shape<T>::is_strongly_reduced() const {
  // This method is only used in assertions: efficiency is not a must.

  // An empty octagon is already transitively reduced.
  if (marked_empty()) {
    return true;
  }

  Octagonal_Shape x = *this;
  // The matrix representing an OS is strongly reduced if, by removing
  // any constraint, the resulting matrix describes a different OS.
  for (typename OR_Matrix<N>::const_row_iterator iter = matrix.row_begin(),
         matrix_row_end = matrix.row_end(); iter != matrix_row_end; ++iter) {
    typename OR_Matrix<N>::const_row_reference_type m_i = *iter;
    const dimension_type i = iter.index();
    for (dimension_type j = iter.row_size(); j-- > 0; ) {
      if (!is_plus_infinity(m_i[j])) {
        Octagonal_Shape x_copy = *this;
        assign_r(x_copy.matrix[i][j], PLUS_INFINITY, ROUND_NOT_NEEDED);
        if (x == x_copy) {
          return false;
        }
      }
    }
  }
  // The octagon is just reduced.
  return true;
}

template <typename T>
bool
Octagonal_Shape<T>::bounds(const Linear_Expression& expr,
                           const bool from_above) const {
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible((from_above
                                  ? "bounds_from_above(e)"
                                  : "bounds_from_below(e)"), "e", expr);
  }
  strong_closure_assign();

  // A zero-dimensional or empty octagon bounds everything.
  if (space_dim == 0 || marked_empty()) {
    return true;
  }

  // The constraint `c' is used to check if `expr' is an octagonal difference
  // and, in this case, to select the cell.
  const Constraint& c = (from_above) ? expr <= 0 : expr >= 0;
  dimension_type num_vars = 0;
  dimension_type i = 0;
  dimension_type j = 0;
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  PPL_DIRTY_TEMP_COEFFICIENT(term);
  if (Octagonal_Shape_Helper
    ::extract_octagonal_difference(c, c.space_dimension(), num_vars,
                                   i, j, coeff, term)) {
    if (num_vars == 0) {
      return true;
    }
    // Select the cell to be checked.
    typename OR_Matrix<N>::const_row_iterator i_iter = matrix.row_begin() + i;
    typename OR_Matrix<N>::const_row_reference_type m_i = *i_iter;
    return !is_plus_infinity(m_i[j]);
  }
  else {
    // `c' is not an octagonal constraint: use the MIP solver.
    Optimization_Mode mode_bounds =
      from_above ? MAXIMIZATION : MINIMIZATION;
    MIP_Problem mip(space_dim, constraints(), expr, mode_bounds);
    return mip.solve() == OPTIMIZED_MIP_PROBLEM;
  }
}

template <typename T>
bool
Octagonal_Shape<T>::max_min(const Linear_Expression& expr,
                            const bool maximize,
                            Coefficient& ext_n, Coefficient& ext_d,
                            bool& included) const {
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible((maximize
                                  ? "maximize(e, ...)"
                                  : "minimize(e, ...)"), "e", expr);
  }
  // Deal with zero-dim octagons first.
  if (space_dim == 0) {
    if (marked_empty()) {
      return false;
    }
    else {
      ext_n = expr.inhomogeneous_term();
      ext_d = 1;
      included = true;
      return true;
    }
  }

  strong_closure_assign();
  // For an empty OS we simply return false.
  if (marked_empty()) {
    return false;
  }

  // The constraint `c' is used to check if `expr' is an octagonal difference
  // and, in this case, to select the cell.
  const Constraint& c = (maximize) ? expr <= 0 : expr >= 0;
  dimension_type num_vars = 0;
  dimension_type i = 0;
  dimension_type j = 0;
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  PPL_DIRTY_TEMP_COEFFICIENT(term);
  if (!Octagonal_Shape_Helper
    ::extract_octagonal_difference(c, c.space_dimension(), num_vars,
                                   i, j, coeff, term)) {
    // `c' is not an octagonal constraint: use the MIP solver.
    Optimization_Mode max_min = (maximize) ? MAXIMIZATION : MINIMIZATION;
    MIP_Problem mip(space_dim, constraints(), expr, max_min);
    if (mip.solve() == OPTIMIZED_MIP_PROBLEM) {
      mip.optimal_value(ext_n, ext_d);
      included = true;
      return true;
    }
    else {
      // Here`expr' is unbounded in `*this'.
      return false;
    }
  }
  else {
    // `c' is an octagonal constraint.
    if (num_vars == 0) {
      ext_n = expr.inhomogeneous_term();
      ext_d = 1;
      included = true;
      return true;
    }

    // Select the cell to be checked.
    typename OR_Matrix<N>::const_row_iterator i_iter = matrix.row_begin() + i;
    typename OR_Matrix<N>::const_row_reference_type m_i = *i_iter;
    PPL_DIRTY_TEMP(N, d);
    if (!is_plus_infinity(m_i[j])) {
      const Coefficient& b = expr.inhomogeneous_term();
      PPL_DIRTY_TEMP_COEFFICIENT(minus_b);
      neg_assign(minus_b, b);
      const Coefficient& sc_b = maximize ? b : minus_b;
      assign_r(d, sc_b, ROUND_UP);
      // Set `coeff_expr' to the absolute value of coefficient of a variable
      // of `expr'.
      PPL_DIRTY_TEMP(N, coeff_expr);
      const Coefficient& coeff_i = expr.coefficient(Variable(i/2));
      const int sign_i = sgn(coeff_i);
      if (sign_i > 0) {
        assign_r(coeff_expr, coeff_i, ROUND_UP);
      }
      else {
        PPL_DIRTY_TEMP_COEFFICIENT(minus_coeff_i);
        neg_assign(minus_coeff_i, coeff_i);
        assign_r(coeff_expr, minus_coeff_i, ROUND_UP);
      }
      // Approximating the maximum/minimum of `expr'.
      if (num_vars == 1) {
        PPL_DIRTY_TEMP(N, m_i_j);
        div_2exp_assign_r(m_i_j, m_i[j], 1, ROUND_UP);
        add_mul_assign_r(d, coeff_expr, m_i_j, ROUND_UP);
      }
      else {
        add_mul_assign_r(d, coeff_expr, m_i[j], ROUND_UP);
      }
      numer_denom(d, ext_n, ext_d);
      if (!maximize) {
        neg_assign(ext_n);
      }
      included = true;
      return true;
    }

    // The `expr' is unbounded.
    return false;
  }
}

template <typename T>
bool
Octagonal_Shape<T>::max_min(const Linear_Expression& expr,
                            const bool maximize,
                            Coefficient& ext_n, Coefficient& ext_d,
                            bool& included, Generator& g) const {
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible((maximize
                                  ? "maximize(e, ...)"
                                  : "minimize(e, ...)"), "e", expr);
  }
  // Deal with zero-dim octagons first.
  if (space_dim == 0) {
    if (marked_empty()) {
      return false;
    }
    else {
      ext_n = expr.inhomogeneous_term();
      ext_d = 1;
      included = true;
      g = point();
      return true;
    }
  }

  strong_closure_assign();
  // For an empty OS we simply return false.
  if (marked_empty()) {
    return false;
  }
  if (!is_universe()) {
    // We use MIP_Problems to handle constraints that are not
    // octagonal difference.
    Optimization_Mode max_min = (maximize) ? MAXIMIZATION : MINIMIZATION;
    MIP_Problem mip(space_dim, constraints(), expr, max_min);
    if (mip.solve() == OPTIMIZED_MIP_PROBLEM) {
      g = mip.optimizing_point();
      mip.evaluate_objective_function(g, ext_n, ext_d);
      included = true;
      return true;
    }
  }
  // The `expr' is unbounded.
  return false;
}

template <typename T>
Poly_Con_Relation
Octagonal_Shape<T>::relation_with(const Congruence& cg) const {
  dimension_type cg_space_dim = cg.space_dimension();

  // Dimension-compatibility check.
  if (cg_space_dim > space_dim) {
    throw_dimension_incompatible("relation_with(cg)", cg);
  }

  // If the congruence is an equality,
  // find the relation with the equivalent equality constraint.
  if (cg.is_equality()) {
    Constraint c(cg);
    return relation_with(c);
  }

  strong_closure_assign();

  if (marked_empty()) {
    return Poly_Con_Relation::saturates()
      && Poly_Con_Relation::is_included()
      && Poly_Con_Relation::is_disjoint();
  }

  if (space_dim == 0) {
    if (cg.is_inconsistent()) {
      return Poly_Con_Relation::is_disjoint();
    }
    else {
      return Poly_Con_Relation::saturates()
        && Poly_Con_Relation::is_included();
    }
  }

  // Find the lower bound for a hyperplane with direction
  // defined by the congruence.
  Linear_Expression le(cg.expression());
  PPL_DIRTY_TEMP_COEFFICIENT(min_numer);
  PPL_DIRTY_TEMP_COEFFICIENT(min_denom);
  bool min_included;
  bool bounded_below = minimize(le, min_numer, min_denom, min_included);

  // If there is no lower bound, then some of the hyperplanes defined by
  // the congruence will strictly intersect the shape.
  if (!bounded_below) {
    return Poly_Con_Relation::strictly_intersects();
  }

  // TODO: Consider adding a max_and_min() method, performing both
  // maximization and minimization so as to possibly exploit
  // incrementality of the MIP solver.

  // Find the upper bound for a hyperplane with direction
  // defined by the congruence.
  PPL_DIRTY_TEMP_COEFFICIENT(max_numer);
  PPL_DIRTY_TEMP_COEFFICIENT(max_denom);
  bool max_included;
  bool bounded_above = maximize(le, max_numer, max_denom, max_included);

  // If there is no upper bound, then some of the hyperplanes defined by
  // the congruence will strictly intersect the shape.
  if (!bounded_above) {
    return Poly_Con_Relation::strictly_intersects();
  }

  PPL_DIRTY_TEMP_COEFFICIENT(signed_distance);

  // Find the position value for the hyperplane that satisfies the congruence
  // and is above the lower bound for the shape.
  PPL_DIRTY_TEMP_COEFFICIENT(min_value);
  min_value = min_numer / min_denom;
  const Coefficient& modulus = cg.modulus();
  signed_distance = min_value % modulus;
  min_value -= signed_distance;
  if (min_value * min_denom < min_numer) {
    min_value += modulus;
  }

  // Find the position value for the hyperplane that satisfies the congruence
  // and is below the upper bound for the shape.
  PPL_DIRTY_TEMP_COEFFICIENT(max_value);
  max_value = max_numer / max_denom;
  signed_distance = max_value % modulus;
  max_value += signed_distance;
  if (max_value * max_denom > max_numer) {
    max_value -= modulus;
  }

  // If the upper bound value is less than the lower bound value,
  // then there is an empty intersection with the congruence;
  // otherwise it will strictly intersect.
  if (max_value < min_value) {
    return Poly_Con_Relation::is_disjoint();
  }
  else {
    return Poly_Con_Relation::strictly_intersects();
  }
}

template <typename T>
Poly_Con_Relation
Octagonal_Shape<T>::relation_with(const Constraint& c) const {
  dimension_type c_space_dim = c.space_dimension();

  // Dimension-compatibility check.
  if (c_space_dim > space_dim) {
    throw_dimension_incompatible("relation_with(c)", c);
  }

  // The closure needs to make explicit the implicit constraints.
  strong_closure_assign();

  if (marked_empty()) {
    return Poly_Con_Relation::saturates()
      && Poly_Con_Relation::is_included()
      && Poly_Con_Relation::is_disjoint();
  }

  if (space_dim == 0) {
    // Trivially false zero-dimensional constraint.
    if ((c.is_equality() && c.inhomogeneous_term() != 0)
        || (c.is_inequality() && c.inhomogeneous_term() < 0)) {
      return Poly_Con_Relation::is_disjoint();
    }
    else if (c.is_strict_inequality() && c.inhomogeneous_term() == 0) {
      // The constraint 0 > 0 implicitly defines the hyperplane 0 = 0;
      // thus, the zero-dimensional point also saturates it.
      return Poly_Con_Relation::saturates()
        && Poly_Con_Relation::is_disjoint();
    }

    // Trivially true zero-dimensional constraint.
    else if (c.is_equality() || c.inhomogeneous_term() == 0) {
      return Poly_Con_Relation::saturates()
        && Poly_Con_Relation::is_included();
    }
    else {
      // The zero-dimensional point saturates
      // neither the positivity constraint 1 >= 0,
      // nor the strict positivity constraint 1 > 0.
      return Poly_Con_Relation::is_included();
    }
  }

  dimension_type num_vars = 0;
  dimension_type i = 0;
  dimension_type j = 0;
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  PPL_DIRTY_TEMP_COEFFICIENT(c_term);
  if (!Octagonal_Shape_Helper
    ::extract_octagonal_difference(c, c_space_dim, num_vars,
                                   i, j, coeff, c_term)) {
    // Constraints that are not octagonal differences.
    // Use maximize() and minimize() to do much of the work.

    // Find the linear expression for the constraint and use that to
    // find if the expression is bounded from above or below and if it
    // is, find the maximum and minimum values.
    Linear_Expression le;
    le.set_space_dimension(c.space_dimension());
    le.linear_combine(c.expr, Coefficient_one(), Coefficient_one(),
                      1, c_space_dim + 1);

    PPL_DIRTY_TEMP_COEFFICIENT(max_numer);
    PPL_DIRTY_TEMP_COEFFICIENT(max_denom);
    bool max_included;
    PPL_DIRTY_TEMP_COEFFICIENT(min_numer);
    PPL_DIRTY_TEMP_COEFFICIENT(min_denom);
    bool min_included;
    bool bounded_above = maximize(le, max_numer, max_denom, max_included);
    bool bounded_below = minimize(le, min_numer, min_denom, min_included);
    if (!bounded_above) {
      if (!bounded_below) {
        return Poly_Con_Relation::strictly_intersects();
      }
      min_numer += c.inhomogeneous_term() * min_denom;
      switch (sgn(min_numer)) {
      case 1:
        if (c.is_equality()) {
          return Poly_Con_Relation::is_disjoint();
        }
        return Poly_Con_Relation::is_included();
      case 0:
        if (c.is_strict_inequality() || c.is_equality()) {
          return Poly_Con_Relation::strictly_intersects();
        }
        return Poly_Con_Relation::is_included();
      case -1:
        return Poly_Con_Relation::strictly_intersects();
      }
    }
    if (!bounded_below) {
      max_numer += c.inhomogeneous_term() * max_denom;
      switch (sgn(max_numer)) {
      case 1:
        return Poly_Con_Relation::strictly_intersects();
      case 0:
        if (c.is_strict_inequality()) {
          return Poly_Con_Relation::is_disjoint();
        }
        return Poly_Con_Relation::strictly_intersects();
      case -1:
        return Poly_Con_Relation::is_disjoint();
      }
    }
    else {
      max_numer += c.inhomogeneous_term() * max_denom;
      min_numer += c.inhomogeneous_term() * min_denom;
      switch (sgn(max_numer)) {
      case 1:
        switch (sgn(min_numer)) {
        case 1:
          if (c.is_equality()) {
            return Poly_Con_Relation::is_disjoint();
          }
          return Poly_Con_Relation::is_included();
        case 0:
          if (c.is_equality()) {
            return Poly_Con_Relation::strictly_intersects();
          }
          if (c.is_strict_inequality()) {
            return Poly_Con_Relation::strictly_intersects();
          }
          return Poly_Con_Relation::is_included();
        case -1:
          return Poly_Con_Relation::strictly_intersects();
        }
        PPL_UNREACHABLE;
        break;
      case 0:
        if (min_numer == 0) {
          if (c.is_strict_inequality()) {
            return Poly_Con_Relation::is_disjoint()
              && Poly_Con_Relation::saturates();
          }
          return Poly_Con_Relation::is_included()
            && Poly_Con_Relation::saturates();
        }
        if (c.is_strict_inequality()) {
          return Poly_Con_Relation::is_disjoint();
        }
        return Poly_Con_Relation::strictly_intersects();
      case -1:
        return Poly_Con_Relation::is_disjoint();
      }
    }
  }

  if (num_vars == 0) {
    // Dealing with a trivial constraint.
    switch (sgn(c.inhomogeneous_term())) {
    case -1:
      return Poly_Con_Relation::is_disjoint();
    case 0:
      if (c.is_strict_inequality()) {
        return Poly_Con_Relation::saturates()
          && Poly_Con_Relation::is_disjoint();
      }
      else {
        return Poly_Con_Relation::saturates()
          && Poly_Con_Relation::is_included();
      }
    case 1:
      if (c.is_equality()) {
        return Poly_Con_Relation::is_disjoint();
      }
      else {
        return Poly_Con_Relation::is_included();
      }
    }
  }

  // Select the cell to be checked for the "<=" part of constraint.
  typename OR_Matrix<N>::const_row_iterator i_iter = matrix.row_begin() + i;
  typename OR_Matrix<N>::const_row_reference_type m_i = *i_iter;
  const N& m_i_j = m_i[j];
  // Set `coeff' to the absolute value of itself.
  if (coeff < 0) {
    neg_assign(coeff);
  }

  // Select the cell to be checked for the ">=" part of constraint.
  // Select the right row of the cell.
  if (i % 2 == 0) {
    ++i_iter;
  }
  else {
    --i_iter;
  }
  typename OR_Matrix<N>::const_row_reference_type m_ci = *i_iter;
  using namespace Implementation::Octagonal_Shapes;
  const N& m_ci_cj = m_ci[coherent_index(j)];
  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);
  // The following variables of mpq_class type are used to be precise
  // when the octagon is defined by integer constraints.
  PPL_DIRTY_TEMP(mpq_class, q_x);
  PPL_DIRTY_TEMP(mpq_class, q_y);
  PPL_DIRTY_TEMP(mpq_class, d);
  PPL_DIRTY_TEMP(mpq_class, d1);
  PPL_DIRTY_TEMP(mpq_class, c_denom);
  PPL_DIRTY_TEMP(mpq_class, q_denom);
  assign_r(c_denom, coeff, ROUND_NOT_NEEDED);
  assign_r(d, c_term, ROUND_NOT_NEEDED);
  neg_assign_r(d1, d, ROUND_NOT_NEEDED);
  div_assign_r(d, d, c_denom, ROUND_NOT_NEEDED);
  div_assign_r(d1, d1, c_denom, ROUND_NOT_NEEDED);

  if (is_plus_infinity(m_i_j)) {
    if (!is_plus_infinity(m_ci_cj)) {
      // `*this' is in the following form:
      // `-m_ci_cj <= v - u'.
      // In this case `*this' is disjoint from `c' if
      // `-m_ci_cj > d' (`-m_ci_cj >= d' if c is a strict inequality),
      // i.e., if `m_ci_cj < d1' (`m_ci_cj <= d1'
      // if c is a strict inequality).
      numer_denom(m_ci_cj, numer, denom);
      assign_r(q_denom, denom, ROUND_NOT_NEEDED);
      assign_r(q_y, numer, ROUND_NOT_NEEDED);
      div_assign_r(q_y, q_y, q_denom, ROUND_NOT_NEEDED);
      if (q_y < d1) {
        return Poly_Con_Relation::is_disjoint();
      }
      if (q_y == d1 && c.is_strict_inequality()) {
        return Poly_Con_Relation::is_disjoint();
      }
    }

    // In all other cases `*this' intersects `c'.
    return Poly_Con_Relation::strictly_intersects();
  }

  // Here `m_i_j' is not plus-infinity.
  numer_denom(m_i_j, numer, denom);
  assign_r(q_denom, denom, ROUND_NOT_NEEDED);
  assign_r(q_x, numer, ROUND_NOT_NEEDED);
  div_assign_r(q_x, q_x, q_denom, ROUND_NOT_NEEDED);

  if (!is_plus_infinity(m_ci_cj)) {
    numer_denom(m_ci_cj, numer, denom);
    assign_r(q_denom, denom, ROUND_NOT_NEEDED);
    assign_r(q_y, numer, ROUND_NOT_NEEDED);
    div_assign_r(q_y, q_y, q_denom, ROUND_NOT_NEEDED);
    if (q_x == d && q_y == d1) {
      if (c.is_strict_inequality()) {
        return Poly_Con_Relation::saturates()
          && Poly_Con_Relation::is_disjoint();
      }
      else {
        return Poly_Con_Relation::saturates()
          && Poly_Con_Relation::is_included();
      }
    }
    // `*this' is disjoint from `c' when
    // `m_ci_cj < d1' (`m_ci_cj <= d1' if `c' is a strict inequality).
    if (q_y < d1) {
      return Poly_Con_Relation::is_disjoint();
    }
    if (q_y == d1 && c.is_strict_inequality()) {
      return Poly_Con_Relation::is_disjoint();
    }
  }

  // Here `m_ci_cj' can be also plus-infinity.
  // If `c' is an equality, `*this' is disjoint from `c' if
  // `m_i_j < d'.
  if (d > q_x) {
    if (c.is_equality()) {
      return Poly_Con_Relation::is_disjoint();
    }
    else {
      return Poly_Con_Relation::is_included();
    }
  }

  if (d == q_x && c.is_nonstrict_inequality()) {
    return Poly_Con_Relation::is_included();
  }
  // In all other cases `*this' intersects `c'.
  return Poly_Con_Relation::strictly_intersects();
}

template <typename T>
Poly_Gen_Relation
Octagonal_Shape<T>::relation_with(const Generator& g) const {
  const dimension_type g_space_dim = g.space_dimension();

  // Dimension-compatibility check.
  if (space_dim < g_space_dim) {
    throw_dimension_incompatible("relation_with(g)", g);
  }

  // The closure needs to make explicit the implicit constraints and if the
  // octagon is empty.
  strong_closure_assign();

  // The empty octagon cannot subsume a generator.
  if (marked_empty()) {
    return Poly_Gen_Relation::nothing();
  }

  // A universe octagon in a zero-dimensional space subsumes
  // all the generators of a zero-dimensional space.
  if (space_dim == 0) {
    return Poly_Gen_Relation::subsumes();
  }

  const bool is_line = g.is_line();
  const bool is_line_or_ray = g.is_line_or_ray();

  // The relation between the octagon and the given generator is obtained
  // checking if the generator satisfies all the constraints in the octagon.
  // To check if the generator satisfies all the constraints it's enough
  // studying the sign of the scalar product between the generator and
  // all the constraints in the octagon.

  typedef typename OR_Matrix<N>::const_row_iterator row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type row_reference;

  const row_iterator m_begin = matrix.row_begin();
  const row_iterator m_end = matrix.row_end();

  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);
  PPL_DIRTY_TEMP_COEFFICIENT(product);

  // We find in `*this' all the constraints.
  for (row_iterator i_iter = m_begin; i_iter != m_end; i_iter += 2) {
    dimension_type i = i_iter.index();
    row_reference m_i = *i_iter;
    row_reference m_ii = *(i_iter + 1);
    const N& m_i_ii = m_i[i + 1];
    const N& m_ii_i = m_ii[i];
    // We have the unary constraints.
    const Variable x(i/2);
    const Coefficient& g_coeff_x
      = (x.space_dimension() > g_space_dim)
      ? Coefficient_zero()
      : g.coefficient(x);
    if (is_additive_inverse(m_i_ii, m_ii_i)) {
      // The constraint has form ax = b.
      // To satisfy the constraint it is necessary that the scalar product
      // is not zero. The scalar product has the form
      // 'denom * g_coeff_x - numer * g.divisor()'.
      numer_denom(m_ii_i, numer, denom);
      denom *= 2;
      product = denom * g_coeff_x;
      // Note that if the generator `g' is a line or a ray,
      // its divisor is zero.
      if (!is_line_or_ray) {
        neg_assign(numer);
        add_mul_assign(product, numer, g.divisor());
      }
      if (product != 0) {
        return Poly_Gen_Relation::nothing();
      }
    }
    // We have 0, 1 or 2 inequality constraints.
    else {
      if (!is_plus_infinity(m_i_ii)) {
        // The constraint has form -ax <= b.
        // If the generator is a line it's necessary to check if
        // the scalar product is not zero, if it is positive otherwise.
        numer_denom(m_i_ii, numer, denom);
        denom *= -2;
        product = denom * g_coeff_x;
        // Note that if the generator `g' is a line or a ray,
        // its divisor is zero.
        if (!is_line_or_ray) {
          neg_assign(numer);
          add_mul_assign(product, numer, g.divisor());
        }
        if (is_line && product != 0) {
          return Poly_Gen_Relation::nothing();
        }
        else
          // If the generator is not a line it's necessary to check
          // that the scalar product sign is not positive and the scalar
          // product has the form
          // '-denom * g.coeff_x - numer * g.divisor()'.
          if (product > 0) {
            return Poly_Gen_Relation::nothing();
          }
      }
      if (!is_plus_infinity(m_ii_i)) {
        // The constraint has form ax <= b.
        numer_denom(m_ii_i, numer, denom);
        denom *= 2;
        product = denom * g_coeff_x;
         // Note that if the generator `g' is a line or a ray,
        // its divisor is zero.
        if (!is_line_or_ray) {
          neg_assign(numer);
          add_mul_assign(product, numer , g.divisor());
        }
        if (is_line && product != 0) {
          return Poly_Gen_Relation::nothing();
        }
        else {
          // If the generator is not a line it's necessary to check
          // that the scalar product sign is not positive and the scalar
          // product has the form
          // 'denom * g_coeff_x - numer * g.divisor()'.
          if (product > 0) {
            return Poly_Gen_Relation::nothing();
          }
        }
      }
    }
  }

  // We have the binary constraints.
  for (row_iterator i_iter = m_begin ; i_iter != m_end; i_iter += 2) {
    dimension_type i = i_iter.index();
    row_reference m_i = *i_iter;
    row_reference m_ii = *(i_iter + 1);
    for (dimension_type j = 0; j < i; j += 2) {
      const N& m_i_j = m_i[j];
      const N& m_ii_jj = m_ii[j + 1];
      const N& m_ii_j = m_ii[j];
      const N& m_i_jj = m_i[j + 1];
      const Variable x(j/2);
      const Variable y(i/2);
      const Coefficient& g_coeff_x
        = (x.space_dimension() > g_space_dim)
        ? Coefficient_zero()
        : g.coefficient(x);
      const Coefficient& g_coeff_y
        = (y.space_dimension() > g_space_dim)
        ? Coefficient_zero()
        : g.coefficient(y);

      const bool difference_is_equality = is_additive_inverse(m_ii_jj, m_i_j);
      if (difference_is_equality) {
        // The constraint has form a*x - a*y = b.
        // The scalar product has the form
        // 'denom * coeff_x - denom * coeff_y - numer * g.divisor()'.
        // To satisfy the constraint it's necessary that the scalar product
        // is not zero.
        numer_denom(m_i_j, numer, denom);
        product = denom * g_coeff_x;
        neg_assign(denom);
        add_mul_assign(product, denom, g_coeff_y);
        // Note that if the generator `g' is a line or a ray,
        // its divisor is zero.
        if (!is_line_or_ray) {
          neg_assign(numer);
          add_mul_assign(product, numer, g.divisor());
        }
        if (product != 0) {
          return Poly_Gen_Relation::nothing();
        }
      }
      else {
        if (!is_plus_infinity(m_i_j)) {
          // The constraint has form a*x - a*y <= b.
          // The scalar product has the form
          // 'denom * coeff_x - denom * coeff_y - numer * g.divisor()'.
          // If the generator is not a line it's necessary to check
          // that the scalar product sign is not positive.
          numer_denom(m_i_j, numer, denom);
          product = denom * g_coeff_x;
          neg_assign(denom);
          add_mul_assign(product, denom, g_coeff_y);
          // Note that if the generator `g' is a line or a ray,
          // its divisor is zero.
          if (!is_line_or_ray) {
            neg_assign(numer);
            add_mul_assign(product, numer, g.divisor());
          }
          if (is_line && product != 0) {
            return Poly_Gen_Relation::nothing();
          }
          else if (product > 0) {
            return Poly_Gen_Relation::nothing();
          }
        }
        if (!is_plus_infinity(m_ii_jj)) {
          // The constraint has form -a*x + a*y <= b.
          // The scalar product has the form
          // '-denom * coeff_x + denom * coeff_y - numer * g.divisor()'.
          // If the generator is not a line it's necessary to check
          // that the scalar product sign is not positive.
          numer_denom(m_ii_jj, numer, denom);
          product = denom * g_coeff_y;
          neg_assign(denom);
          add_mul_assign(product, denom, g_coeff_x);
          // Note that if the generator `g' is a line or a ray,
          // its divisor is zero.
          if (!is_line_or_ray) {
            neg_assign(numer);
            add_mul_assign(product, numer, g.divisor());
          }
          if (is_line && product != 0) {
            return Poly_Gen_Relation::nothing();
          }
          else if (product > 0) {
            return Poly_Gen_Relation::nothing();
          }
        }
      }

      const bool sum_is_equality = is_additive_inverse(m_i_jj, m_ii_j);
      if (sum_is_equality) {
        // The constraint has form a*x + a*y = b.
        // The scalar product has the form
        // 'denom * coeff_x + denom * coeff_y - numer * g.divisor()'.
        // To satisfy the constraint it's necessary that the scalar product
        // is not zero.
        numer_denom(m_ii_j, numer, denom);
        product = denom * g_coeff_x;
        add_mul_assign(product, denom, g_coeff_y);
        // Note that if the generator `g' is a line or a ray,
        // its divisor is zero.
        if (!is_line_or_ray) {
          neg_assign(numer);
          add_mul_assign(product, numer, g.divisor());
        }
        if (product != 0) {
          return Poly_Gen_Relation::nothing();
        }
      }
      else {
        if (!is_plus_infinity(m_i_jj)) {
          // The constraint has form -a*x - a*y <= b.
          // The scalar product has the form
          // '-denom * coeff_x - denom * coeff_y - numer * g.divisor()'.
          // If the generator is not a line it's necessary to check
          // that the scalar product sign is not positive.
          numer_denom(m_i_jj, numer, denom);
          neg_assign(denom);
          product = denom * g_coeff_x;
          add_mul_assign(product, denom, g_coeff_y);
          // Note that if the generator `g' is a line or a ray,
          // its divisor is zero.
          if (!is_line_or_ray) {
            neg_assign(numer);
            add_mul_assign(product, numer, g.divisor());
          }
          if (is_line && product != 0) {
            return Poly_Gen_Relation::nothing();
          }
          else if (product > 0) {
            return Poly_Gen_Relation::nothing();
          }
        }
        if (!is_plus_infinity(m_ii_j)) {
          // The constraint has form a*x + a*y <= b.
          // The scalar product has the form
          // 'denom * coeff_x + denom * coeff_y - numer * g.divisor()'.
          // If the generator is not a line it's necessary to check
          // that the scalar product sign is not positive.
          numer_denom(m_ii_j, numer, denom);
          product = denom * g_coeff_x;
          add_mul_assign(product, denom, g_coeff_y);
          // Note that if the generator `g' is a line or a ray,
          // its divisor is zero.
          if (!is_line_or_ray) {
            neg_assign(numer);
            add_mul_assign(product, numer, g.divisor());
          }
          if (is_line && product != 0) {
            return Poly_Gen_Relation::nothing();
          }
          else if (product > 0) {
            return Poly_Gen_Relation::nothing();
          }
        }
      }
    }
  }
  // If this point is reached the constraint 'g' satisfies
  // all the constraints in the octagon.
  return Poly_Gen_Relation::subsumes();
}

template <typename T>
void
Octagonal_Shape<T>::strong_closure_assign() const {
  // Do something only if necessary (zero-dim implies strong closure).
  if (marked_empty() || marked_strongly_closed() || space_dim == 0) {
    return;
  }
  // Even though the octagon will not change, its internal representation
  // is going to be modified by the closure algorithm.
  Octagonal_Shape& x = const_cast<Octagonal_Shape<T>&>(*this);

  typedef typename OR_Matrix<N>::row_iterator row_iterator;
  typedef typename OR_Matrix<N>::row_reference_type row_reference;

  const dimension_type n_rows = x.matrix.num_rows();
  const row_iterator m_begin = x.matrix.row_begin();
  const row_iterator m_end = x.matrix.row_end();

  // Fill the main diagonal with zeros.
  for (row_iterator i = m_begin; i != m_end; ++i) {
    PPL_ASSERT(is_plus_infinity((*i)[i.index()]));
    assign_r((*i)[i.index()], 0, ROUND_NOT_NEEDED);
  }

  // This algorithm is given by two steps: the first one is a simple
  // adaptation of the `shortest-path closure' using the Floyd-Warshall
  // algorithm; the second one is the `strong-coherence' algorithm.
  // It is important to note that after the strong-coherence,
  // the octagon is still shortest-path closed and hence, strongly closed.

  // Recall that, given an index `h', we indicate with `ch' the coherent
  // index, i.e., the index such that:
  //   ch = h + 1, if h is an even number;
  //   ch = h - 1, if h is an odd number.

  typename OR_Matrix<N>::element_iterator iter_ij;
  std::vector<N> vec_k(n_rows);
  std::vector<N> vec_ck(n_rows);
  PPL_DIRTY_TEMP(N, sum1);
  PPL_DIRTY_TEMP(N, sum2);
  row_reference x_k;
  row_reference x_ck;
  row_reference x_i;
  row_reference x_ci;

  // Since the index `j' of the inner loop will go from 0 up to `i',
  // the three nested loops have to be executed twice.
  for (int twice = 0; twice < 2; ++twice) {

    row_iterator x_k_iter = m_begin;
    row_iterator x_i_iter = m_begin;
    for (dimension_type k = 0; k < n_rows; k += 2) {
      const dimension_type ck = k + 1;
      // Re-initialize the element iterator.
      iter_ij = x.matrix.element_begin();
      // Compute the row references `x_k' and `x_ck'.
      x_k  = *x_k_iter;
      ++x_k_iter;
      x_ck = *x_k_iter;
      ++x_k_iter;

      for (dimension_type i = 0; i <= k; i += 2) {
        const dimension_type ci = i + 1;
        // Storing x_k_i == x_ci_ck.
        vec_k[i] = x_k[i];
        // Storing x_k_ci == x_i_ck.
        vec_k[ci] = x_k[ci];
        // Storing x_ck_i == x_ci_k.
        vec_ck[i] = x_ck[i];
        // Storing x_ck_ci == x_i_k.
        vec_ck[ci] = x_ck[ci];
      }
      x_i_iter = x_k_iter;
      for (dimension_type i = k + 2; i < n_rows; i += 2) {
        const dimension_type ci = i + 1;
        x_i = *x_i_iter;
        ++x_i_iter;
        x_ci = *x_i_iter;
        ++x_i_iter;
        // Storing x_k_i == x_ci_ck.
        vec_k[i] = x_ci[ck];
        // Storing x_k_ci == x_i_ck.
        vec_k[ci] = x_i[ck];
        // Storing x_ck_i == x_ci_k.
        vec_ck[i] = x_ci[k];
        // Storing x_ck_ci == x_i_k.
        vec_ck[ci] = x_i[k];
      }

      for (dimension_type i = 0; i < n_rows; ++i) {
        using namespace Implementation::Octagonal_Shapes;
        const dimension_type ci = coherent_index(i);
        const N& vec_k_ci = vec_k[ci];
        const N& vec_ck_ci = vec_ck[ci];
        // Unfolding two iterations on `j': this ensures that
        // the loop exit condition `j <= i' is OK.
        for (dimension_type j = 0; j <= i; ) {
          // First iteration: compute
          //
          // <CODE>
          //   sum1 = x_i_k + x_k_j == x_ck_ci + x_k_j;
          //   sum2 = x_i_ck + x_ck_j == x_k_ci + x_ck_j;
          // </CODE>
          add_assign_r(sum1, vec_ck_ci, vec_k[j], ROUND_UP);
          add_assign_r(sum2, vec_k_ci, vec_ck[j], ROUND_UP);
          min_assign(sum1, sum2);
          min_assign(*iter_ij, sum1);
          // Exiting the first iteration: loop index control.
          ++j;
          ++iter_ij;
          // Second iteration: ditto.
          add_assign_r(sum1, vec_ck_ci, vec_k[j], ROUND_UP);
          add_assign_r(sum2, vec_k_ci, vec_ck[j], ROUND_UP);
          min_assign(sum1, sum2);
          min_assign(*iter_ij, sum1);
          // Exiting the second iteration: loop index control.
          ++j;
          ++iter_ij;
        }
      }
    }
  }

  // Check for emptiness: the octagon is empty if and only if there is a
  // negative value in the main diagonal.
  for (row_iterator i = m_begin; i != m_end; ++i) {
    N& x_i_i = (*i)[i.index()];
    if (sgn(x_i_i) < 0) {
      x.set_empty();
      return;
    }
    else {
      PPL_ASSERT(sgn(x_i_i) == 0);
      // Restore PLUS_INFINITY on the main diagonal.
      assign_r(x_i_i, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
  }

  // Step 2: we enforce the strong coherence.
  x.strong_coherence_assign();
  // The octagon is not empty and it is now strongly closed.
  x.set_strongly_closed();
}

template <typename T>
void
Octagonal_Shape<T>::strong_coherence_assign() {
  // The strong-coherence is: for every indexes i and j
  // m_i_j <= (m_i_ci + m_cj_j)/2
  // where ci = i + 1, if i is even number or
  //       ci = i - 1, if i is odd.
  // Ditto for cj.
  PPL_DIRTY_TEMP(N, semi_sum);
  for (typename OR_Matrix<N>::row_iterator i_iter = matrix.row_begin(),
         i_end = matrix.row_end(); i_iter != i_end; ++i_iter) {
    typename OR_Matrix<N>::row_reference_type x_i = *i_iter;
    const dimension_type i = i_iter.index();
    using namespace Implementation::Octagonal_Shapes;
    const N& x_i_ci = x_i[coherent_index(i)];
    // Avoid to do unnecessary sums.
    if (!is_plus_infinity(x_i_ci)) {
      for (dimension_type j = 0, rs_i = i_iter.row_size();
           j < rs_i; ++j) {
        if (i != j) {
          const N& x_cj_j = matrix[coherent_index(j)][j];
          if (!is_plus_infinity(x_cj_j)) {
            add_assign_r(semi_sum, x_i_ci, x_cj_j, ROUND_UP);
            div_2exp_assign_r(semi_sum, semi_sum, 1, ROUND_UP);
            min_assign(x_i[j], semi_sum);
          }
        }
      }
    }
  }
}

template <typename T>
bool
Octagonal_Shape<T>::tight_coherence_would_make_empty() const {
  PPL_ASSERT(std::numeric_limits<N>::is_integer);
  PPL_ASSERT(marked_strongly_closed());
  for (dimension_type i = 0; i < 2*space_dim; i += 2) {
    const dimension_type ci = i + 1;
    const N& mat_i_ci = matrix[i][ci];
    if (!is_plus_infinity(mat_i_ci)
        // Check for oddness of `mat_i_ci'.
        && !is_even(mat_i_ci)
        // Check for zero-equivalence of `i' and `ci'.
        && is_additive_inverse(mat_i_ci, matrix[ci][i])) {
      return true;
    }
  }
  return false;
}

template <typename T>
void
Octagonal_Shape<T>::tight_closure_assign() {
  PPL_COMPILE_TIME_CHECK(std::numeric_limits<T>::is_integer,
                         "Octagonal_Shape<T>::tight_closure_assign():"
                         " T in not an integer datatype.");
  // FIXME: this is just an executable specification.
  // (The following call could be replaced by shortest-path closure.)
  strong_closure_assign();
  if (marked_empty()) {
    return;
  }
  if (tight_coherence_would_make_empty()) {
    set_empty();
  }
  else {
    // Tighten the unary constraints.
    PPL_DIRTY_TEMP(N, temp_one);
    assign_r(temp_one, 1, ROUND_NOT_NEEDED);
    for (dimension_type i = 0; i < 2*space_dim; i += 2) {
      const dimension_type ci = i + 1;
      N& mat_i_ci = matrix[i][ci];
      if (!is_plus_infinity(mat_i_ci) && !is_even(mat_i_ci)) {
        sub_assign_r(mat_i_ci, mat_i_ci, temp_one, ROUND_UP);
      }
      N& mat_ci_i = matrix[ci][i];
      if (!is_plus_infinity(mat_ci_i) && !is_even(mat_ci_i)) {
        sub_assign_r(mat_ci_i, mat_ci_i, temp_one, ROUND_UP);
      }
    }
    // Propagate tightened unary constraints.
    strong_coherence_assign();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>
::incremental_strong_closure_assign(const Variable var) const {
  // `var' should be one of the dimensions of the octagon.
  if (var.id() >= space_dim) {
    throw_dimension_incompatible("incremental_strong_closure_assign(v)",
                                 var.id());
  }

  // Do something only if necessary.
  if (marked_empty() || marked_strongly_closed()) {
    return;
  }

  Octagonal_Shape& x = const_cast<Octagonal_Shape<T>&>(*this);

  typedef typename OR_Matrix<N>::row_iterator row_iterator;
  typedef typename OR_Matrix<N>::row_reference_type row_reference;

  const row_iterator m_begin = x.matrix.row_begin();
  const row_iterator m_end = x.matrix.row_end();

  // Fill the main diagonal with zeros.
  for (row_iterator i = m_begin; i != m_end; ++i) {
    PPL_ASSERT(is_plus_infinity((*i)[i.index()]));
    assign_r((*i)[i.index()], 0, ROUND_NOT_NEEDED);
  }

  // Using the incremental Floyd-Warshall algorithm.
  // Step 1: Improve all constraints on variable `var'.
  const dimension_type v = 2*var.id();
  const dimension_type cv = v + 1;
  row_iterator v_iter = m_begin + v;
  row_iterator cv_iter = v_iter + 1;
  row_reference x_v = *v_iter;
  row_reference x_cv = *cv_iter;
  const dimension_type rs_v = v_iter.row_size();
  const dimension_type n_rows = x.matrix.num_rows();
  PPL_DIRTY_TEMP(N, sum);
  using namespace Implementation::Octagonal_Shapes;
  for (row_iterator k_iter = m_begin; k_iter != m_end; ++k_iter) {
    const dimension_type k = k_iter.index();
    const dimension_type ck = coherent_index(k);
    const dimension_type rs_k = k_iter.row_size();
    row_reference x_k = *k_iter;
    row_reference x_ck = (k % 2 != 0) ? *(k_iter-1) : *(k_iter + 1);

    for (row_iterator i_iter = m_begin; i_iter != m_end; ++i_iter) {
      const dimension_type i = i_iter.index();
      const dimension_type ci = coherent_index(i);
      const dimension_type rs_i = i_iter.row_size();
      row_reference x_i = *i_iter;
      row_reference x_ci = (i % 2 != 0) ? *(i_iter-1) : *(i_iter + 1);

      const N& x_i_k = (k < rs_i) ? x_i[k] : x_ck[ci];
      if (!is_plus_infinity(x_i_k)) {
        const N& x_k_v = (v < rs_k) ? x_k[v] : x_cv[ck];
        if (!is_plus_infinity(x_k_v)) {
          add_assign_r(sum, x_i_k, x_k_v, ROUND_UP);
          N& x_i_v = (v < rs_i) ? x_i[v] : x_cv[ci];
          min_assign(x_i_v, sum);
        }
        const N& x_k_cv = (cv < rs_k) ? x_k[cv] : x_v[ck];
        if (!is_plus_infinity(x_k_cv)) {
          add_assign_r(sum, x_i_k, x_k_cv, ROUND_UP);
          N& x_i_cv = (cv < rs_i) ? x_i[cv] : x_v[ci];
          min_assign(x_i_cv, sum);
        }
      }
      const N& x_k_i = (i < rs_k) ? x_k[i] : x_ci[ck];
      if (!is_plus_infinity(x_k_i)) {
        const N& x_v_k = (k < rs_v) ? x_v[k] : x_ck[cv];
        if (!is_plus_infinity(x_v_k)) {
          N& x_v_i = (i < rs_v) ? x_v[i] : x_ci[cv];
          add_assign_r(sum, x_v_k, x_k_i, ROUND_UP);
          min_assign(x_v_i, sum);
        }
        const N& x_cv_k = (k < rs_v) ? x_cv[k] : x_ck[v];
        if (!is_plus_infinity(x_cv_k)) {
          N& x_cv_i = (i < rs_v) ? x_cv[i] : x_ci[v];
          add_assign_r(sum, x_cv_k, x_k_i, ROUND_UP);
          min_assign(x_cv_i, sum);
        }
      }

    }
  }

  // Step 2: improve the other bounds by using the precise bounds
  // for the constraints on `var'.
  for (row_iterator i_iter = m_begin; i_iter != m_end; ++i_iter) {
    const dimension_type i = i_iter.index();
    const dimension_type ci = coherent_index(i);
    const dimension_type rs_i = i_iter.row_size();
    row_reference x_i = *i_iter;
    const N& x_i_v = (v < rs_i) ? x_i[v] : x_cv[ci];
    // TODO: see if it is possible to optimize this inner loop
    // by splitting it into several parts, so as to avoid
    // conditional expressions.
    for (dimension_type j = 0; j < n_rows; ++j) {
      const dimension_type cj = coherent_index(j);
      row_reference x_cj = *(m_begin + cj);
      N& x_i_j = (j < rs_i) ? x_i[j] : x_cj[ci];
      if (!is_plus_infinity(x_i_v)) {
        const N& x_v_j = (j < rs_v) ? x_v[j] : x_cj[cv];
        if (!is_plus_infinity(x_v_j)) {
          add_assign_r(sum, x_i_v, x_v_j, ROUND_UP);
          min_assign(x_i_j, sum);
        }
      }
      const N& x_i_cv = (cv < rs_i) ? x_i[cv] : x_v[ci];
      if (!is_plus_infinity(x_i_cv)) {
        const N& x_cv_j = (j < rs_v) ? x_cv[j] : x_cj[v];
        if (!is_plus_infinity(x_cv_j)) {
          add_assign_r(sum, x_i_cv, x_cv_j, ROUND_UP);
          min_assign(x_i_j, sum);
        }
      }
    }
  }

  // Check for emptiness: the octagon is empty if and only if there is a
  // negative value on the main diagonal.
  for (row_iterator i = m_begin; i != m_end; ++i) {
    N& x_i_i = (*i)[i.index()];
    if (sgn(x_i_i) < 0) {
      x.set_empty();
      return;
    }
    else {
      // Restore PLUS_INFINITY on the main diagonal.
      PPL_ASSERT(sgn(x_i_i) == 0);
      assign_r(x_i_i, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
  }

  // Step 3: we enforce the strong coherence.
  x.strong_coherence_assign();
  // The octagon is not empty and it is now strongly closed.
  x.set_strongly_closed();
}

template <typename T>
void
Octagonal_Shape<T>
::compute_successors(std::vector<dimension_type>& successor) const {
  PPL_ASSERT(!marked_empty() && marked_strongly_closed());
  PPL_ASSERT(successor.size() == 0);
  // Variables are ordered according to their index.
  // The vector `successor' is used to indicate which variable
  // immediately follows a given one in the corresponding equivalence class.
  const dimension_type successor_size = matrix.num_rows();
  // Initially, each variable is successor of its own zero-equivalence class.
  successor.reserve(successor_size);
  for (dimension_type i = 0; i < successor_size; ++i) {
    successor.push_back(i);
  }
  // Now compute actual successors.
  for (dimension_type i = successor_size; i-- > 0; )  {
    typename OR_Matrix<N>::const_row_iterator i_iter = matrix.row_begin() + i;
    typename OR_Matrix<N>::const_row_reference_type m_i = *i_iter;
    typename OR_Matrix<N>::const_row_reference_type m_ci
      = (i % 2 != 0) ? *(i_iter-1) : *(i_iter + 1);
    for (dimension_type j = 0; j < i; ++j) {
      // FIXME: what is the following, commented-out for?
    //for (dimension_type j = i; j-- > 0; ) {
      using namespace Implementation::Octagonal_Shapes;
      dimension_type cj = coherent_index(j);
      if (is_additive_inverse(m_ci[cj], m_i[j])) {
        // Choose as successor the variable having the greatest index.
        successor[j] = i;
      }
    }
  }
}

template <typename T>
void
Octagonal_Shape<T>
::compute_leaders(std::vector<dimension_type>& leaders) const {
  PPL_ASSERT(!marked_empty() && marked_strongly_closed());
  PPL_ASSERT(leaders.size() == 0);
  // Variables are ordered according to their index.
  // The vector `leaders' is used to indicate the smallest variable
  // that belongs to the corresponding equivalence class.
  const dimension_type leader_size = matrix.num_rows();
  // Initially, each variable is leader of its own zero-equivalence class.
  leaders.reserve(leader_size);
  for (dimension_type i = 0; i < leader_size; ++i) {
    leaders.push_back(i);
  }
  // Now compute actual leaders.
  for (typename OR_Matrix<N>::const_row_iterator i_iter = matrix.row_begin(),
         matrix_row_end = matrix.row_end();
       i_iter != matrix_row_end; ++i_iter) {
    typename OR_Matrix<N>::const_row_reference_type m_i = *i_iter;
    dimension_type i = i_iter.index();
    typename OR_Matrix<N>::const_row_reference_type m_ci
      = (i % 2 != 0) ? *(i_iter-1) : *(i_iter + 1);
    for (dimension_type j = 0; j < i; ++j) {
      using namespace Implementation::Octagonal_Shapes;
      dimension_type cj = coherent_index(j);
      if (is_additive_inverse(m_ci[cj], m_i[j])) {
        // Choose as leader the variable having the smaller index.
        leaders[i] = leaders[j];
      }
    }
  }
}

template <typename T>
void
Octagonal_Shape<T>
::compute_leaders(std::vector<dimension_type>& successor,
                  std::vector<dimension_type>& no_sing_leaders,
                  bool& exist_sing_class,
                  dimension_type& sing_leader) const {
  PPL_ASSERT(!marked_empty() && marked_strongly_closed());
  PPL_ASSERT(no_sing_leaders.size() == 0);
  dimension_type successor_size = successor.size();
  std::deque<bool> dealt_with(successor_size, false);
  for (dimension_type i = 0; i < successor_size; ++i) {
    dimension_type next_i = successor[i];
    if (!dealt_with[i]) {
      // The index is a leader.
      // Now check if it is a leader of a singular class or not.
      using namespace Implementation::Octagonal_Shapes;
      if (next_i == coherent_index(i)) {
        exist_sing_class = true;
        sing_leader = i;
      }
      else {
        no_sing_leaders.push_back(i);
      }
    }
    // The following index is not a leader.
    dealt_with[next_i] = true;
  }
}

template <typename T>
void
Octagonal_Shape<T>::strong_reduction_assign() const {
  // Zero-dimensional octagonal shapes are necessarily reduced.
  if (space_dim == 0) {
    return;
  }
  strong_closure_assign();
  // If `*this' is empty, then there is nothing to reduce.
  if (marked_empty()) {
    return;
  }
  // Detect non-redundant constraints.
  std::vector<Bit_Row> non_red;
  non_redundant_matrix_entries(non_red);

  // Throw away redundant constraints.
  Octagonal_Shape<T>& x = const_cast<Octagonal_Shape<T>&>(*this);
#ifndef NDEBUG
  const Octagonal_Shape x_copy_before(x);
#endif
  typename OR_Matrix<N>::element_iterator x_i = x.matrix.element_begin();
  for (dimension_type i = 0; i < 2 * space_dim; ++i) {
    const Bit_Row& non_red_i = non_red[i];
    for (dimension_type j = 0,
           j_end = OR_Matrix<N>::row_size(i); j < j_end; ++j, ++x_i) {
      if (!non_red_i[j]) {
        assign_r(*x_i, PLUS_INFINITY, ROUND_NOT_NEEDED);
      }
    }
  }
  x.reset_strongly_closed();
#ifndef NDEBUG
  const Octagonal_Shape x_copy_after(x);
  PPL_ASSERT(x_copy_before == x_copy_after);
  PPL_ASSERT(x.is_strongly_reduced());
  PPL_ASSERT(x.OK());
#endif
}

template <typename T>
void
Octagonal_Shape<T>
::non_redundant_matrix_entries(std::vector<Bit_Row>& non_redundant) const {
  // Private method: the caller has to ensure the following.
  PPL_ASSERT(space_dim > 0 && !marked_empty() && marked_strongly_closed());
  PPL_ASSERT(non_redundant.empty());

  // Initialize `non_redundant' as if it was an OR_Matrix of booleans
  // (initially set to false).
  non_redundant.resize(2*space_dim);

  // Step 1: compute zero-equivalence classes.
  // Variables corresponding to indices `i' and `j' are zero-equivalent
  // if they lie on a zero-weight loop; since the matrix is strongly
  // closed, this happens if and only if matrix[i][j] == -matrix[ci][cj].
  std::vector<dimension_type> no_sing_leaders;
  dimension_type sing_leader = 0;
  bool exist_sing_class = false;
  std::vector<dimension_type> successor;
  compute_successors(successor);
  compute_leaders(successor, no_sing_leaders, exist_sing_class, sing_leader);
  const dimension_type num_no_sing_leaders = no_sing_leaders.size();


  // Step 2: flag redundant constraints in `redundancy'.
  // Go through non-singular leaders first.
  for (dimension_type li = 0; li < num_no_sing_leaders; ++li) {
    const dimension_type i = no_sing_leaders[li];
    using namespace Implementation::Octagonal_Shapes;
    const dimension_type ci = coherent_index(i);
    typename OR_Matrix<N>::const_row_reference_type
      m_i = *(matrix.row_begin() + i);
    if (i % 2 == 0) {
      // Each positive equivalence class must have a single 0-cycle
      // connecting all equivalent variables in increasing order.
      // Note: by coherence assumption, the variables in the
      // corresponding negative equivalence class are
      // automatically connected.
      if (i != successor[i]) {
        dimension_type j = i;
        dimension_type next_j = successor[j];
        while (j != next_j) {
          non_redundant[next_j].set(j);
          j = next_j;
          next_j = successor[j];
        }
        const dimension_type cj = coherent_index(j);
        non_redundant[cj].set(ci);
      }
    }

    dimension_type rs_li = (li % 2 != 0) ? li : (li + 1);
    // Check if the constraint is redundant.
    PPL_DIRTY_TEMP(N, tmp);
    for (dimension_type lj = 0 ; lj <= rs_li; ++lj) {
      const dimension_type j = no_sing_leaders[lj];
      const dimension_type cj = coherent_index(j);
      const N& m_i_j = m_i[j];
      const N& m_i_ci = m_i[ci];
      bool to_add = true;
      // Control if the constraint is redundant by strong-coherence,
      // that is:
      // m_i_j >= (m_i_ci + m_cj_j)/2,   where j != ci.
      if (j != ci) {
        add_assign_r(tmp, m_i_ci, matrix[cj][j], ROUND_UP);
        div_2exp_assign_r(tmp, tmp, 1, ROUND_UP);
        if (m_i_j >= tmp) {
          // The constraint is redundant.
          continue;
        }
      }
      // Control if the constraint is redundant by strong closure, that is
      // if there is a path from i to j (i = i_0, ... , i_n = j), such that
      // m_i_j = sum_{k=0}^{n-1} m_{i_k}_{i_(k + 1)}.
      // Since the octagon is already strongly closed, the above relation
      // is reduced to three case, in accordance with k, i, j inter-depend:
      // exit k such that
      // 1.) m_i_j >= m_i_k   + m_cj_ck,   if k < j < i; or
      // 2.) m_i_j >= m_i_k   + m_k,_j,    if j < k < i; or
      // 3.) m_i_j >= m_ck_ci + m_k_j,     if j < i < k.
      // Note: `i > j'.
      for (dimension_type lk = 0; lk < num_no_sing_leaders; ++lk) {
        const dimension_type k = no_sing_leaders[lk];
        if (k != i && k != j) {
          dimension_type ck = coherent_index(k);
          if (k < j) {
            // Case 1.
            add_assign_r(tmp, m_i[k], matrix[cj][ck], ROUND_UP);
          }
          else if (k < i) {
            // Case 2.
            add_assign_r(tmp, m_i[k], matrix[k][j], ROUND_UP);
          }
          else {
            // Case 3.
            add_assign_r(tmp, matrix[ck][ci], matrix[k][j], ROUND_UP);
          }
          // Checks if the constraint is redundant.
          if (m_i_j >= tmp) {
            to_add = false;
            break;
          }
        }
      }

      if (to_add) {
        // The constraint is not redundant.
        non_redundant[i].set(j);
      }
    }
  }

  // If there exist a singular equivalence class, then it must have a
  // single 0-cycle connecting all the positive and negative equivalent
  // variables.
  // Note: the singular class is not connected with the other classes.
  if (exist_sing_class) {
    non_redundant[sing_leader].set(sing_leader + 1);
    if (successor[sing_leader + 1] != sing_leader + 1) {
      dimension_type j = sing_leader;
      dimension_type next_j = successor[j + 1];
      while (next_j != j + 1) {
        non_redundant[next_j].set(j);
        j = next_j;
        next_j = successor[j + 1];
      }
      non_redundant[j + 1].set(j);
    }
    else {
      non_redundant[sing_leader + 1].set(sing_leader);
    }
  }
}

template <typename T>
void
Octagonal_Shape<T>::upper_bound_assign(const Octagonal_Shape& y) {
  // Dimension-compatibility check.
  if (space_dim != y.space_dim) {
    throw_dimension_incompatible("upper_bound_assign(y)", y);
  }

  // The hull of an octagon `x' with an empty octagon is `x'.
  y.strong_closure_assign();
  if (y.marked_empty()) {
    return;
  }
  strong_closure_assign();
  if (marked_empty()) {
    *this = y;
    return;
  }

  // The oct-hull is obtained by computing maxima.
  typename OR_Matrix<N>::const_element_iterator j = y.matrix.element_begin();
  for (typename OR_Matrix<N>::element_iterator i = matrix.element_begin(),
         matrix_element_end = matrix.element_end();
       i != matrix_element_end; ++i, ++j) {
    max_assign(*i, *j);
  }

  // The result is still closed.
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::difference_assign(const Octagonal_Shape& y) {
  // Dimension-compatibility check.
  if (space_dim != y.space_dim) {
    throw_dimension_incompatible("difference_assign(y)", y);
  }

  Octagonal_Shape& x = *this;

  // Being lazy here is only harmful.
  // We close.
  x.strong_closure_assign();
  // The difference of an empty octagon and of an octagon `p' is empty.
  if (x.marked_empty()) {
    return;
  }
  // The difference of a octagon `p' and an empty octagon is `p'.
  if (y.marked_empty()) {
    return;
  }
  // If both octagons are zero-dimensional,
  // then at this point they are necessarily universe octagons,
  // so that their difference is empty.
  if (x.space_dim == 0) {
    x.set_empty();
    return;
  }

  // TODO: This is just an executable specification.
  //       Have to find a more efficient method.
  if (y.contains(x)) {
    x.set_empty();
    return;
  }

  Octagonal_Shape new_oct(space_dim, EMPTY);
  // We take a constraint of the octagon y at the time and we
  // consider its complementary. Then we intersect the union
  // of these complementary constraints with the octagon x.
  const Constraint_System& y_cs = y.constraints();
  for (Constraint_System::const_iterator i = y_cs.begin(),
         y_cs_end = y_cs.end(); i != y_cs_end; ++i) {
    const Constraint& c = *i;
    // If the octagon `x' is included the octagon defined by `c',
    // then `c' _must_ be skipped, as adding its complement to `x'
    // would result in the empty octagon, and as we would obtain
    // a result that is less precise than the difference.
    if (x.relation_with(c).implies(Poly_Con_Relation::is_included())) {
      continue;
    }
    Octagonal_Shape z = x;
    const Linear_Expression e(c.expression());
    z.add_constraint(e <= 0);
    if (!z.is_empty()) {
      new_oct.upper_bound_assign(z);
    }
    if (c.is_equality()) {
      z = x;
      z.add_constraint(e >= 0);
      if (!z.is_empty()) {
        new_oct.upper_bound_assign(z);
      }
    }
  }
  *this = new_oct;
  PPL_ASSERT(OK());
}

template <typename T>
bool
Octagonal_Shape<T>::simplify_using_context_assign(const Octagonal_Shape& y) {
  Octagonal_Shape& x = *this;
  const dimension_type dim = x.space_dimension();
  // Dimension-compatibility check.
  if (dim != y.space_dimension()) {
    throw_dimension_incompatible("simplify_using_context_assign(y)", y);
  }

  // Filter away the zero-dimensional case.
  if (dim == 0) {
    if (y.marked_empty()) {
      x.set_zero_dim_univ();
      return false;
    }
    else {
      return !x.marked_empty();
    }
  }

  // Filter away the case where `x' contains `y'
  // (this subsumes the case when `y' is empty).
  if (x.contains(y)) {
    Octagonal_Shape<T> res(dim, UNIVERSE);
    x.m_swap(res);
    return false;
  }

  // Filter away the case where `x' is empty.
  x.strong_closure_assign();
  if (x.marked_empty()) {
    // Search for a constraint of `y' that is not a tautology.
    dimension_type i;
    dimension_type j;
    // Prefer unary constraints.
    for (i = 0; i < 2*dim; i += 2) {
      // FIXME: if N is a float or bounded integer type, then
      // we also need to check that we are actually able to construct
      // a constraint inconsistent with respect to this one.
      // Use something like !is_maximal()?
      if (!is_plus_infinity(y.matrix_at(i, i + 1))) {
        j = i + 1;
        goto found;
      }
      // Use something like !is_maximal()?
      if (!is_plus_infinity(y.matrix_at(i + 1, i))) {
        j = i;
        ++i;
        goto found;
      }
    }
    // Then search binary constraints.
    // TODO: use better iteration scheme.
    for (i = 2; i < 2*dim; ++i) {
      for (j = 0; j < i; ++j) {
        // Use something like !is_maximal()?
        if (!is_plus_infinity(y.matrix_at(i, j))) {
          goto found;
        }
      }
    }

    // Not found: we were not able to build a constraint contradicting
    // one of the constraints in `y': `x' cannot be enlarged.
    return false;

  found:
    // Found: build a new OS contradicting the constraint found.
    PPL_ASSERT(i < dim && j < dim && i != j);
    Octagonal_Shape<T> res(dim, UNIVERSE);
    // FIXME: compute a proper contradicting constraint.
    PPL_DIRTY_TEMP(N, tmp);
    assign_r(tmp, 1, ROUND_UP);
    add_assign_r(tmp, tmp, y.matrix_at(i, j), ROUND_UP);
    // CHECKME: round down is really meant.
    neg_assign_r(res.matrix_at(j, i), tmp, ROUND_DOWN);
    PPL_ASSERT(!is_plus_infinity(res.matrix_at(j, i)));
    x.m_swap(res);
    return false;
  }

  // Here `x' and `y' are not empty and strongly closed;
  // also, `x' does not contain `y'.
  // Let `target' be the intersection of `x' and `y'.
  Octagonal_Shape<T> target = x;
  target.intersection_assign(y);
  const bool bool_result = !target.is_empty();

  // Compute redundancy information for x and ...
  // TODO: provide a nicer data structure for redundancy.
  std::vector<Bit_Row> x_non_redundant;
  x.non_redundant_matrix_entries(x_non_redundant);
  // ... count the non-redundant constraints.
  dimension_type x_num_non_redundant = 0;
  for (size_t i = x_non_redundant.size(); i-- > 0 ; ) {
    x_num_non_redundant += x_non_redundant[i].count_ones();
  }
  PPL_ASSERT(x_num_non_redundant > 0);

  // Let `yy' be a copy of `y': we will keep adding to `yy'
  // the non-redundant constraints of `x',
  // stopping as soon as `yy' becomes equal to `target'.
  Octagonal_Shape<T> yy = y;

  // The constraints added to `yy' will be recorded in `res' ...
  Octagonal_Shape<T> res(dim, UNIVERSE);
  // ... and we will count them too.
  dimension_type res_num_non_redundant = 0;

  // Compute leader information for `x'.
  std::vector<dimension_type> x_leaders;
  x.compute_leaders(x_leaders);

  // First go through the unary equality constraints.
  // Find the leader of the singular equivalence class (it is even!).
  dimension_type sing_leader;
  for (sing_leader = 0; sing_leader < 2*dim; sing_leader += 2) {
    if (sing_leader == x_leaders[sing_leader]) {
      const N& x_s_ss = x.matrix_at(sing_leader, sing_leader + 1);
      const N& x_ss_s = x.matrix_at(sing_leader + 1, sing_leader);
      if (is_additive_inverse(x_s_ss, x_ss_s)) {
        // Singular leader found.
        break;
      }
    }
  }

  // Unary equalities have `sing_leader' as a leader.
  for (dimension_type i = sing_leader; i < 2*dim; i += 2) {
    if (x_leaders[i] != sing_leader) {
      continue;
    }
    // Found a unary equality constraint:
    // see if any of the two inequalities have to be added.
    const N& x_i_ii = x.matrix_at(i, i + 1);
    N& yy_i_ii = yy.matrix_at(i, i + 1);
    if (x_i_ii < yy_i_ii) {
      // The \leq inequality is not implied by context.
      res.matrix_at(i, i + 1) = x_i_ii;
      ++res_num_non_redundant;
      // Tighten context `yy' using the newly added constraint.
      yy_i_ii = x_i_ii;
      yy.reset_strongly_closed();
    }
    const N& x_ii_i = x.matrix_at(i + 1, i);
    N& yy_ii_i = yy.matrix_at(i + 1, i);
    if (x_ii_i < yy_ii_i) {
      // The \geq inequality is not implied by context.
      res.matrix_at(i + 1, i) = x_ii_i;
      ++res_num_non_redundant;
      // Tighten context `yy' using the newly added constraint.
      yy_ii_i = x_ii_i;
      yy.reset_strongly_closed();
    }
    // Restore strong closure, if it was lost.
    if (!yy.marked_strongly_closed()) {
      Variable var_i(i/2);
      yy.incremental_strong_closure_assign(var_i);
      if (target.contains(yy)) {
        // Target reached: swap `x' and `res' if needed.
        if (res_num_non_redundant < x_num_non_redundant) {
          res.reset_strongly_closed();
          x.m_swap(res);
        }
        return bool_result;
      }
    }
  }

  // Go through the binary equality constraints.
  for (dimension_type i = 0; i < 2*dim; ++i) {
    const dimension_type j = x_leaders[i];
    if (j == i || j == sing_leader) {
      continue;
    }
    const N& x_i_j = x.matrix_at(i, j);
    PPL_ASSERT(!is_plus_infinity(x_i_j));
    N& yy_i_j = yy.matrix_at(i, j);
    if (x_i_j < yy_i_j) {
      res.matrix_at(i, j) = x_i_j;
      ++res_num_non_redundant;
      // Tighten context `yy' using the newly added constraint.
      yy_i_j = x_i_j;
      yy.reset_strongly_closed();
    }
    const N& x_j_i = x.matrix_at(j, i);
    N& yy_j_i = yy.matrix_at(j, i);
    PPL_ASSERT(!is_plus_infinity(x_j_i));
    if (x_j_i < yy_j_i) {
      res.matrix_at(j, i) = x_j_i;
      ++res_num_non_redundant;
      // Tighten context `yy' using the newly added constraint.
      yy_j_i = x_j_i;
      yy.reset_strongly_closed();
    }
    // Restore strong closure, if it was lost.
    if (!yy.marked_strongly_closed()) {
      Variable var_j(j/2);
      yy.incremental_strong_closure_assign(var_j);
      if (target.contains(yy)) {
        // Target reached: swap `x' and `res' if needed.
        if (res_num_non_redundant < x_num_non_redundant) {
          res.reset_strongly_closed();
          x.m_swap(res);
        }
        return bool_result;
      }
    }
  }

  // Finally go through the (proper) inequality constraints:
  // both indices i and j should be leaders.
  // FIXME: improve iteration scheme (are we doing twice the work?)
  for (dimension_type i = 0; i < 2*dim; ++i) {
    if (i != x_leaders[i]) {
      continue;
    }
    const Bit_Row& x_non_redundant_i = x_non_redundant[i];
    for (dimension_type j = 0; j < 2*dim; ++j) {
      if (j != x_leaders[j]) {
        continue;
      }
      if (i >= j) {
        if (!x_non_redundant_i[j]) {
          continue;
        }
      }
      else if (!x_non_redundant[j][i]) {
        continue;
      }
      N& yy_i_j = yy.matrix_at(i, j);
      const N& x_i_j = x.matrix_at(i, j);
      if (x_i_j < yy_i_j) {
        res.matrix_at(i, j) = x_i_j;
        ++res_num_non_redundant;
        // Tighten context `yy' using the newly added constraint.
        yy_i_j = x_i_j;
        yy.reset_strongly_closed();
        Variable var(i/2);
        yy.incremental_strong_closure_assign(var);
        if (target.contains(yy)) {
          // Target reached: swap `x' and `res' if needed.
          if (res_num_non_redundant < x_num_non_redundant) {
            res.reset_strongly_closed();
            x.m_swap(res);
          }
          return bool_result;
        }
      }
    }
  }
  // This point should be unreachable.
  PPL_UNREACHABLE;
  return false;
}

template <typename T>
void
Octagonal_Shape<T>::add_space_dimensions_and_embed(dimension_type m) {
  // Adding no dimensions is a no-op.
  if (m == 0) {
    return;
  }

  const dimension_type new_dim = space_dim + m;
  const bool was_zero_dim_univ = !marked_empty() && space_dim == 0;

  // To embed an n-dimension space octagon in a (n + m)-dimension space,
  // we just add `m' variables in the matrix of constraints.
  matrix.grow(new_dim);
  space_dim = new_dim;
  // If `*this' was the zero-dim space universe octagon,
  // then we can set the strongly closure flag.
  if (was_zero_dim_univ) {
    set_strongly_closed();
  }

  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::add_space_dimensions_and_project(dimension_type m) {
  // Adding no dimensions is a no-op.
  if (m == 0) {
    return;
  }

  const dimension_type n = matrix.num_rows();

  // To project an n-dimension space OS in a (space_dim + m)-dimension space,
  // we just add `m' columns and rows in the matrix of constraints.
  add_space_dimensions_and_embed(m);
  // We insert 0 where it needs.
  // Attention: now num_rows of matrix is update!
  for (typename OR_Matrix<N>::row_iterator i = matrix.row_begin() + n,
         matrix_row_end =  matrix.row_end(); i != matrix_row_end; i += 2) {
    typename OR_Matrix<N>::row_reference_type x_i = *i;
    typename OR_Matrix<N>::row_reference_type x_ci = *(i + 1);
    const dimension_type ind = i.index();
    assign_r(x_i[ind + 1], 0, ROUND_NOT_NEEDED);
    assign_r(x_ci[ind], 0, ROUND_NOT_NEEDED);
  }

  if (marked_strongly_closed()) {
    reset_strongly_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::remove_space_dimensions(const Variables_Set& vars) {
  // The removal of no dimensions from any octagon is a no-op.
  // Note that this case also captures the only legal removal of
  // dimensions from a octagon in a 0-dim space.
  if (vars.empty()) {
    PPL_ASSERT(OK());
    return;
  }

  // Dimension-compatibility check.
  const dimension_type min_space_dim = vars.space_dimension();
  if (space_dim < min_space_dim) {
    throw_dimension_incompatible("remove_space_dimensions(vs)", min_space_dim);
  }

  const dimension_type new_space_dim = space_dim - vars.size();

  strong_closure_assign();
  // When removing _all_ dimensions from an octagon,
  // we obtain the zero-dimensional octagon.
  if (new_space_dim == 0) {
    matrix.shrink(0);
    if (!marked_empty()) {
      // We set the zero_dim_univ flag.
      set_zero_dim_univ();
    }
    space_dim = 0;
    PPL_ASSERT(OK());
    return;
  }

  // We consider each variable and we check if it has to be removed.
  // If it has to be removed, we pass to the next one, then we will
  // overwrite its representation in the matrix.
  typedef typename OR_Matrix<N>::element_iterator Elem_Iter;
  typedef typename std::iterator_traits<Elem_Iter>::difference_type diff_t;

  dimension_type first = *vars.begin();
  const dimension_type first_size = 2 * first * (first + 1);
  Elem_Iter iter = matrix.element_begin() + static_cast<diff_t>(first_size);

  for (dimension_type i = first + 1; i < space_dim; ++i) {
    if (vars.count(i) == 0) {
      typename OR_Matrix<N>::row_iterator row_iter = matrix.row_begin() + 2*i;
      typename OR_Matrix<N>::row_reference_type row_ref = *row_iter;
      typename OR_Matrix<N>::row_reference_type row_ref1 = *(++row_iter);
      // Beware: first we shift the cells corresponding to the first
      // row of variable(j), then we shift the cells corresponding to the
      // second row. We recall that every variable is represented
      // in the `matrix' by two rows and two columns.
      for (dimension_type j = 0; j <= i; ++j) {
        if (vars.count(j) == 0) {
          assign_or_swap(*(iter++), row_ref[2*j]);
          assign_or_swap(*(iter++), row_ref[2*j + 1]);
        }
      }
      for (dimension_type j = 0; j <= i; ++j) {
        if (vars.count(j) == 0) {
          assign_or_swap(*(iter++), row_ref1[2*j]);
          assign_or_swap(*(iter++), row_ref1[2*j + 1]);
        }
      }
    }
  }
  // Update the space dimension.
  matrix.shrink(new_space_dim);
  space_dim = new_space_dim;
  PPL_ASSERT(OK());
}

template <typename T>
template <typename Partial_Function>
void
Octagonal_Shape<T>::map_space_dimensions(const Partial_Function& pfunc) {
  if (space_dim == 0) {
    return;
  }

  if (pfunc.has_empty_codomain()) {
    // All dimensions vanish: the octagon becomes zero_dimensional.
    remove_higher_space_dimensions(0);
    return;
  }

  const dimension_type new_space_dim = pfunc.max_in_codomain() + 1;
  // If we are going to actually reduce the space dimension,
  // then shortest-path closure is required to keep precision.
  if (new_space_dim < space_dim) {
    strong_closure_assign();
  }
  // If the octagon is empty, then it is sufficient to adjust
  // the space dimension of the octagon.
  if (marked_empty()) {
    remove_higher_space_dimensions(new_space_dim);
    return;
  }

  // We create a new matrix with the new space dimension.
  OR_Matrix<N> x(new_space_dim);

  typedef typename OR_Matrix<N>::row_iterator row_iterator;
  typedef typename OR_Matrix<N>::row_reference_type row_reference;

  row_iterator m_begin = x.row_begin();

  for (row_iterator i_iter = matrix.row_begin(), i_end = matrix.row_end();
       i_iter != i_end; i_iter += 2) {
    dimension_type new_i;
    dimension_type i = i_iter.index()/2;
    // We copy and place in the position into `x' the only cells of
    // the `matrix' that refer to both mapped variables,
    // the variable `i' and `j'.
    if (pfunc.maps(i, new_i)) {
      row_reference r_i = *i_iter;
      row_reference r_ii = *(i_iter + 1);
      dimension_type double_new_i = 2*new_i;
      row_iterator x_iter = m_begin + double_new_i;
      row_reference x_i = *x_iter;
      row_reference x_ii = *(x_iter + 1);
      for (dimension_type j = 0; j <= i; ++j) {
        dimension_type new_j;
        // If also the second variable is mapped, we work.
        if (pfunc.maps(j, new_j)) {
          dimension_type dj = 2*j;
          dimension_type double_new_j = 2*new_j;
          // Mapped the constraints, exchanging the indexes.
          // Attention: our matrix is pseudo-triangular.
          // If new_j > new_i, we must consider, as rows, the rows of
          // the variable new_j, and not of new_i ones.
          if (new_i >= new_j) {
            assign_or_swap(x_i[double_new_j], r_i[dj]);
            assign_or_swap(x_ii[double_new_j], r_ii[dj]);
            assign_or_swap(x_ii[double_new_j + 1], r_ii[dj + 1]);
            assign_or_swap(x_i[double_new_j + 1], r_i[dj + 1]);
          }
          else {
            row_iterator x_j_iter = m_begin + double_new_j;
            row_reference x_j = *x_j_iter;
            row_reference x_jj = *(x_j_iter + 1);
            assign_or_swap(x_jj[double_new_i + 1], r_i[dj]);
            assign_or_swap(x_jj[double_new_i], r_ii[dj]);
            assign_or_swap(x_j[double_new_i + 1], r_i[dj + 1]);
            assign_or_swap(x_j[double_new_i], r_ii[dj + 1]);
          }

        }
      }
    }
  }

  using std::swap;
  swap(matrix, x);
  space_dim = new_space_dim;
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::intersection_assign(const Octagonal_Shape& y) {
  // Dimension-compatibility check.
  if (space_dim != y.space_dim) {
    throw_dimension_incompatible("intersection_assign(y)", y);
  }

  // If one of the two octagons is empty, the intersection is empty.
  if (marked_empty()) {
    return;
  }
  if (y.marked_empty()) {
    set_empty();
    return;
  }
  // If both octagons are zero-dimensional,then at this point
  // they are necessarily non-empty,
  // so that their intersection is non-empty too.
  if (space_dim == 0) {
    return;
  }
  // To intersect two octagons we compare the constraints
  // and we choose the less values.
  bool changed = false;

  typename OR_Matrix<N>::const_element_iterator j = y.matrix.element_begin();
  for (typename OR_Matrix<N>::element_iterator i = matrix.element_begin(),
         matrix_element_end = matrix.element_end();
       i != matrix_element_end;
       ++i, ++j) {
    N& elem = *i;
    const N& y_elem = *j;
    if (y_elem < elem) {
      elem = y_elem;
      changed = true;
    }
  }

  // This method not preserve the closure.
  if (changed && marked_strongly_closed()) {
    reset_strongly_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
template <typename Iterator>
void
Octagonal_Shape<T>::CC76_extrapolation_assign(const Octagonal_Shape& y,
                                              Iterator first, Iterator last,
                                              unsigned* tp) {
  // Dimension-compatibility check.
  if (space_dim != y.space_dim) {
    throw_dimension_incompatible("CC76_extrapolation_assign(y)", y);
  }

  // Assume `y' is contained in or equal to `*this'.
  PPL_EXPECT_HEAVY(copy_contains(*this, y));

  // If both octagons are zero-dimensional,
  // since `*this' contains `y', we simply return `*this'.
  if (space_dim == 0) {
    return;
  }

  strong_closure_assign();
  // If `*this' is empty, since `*this' contains `y', `y' is empty too.
  if (marked_empty()) {
    return;
  }
  y.strong_closure_assign();
  // If `y' is empty, we return.
  if (y.marked_empty()) {
    return;
  }

  // If there are tokens available, work on a temporary copy.
  if (tp != 0 && *tp > 0) {
    Octagonal_Shape x_tmp(*this);
    x_tmp.CC76_extrapolation_assign(y, first, last, 0);
    // If the widening was not precise, use one of the available tokens.
    if (!contains(x_tmp)) {
      --(*tp);
    }
    return;
  }

  // Compare each constraint in `y' to the corresponding one in `*this'.
  // The constraint in `*this' is kept as is if it is stronger than or
  // equal to the constraint in `y'; otherwise, the inhomogeneous term
  // of the constraint in `*this' is further compared with elements taken
  // from a sorted container (the stop-points, provided by the user), and
  // is replaced by the first entry, if any, which is greater than or equal
  // to the inhomogeneous term. If no such entry exists, the constraint
  // is removed altogether.
  typename OR_Matrix<N>::const_element_iterator j = y.matrix.element_begin();
  for (typename OR_Matrix<N>::element_iterator i = matrix.element_begin(),
         matrix_element_end = matrix.element_end();
       i != matrix_element_end;
       ++i, ++j) {
    const N& y_elem = *j;
    N& elem = *i;
    if (y_elem < elem) {
      Iterator k = std::lower_bound(first, last, elem);
      if (k != last) {
        if (elem < *k) {
          assign_r(elem, *k, ROUND_UP);
        }
      }
      else {
        assign_r(elem, PLUS_INFINITY, ROUND_NOT_NEEDED);
      }
    }
  }

  reset_strongly_closed();
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>
::get_limiting_octagon(const Constraint_System& cs,
                       Octagonal_Shape& limiting_octagon) const {
  const dimension_type cs_space_dim = cs.space_dimension();
  // Private method: the caller has to ensure the following.
  PPL_ASSERT(cs_space_dim <= space_dim);

  strong_closure_assign();
  bool is_oct_changed = false;

  // Allocate temporaries outside of the loop.
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  PPL_DIRTY_TEMP_COEFFICIENT(term);
  PPL_DIRTY_TEMP(N, d);

  for (Constraint_System::const_iterator cs_i = cs.begin(),
         cs_end = cs.end(); cs_i != cs_end; ++cs_i) {
    const Constraint& c = *cs_i;
    dimension_type num_vars = 0;
    dimension_type i = 0;
    dimension_type j = 0;
    // Constraints that are not octagonal differences are ignored.
    if (!Octagonal_Shape_Helper
      ::extract_octagonal_difference(c, cs_space_dim, num_vars, i, j,
                                     coeff, term)) {
      continue;
    }

    typedef typename OR_Matrix<N>::const_row_iterator Row_iterator;
    typedef typename OR_Matrix<N>::const_row_reference_type Row_reference;
    typedef typename OR_Matrix<N>::row_iterator row_iterator;
    typedef typename OR_Matrix<N>::row_reference_type row_reference;
    Row_iterator m_begin = matrix.row_begin();
    // Select the cell to be modified for the "<=" part of the constraint.
    Row_iterator i_iter = m_begin + i;
    Row_reference m_i = *i_iter;
    OR_Matrix<N>& lo_mat = limiting_octagon.matrix;
    row_iterator lo_iter = lo_mat.row_begin() + i;
    row_reference lo_m_i = *lo_iter;
    N& lo_m_i_j = lo_m_i[j];
    if (coeff < 0) {
      neg_assign(coeff);
    }
    // Compute the bound for `m_i_j', rounding towards plus infinity.
    div_round_up(d, term, coeff);
    if (m_i[j] <= d) {
      if (c.is_inequality()) {
        if (lo_m_i_j > d) {
          lo_m_i_j = d;
          is_oct_changed = true;
        }
        else {
          // Select the right row of the cell.
          if (i % 2 == 0) {
            ++i_iter;
            ++lo_iter;
          }
          else {
            --i_iter;
            --lo_iter;
          }
          Row_reference m_ci = *i_iter;
          row_reference lo_m_ci = *lo_iter;
          // Select the right column of the cell.
          using namespace Implementation::Octagonal_Shapes;
          dimension_type cj = coherent_index(j);
          N& lo_m_ci_cj = lo_m_ci[cj];
          neg_assign(term);
          div_round_up(d, term, coeff);
          if (m_ci[cj] <= d && lo_m_ci_cj > d) {
            lo_m_ci_cj = d;
            is_oct_changed = true;
          }
        }
      }
    }
  }
  // In general, adding a constraint does not preserve the strongly
  // closure of the octagon.
  if (is_oct_changed && limiting_octagon.marked_strongly_closed()) {
    limiting_octagon.reset_strongly_closed();
  }
}

template <typename T>
void
Octagonal_Shape<T>
::limited_CC76_extrapolation_assign(const Octagonal_Shape& y,
                                    const Constraint_System& cs,
                                    unsigned* tp) {

  // Dimension-compatibility check.
  if (space_dim != y.space_dim) {
    throw_dimension_incompatible("limited_CC76_extrapolation_assign(y, cs)",
                                 y);
  }
  // `cs' must be dimension-compatible with the two octagons.
  const dimension_type cs_space_dim = cs.space_dimension();
  if (space_dim < cs_space_dim) {
    throw_constraint_incompatible("limited_CC76_extrapolation_assign(y, cs)");
  }

  // Strict inequalities not allowed.
  if (cs.has_strict_inequalities()) {
    throw_constraint_incompatible("limited_CC76_extrapolation_assign(y, cs)");
  }

  // The limited CC76-extrapolation between two octagons in a
  // zero-dimensional space is a octagon in a zero-dimensional
  // space, too.
  if (space_dim == 0) {
    return;
  }

  // Assume `y' is contained in or equal to `*this'.
  PPL_EXPECT_HEAVY(copy_contains(*this, y));

  // If `*this' is empty, since `*this' contains `y', `y' is empty too.
  if (marked_empty()) {
    return;
  }

  // If `y' is empty, we return.
  if (y.marked_empty()) {
    return;
  }

  Octagonal_Shape limiting_octagon(space_dim, UNIVERSE);
  get_limiting_octagon(cs, limiting_octagon);
  CC76_extrapolation_assign(y, tp);
  intersection_assign(limiting_octagon);
}

template <typename T>
void
Octagonal_Shape<T>::BHMZ05_widening_assign(const Octagonal_Shape& y,
                                           unsigned* tp) {
  // Dimension-compatibility check.
  if (space_dim != y.space_dim) {
    throw_dimension_incompatible("BHMZ05_widening_assign(y)", y);
  }

  // Assume `y' is contained in or equal to `*this'.
  PPL_EXPECT_HEAVY(copy_contains(*this, y));

  // Compute the affine dimension of `y'.
  const dimension_type y_affine_dim = y.affine_dimension();
  // If the affine dimension of `y' is zero, then either `y' is
  // zero-dimensional, or it is empty, or it is a singleton.
  // In all cases, due to the inclusion hypothesis, the result is `*this'.
  if (y_affine_dim == 0) {
    return;
  }

  // If the affine dimension has changed, due to the inclusion hypothesis,
  // the result is `*this'.
  const dimension_type x_affine_dim = affine_dimension();
  PPL_ASSERT(x_affine_dim >= y_affine_dim);
  if (x_affine_dim != y_affine_dim) {
    return;
  }
  // If there are tokens available, work on a temporary copy.
  if (tp != 0 && *tp > 0) {
    Octagonal_Shape x_tmp(*this);
    x_tmp.BHMZ05_widening_assign(y, 0);
    // If the widening was not precise, use one of the available tokens.
    if (!contains(x_tmp)) {
      --(*tp);
    }
    return;
  }

  // Here no token is available.
  PPL_ASSERT(marked_strongly_closed() && y.marked_strongly_closed());
  // Minimize `y'.
  y.strong_reduction_assign();

  // Extrapolate unstable bounds.
  typename OR_Matrix<N>::const_element_iterator j = y.matrix.element_begin();
  for (typename OR_Matrix<N>::element_iterator i = matrix.element_begin(),
       matrix_element_end = matrix.element_end();
       i != matrix_element_end;
       ++i, ++j) {
    N& elem = *i;
      // Note: in the following line the use of `!=' (as opposed to
      // the use of `<' that would seem -but is not- equivalent) is
      // intentional.
    if (*j != elem) {
      assign_r(elem, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
  }
  reset_strongly_closed();
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>
::limited_BHMZ05_extrapolation_assign(const Octagonal_Shape& y,
                                      const Constraint_System& cs,
                                      unsigned* tp) {

  // Dimension-compatibility check.
  if (space_dim != y.space_dim) {
    throw_dimension_incompatible("limited_BHMZ05_extrapolation_assign(y, cs)",
                                 y);
  }

  // `cs' must be dimension-compatible with the two octagons.
  const dimension_type cs_space_dim = cs.space_dimension();
  if (space_dim < cs_space_dim) {
    throw_constraint_incompatible("limited_CH78_extrapolation_assign(y, cs)");
  }

  // Strict inequalities not allowed.
  if (cs.has_strict_inequalities()) {
    throw_constraint_incompatible("limited_CH78_extrapolation_assign(y, cs)");
  }

  // The limited BHMZ05-extrapolation between two octagons in a
  // zero-dimensional space is a octagon in a zero-dimensional
  // space, too.
  if (space_dim == 0) {
    return;
  }

  // Assume `y' is contained in or equal to `*this'.
  PPL_EXPECT_HEAVY(copy_contains(*this, y));

  // If `*this' is empty, since `*this' contains `y', `y' is empty too.
  if (marked_empty()) {
    return;
  }
  // If `y' is empty, we return.
  if (y.marked_empty()) {
    return;
  }

  Octagonal_Shape limiting_octagon(space_dim, UNIVERSE);
  get_limiting_octagon(cs, limiting_octagon);
  BHMZ05_widening_assign(y, tp);
  intersection_assign(limiting_octagon);
}

template <typename T>
void
Octagonal_Shape<T>::CC76_narrowing_assign(const Octagonal_Shape& y) {
  // Dimension-compatibility check.
  if (space_dim != y.space_dim) {
    throw_dimension_incompatible("CC76_narrowing_assign(y)", y);
  }

  // Assume `*this' is contained in or equal to `y'.
  PPL_EXPECT_HEAVY(copy_contains(y, *this));

  // If both octagons are zero-dimensional, since `*this' contains `y',
  // we simply return '*this'.
  if (space_dim == 0) {
    return;
  }

  y.strong_closure_assign();
  // If `y' is empty, since `y' contains `*this', `*this' is empty too.
  if (y.marked_empty()) {
    return;
  }

  strong_closure_assign();
  // If `*this' is empty, we return.
  if (marked_empty()) {
    return;
  }

  // We consider a constraint of `*this', if its value is `plus_infinity',
  // we take the value of the corresponding constraint of `y'.
  bool is_oct_changed = false;
  typename OR_Matrix<N>::const_element_iterator j = y.matrix.element_begin();
  for (typename OR_Matrix<N>::element_iterator i = matrix.element_begin(),
         matrix_element_end = matrix.element_end();
       i != matrix_element_end;
       ++i, ++j) {
    if (!is_plus_infinity(*i)
        && !is_plus_infinity(*j)
        && *i != *j) {
      *i = *j;
      is_oct_changed = true;
    }
  }

  if (is_oct_changed && marked_strongly_closed()) {
    reset_strongly_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>
::deduce_v_pm_u_bounds(const dimension_type v_id,
                       const dimension_type last_id,
                       const Linear_Expression& sc_expr,
                       Coefficient_traits::const_reference sc_denom,
                       const N& ub_v) {
  // Private method: the caller has to ensure the following.
  PPL_ASSERT(sc_denom > 0);
  PPL_ASSERT(!is_plus_infinity(ub_v));

  PPL_DIRTY_TEMP(mpq_class, mpq_sc_denom);
  assign_r(mpq_sc_denom, sc_denom, ROUND_NOT_NEEDED);

  // No need to consider indices greater than `last_id'.
  const dimension_type n_v = 2*v_id;
  typename OR_Matrix<N>::row_reference_type m_cv = matrix[n_v + 1];

  // Speculatively allocate temporaries out of the loop.
  PPL_DIRTY_TEMP(N, half);
  PPL_DIRTY_TEMP(mpq_class, minus_lb_u);
  PPL_DIRTY_TEMP(mpq_class, q);
  PPL_DIRTY_TEMP(mpq_class, minus_q);
  PPL_DIRTY_TEMP(mpq_class, ub_u);
  PPL_DIRTY_TEMP(mpq_class, lb_u);
  PPL_DIRTY_TEMP(N, up_approx);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_expr_u);

  for (Linear_Expression::const_iterator u = sc_expr.begin(),
      u_end = sc_expr.lower_bound(Variable(last_id + 1)); u != u_end; ++u) {
    const dimension_type u_id = u.variable().id();
    // Skip the case when `u_id == v_id'.
    if (u_id == v_id) {
      continue;
    }
    const Coefficient& expr_u = *u;

    const dimension_type n_u = u_id*2;
    // If `expr_u' is positive, we can improve `v - u'.
    if (expr_u > 0) {
      if (expr_u >= sc_denom) {
        // Here q >= 1: deducing `v - u <= ub_v - ub_u'.
        // We avoid to check if `ub_u' is plus infinity, because
        // it is used for the computation of `ub_v'.
        // Let half = m_cu_u / 2.
        div_2exp_assign_r(half, matrix[n_u + 1][n_u], 1, ROUND_UP);
        N& m_v_minus_u = (n_v < n_u) ? matrix[n_u][n_v] : m_cv[n_u + 1];
        sub_assign_r(m_v_minus_u, ub_v, half, ROUND_UP);
      }
      else {
        // Here 0 < q < 1.
        typename OR_Matrix<N>::row_reference_type m_u = matrix[n_u];
        const N& m_u_cu = m_u[n_u + 1];
        if (!is_plus_infinity(m_u_cu)) {
          // Let `ub_u' and `lb_u' be the known upper and lower bound
          // for `u', respectively. The upper bound for `v - u' is
          // computed as `ub_v - (q * ub_u + (1-q) * lb_u)',
          // i.e., `ub_v + (-lb_u) - q * (ub_u + (-lb_u))'.
          assign_r(minus_lb_u, m_u_cu, ROUND_NOT_NEEDED);
          div_2exp_assign_r(minus_lb_u, minus_lb_u, 1, ROUND_NOT_NEEDED);
          assign_r(q, expr_u, ROUND_NOT_NEEDED);
          div_assign_r(q, q, mpq_sc_denom, ROUND_NOT_NEEDED);
          assign_r(ub_u, matrix[n_u + 1][n_u], ROUND_NOT_NEEDED);
          div_2exp_assign_r(ub_u, ub_u, 1, ROUND_NOT_NEEDED);
          // Compute `ub_u - lb_u'.
          add_assign_r(ub_u, ub_u, minus_lb_u, ROUND_NOT_NEEDED);
          // Compute `(-lb_u) - q * (ub_u - lb_u)'.
          sub_mul_assign_r(minus_lb_u, q, ub_u, ROUND_NOT_NEEDED);
          assign_r(up_approx, minus_lb_u, ROUND_UP);
          // Deducing `v - u <= ub_v - (q * ub_u + (1-q) * lb_u)'.
          N& m_v_minus_u = (n_v < n_u) ? m_u[n_v] : m_cv[n_u + 1];
          add_assign_r(m_v_minus_u, ub_v, up_approx, ROUND_UP);
        }
      }
    }
    else {
      PPL_ASSERT(expr_u < 0);
      // If `expr_u' is negative, we can improve `v + u'.
      neg_assign(minus_expr_u, expr_u);
      if (minus_expr_u >= sc_denom) {
        // Here q <= -1: Deducing `v + u <= ub_v + lb_u'.
        // We avoid to check if `lb_u' is plus infinity, because
        // it is used for the computation of `ub_v'.
        // Let half = m_u_cu / 2.
        div_2exp_assign_r(half, matrix[n_u][n_u + 1], 1, ROUND_UP);
        N& m_v_plus_u = (n_v < n_u) ? matrix[n_u + 1][n_v] : m_cv[n_u];
        sub_assign_r(m_v_plus_u, ub_v, half, ROUND_UP);
      }
      else {
        // Here -1 < q < 0.
        typename OR_Matrix<N>::row_reference_type m_cu = matrix[n_u + 1];
        const N& m_cu_u = m_cu[n_u];
        if (!is_plus_infinity(m_cu_u)) {
          // Let `ub_u' and `lb_u' be the known upper and lower bound
          // for `u', respectively. The upper bound for `v + u' is
          // computed as `ub_v + ((-q) * lb_u + (1 + q) * ub_u)',
          // i.e., `ub_v + ub_u + (-q) * (lb_u - ub_u)'.
          assign_r(ub_u, m_cu[n_u], ROUND_NOT_NEEDED);
          div_2exp_assign_r(ub_u, ub_u, 1, ROUND_NOT_NEEDED);
          assign_r(minus_q, minus_expr_u, ROUND_NOT_NEEDED);
          div_assign_r(minus_q, minus_q, mpq_sc_denom, ROUND_NOT_NEEDED);
          assign_r(lb_u, matrix[n_u][n_u + 1], ROUND_NOT_NEEDED);
          div_2exp_assign_r(lb_u, lb_u, 1, ROUND_NOT_NEEDED);
          neg_assign_r(lb_u, lb_u, ROUND_NOT_NEEDED);
          // Compute `lb_u - ub_u'.
          sub_assign_r(lb_u, lb_u, ub_u, ROUND_NOT_NEEDED);
          // Compute `ub_u + (-q) * (lb_u - ub_u)'.
          add_mul_assign_r(ub_u, minus_q, lb_u, ROUND_NOT_NEEDED);
          assign_r(up_approx, ub_u, ROUND_UP);
          // Deducing `v + u <= ub_v + ((-q) * lb_u + (1 + q) * ub_u)'.
          N& m_v_plus_u = (n_v < n_u) ? m_cu[n_v] : m_cv[n_u];
          add_assign_r(m_v_plus_u, ub_v, up_approx, ROUND_UP);
        }
      }
    }
  }
}

template <typename T>
void
Octagonal_Shape<T>
::deduce_minus_v_pm_u_bounds(const dimension_type v_id,
                             const dimension_type last_id,
                             const Linear_Expression& sc_expr,
                             Coefficient_traits::const_reference sc_denom,
                             const N& minus_lb_v) {
  // Private method: the caller has to ensure the following.
  PPL_ASSERT(sc_denom > 0);
  PPL_ASSERT(!is_plus_infinity(minus_lb_v));

  PPL_DIRTY_TEMP(mpq_class, mpq_sc_denom);
  assign_r(mpq_sc_denom, sc_denom, ROUND_NOT_NEEDED);

  // No need to consider indices greater than `last_id'.
  const dimension_type n_v = 2*v_id;
  typename OR_Matrix<N>::row_reference_type m_v = matrix[n_v];

  // Speculatively allocate temporaries out of the loop.
  PPL_DIRTY_TEMP(N, half);
  PPL_DIRTY_TEMP(mpq_class, ub_u);
  PPL_DIRTY_TEMP(mpq_class, q);
  PPL_DIRTY_TEMP(mpq_class, minus_lb_u);
  PPL_DIRTY_TEMP(N, up_approx);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_expr_u);

  for (Linear_Expression::const_iterator u = sc_expr.begin(),
      u_end = sc_expr.lower_bound(Variable(last_id + 1)); u != u_end; ++u) {
    const dimension_type u_id = u.variable().id();
    // Skip the case when `u_id == v_id'.
    if (u_id == v_id) {
      continue;
    }
    const Coefficient& expr_u = *u;

    const dimension_type n_u = u_id*2;
    // If `expr_u' is positive, we can improve `-v + u'.
    if (expr_u > 0) {
      if (expr_u >= sc_denom) {
        // Here q >= 1: deducing `-v + u <= lb_u - lb_v',
        // i.e., `u - v <= (-lb_v) - (-lb_u)'.
        // We avoid to check if `lb_u' is plus infinity, because
        // it is used for the computation of `lb_v'.
        // Let half = m_u_cu / 2.
        div_2exp_assign_r(half, matrix[n_u][n_u + 1], 1, ROUND_UP);
        N& m_u_minus_v = (n_v < n_u) ? matrix[n_u + 1][n_v + 1] : m_v[n_u];
        sub_assign_r(m_u_minus_v, minus_lb_v, half, ROUND_UP);
      }
      else {
        // Here 0 < q < 1.
        typename OR_Matrix<N>::row_reference_type m_cu = matrix[n_u + 1];
        const N& m_cu_u = m_cu[n_u];
        if (!is_plus_infinity(m_cu_u)) {
          // Let `ub_u' and `lb_u' be the known upper and lower bound
          // for `u', respectively. The upper bound for `u - v' is
          // computed as `(q * lb_u + (1-q) * ub_u) - lb_v',
          // i.e., `ub_u - q * (ub_u + (-lb_u)) + minus_lb_v'.
          assign_r(ub_u, m_cu[n_u], ROUND_NOT_NEEDED);
          div_2exp_assign_r(ub_u, ub_u, 1, ROUND_NOT_NEEDED);
          assign_r(q, expr_u, ROUND_NOT_NEEDED);
          div_assign_r(q, q, mpq_sc_denom, ROUND_NOT_NEEDED);
          assign_r(minus_lb_u, matrix[n_u][n_u + 1], ROUND_NOT_NEEDED);
          div_2exp_assign_r(minus_lb_u, minus_lb_u, 1, ROUND_NOT_NEEDED);
          // Compute `ub_u - lb_u'.
          add_assign_r(minus_lb_u, ub_u, minus_lb_u, ROUND_NOT_NEEDED);
          // Compute `ub_u - q * (ub_u - lb_u)'.
          sub_mul_assign_r(ub_u, q, minus_lb_u, ROUND_NOT_NEEDED);
          assign_r(up_approx, ub_u, ROUND_UP);
          // Deducing `u - v <= -lb_v - (q * lb_u + (1-q) * ub_u)'.
          N& m_u_minus_v = (n_v < n_u) ? m_cu[n_v + 1] : m_v[n_u];
          add_assign_r(m_u_minus_v, minus_lb_v, up_approx, ROUND_UP);
        }
      }
    }
    else {
      PPL_ASSERT(expr_u < 0);
      // If `expr_u' is negative, we can improve `-v - u'.
      neg_assign(minus_expr_u, expr_u);
      if (minus_expr_u >= sc_denom) {
        // Here q <= -1: Deducing `-v - u <= -lb_v - ub_u'.
        // We avoid to check if `ub_u' is plus infinity, because
        // it is used for the computation of `lb_v'.
        // Let half = m_cu_u / 2.
        div_2exp_assign_r(half, matrix[n_u + 1][n_u], 1, ROUND_UP);
        N& m_minus_v_minus_u = (n_v < n_u)
          ? matrix[n_u][n_v + 1]
          : m_v[n_u + 1];
        sub_assign_r(m_minus_v_minus_u, minus_lb_v, half, ROUND_UP);
      }
      else {
        // Here -1 < q < 0.
        typename OR_Matrix<N>::row_reference_type m_u = matrix[n_u];
        const N& m_u_cu = m_u[n_u + 1];
        if (!is_plus_infinity(m_u_cu)) {
          // Let `ub_u' and `lb_u' be the known upper and lower bound
          // for `u', respectively. The upper bound for `-v - u' is
          // computed as `-lb_v - ((-q)*ub_u + (1 + q)*lb_u)',
          // i.e., `minus_lb_v - lb_u + q*(ub_u - lb_u)'.
          assign_r(ub_u, matrix[n_u + 1][n_u], ROUND_NOT_NEEDED);
          div_2exp_assign_r(ub_u, ub_u, 1, ROUND_NOT_NEEDED);
          assign_r(q, expr_u, ROUND_NOT_NEEDED);
          div_assign_r(q, q, mpq_sc_denom, ROUND_NOT_NEEDED);
          assign_r(minus_lb_u, m_u[n_u + 1], ROUND_NOT_NEEDED);
          div_2exp_assign_r(minus_lb_u, minus_lb_u, 1, ROUND_NOT_NEEDED);
          // Compute `ub_u - lb_u'.
          add_assign_r(ub_u, ub_u, minus_lb_u, ROUND_NOT_NEEDED);
          // Compute `-lb_u + q*(ub_u - lb_u)'.
          add_mul_assign_r(minus_lb_u, q, ub_u, ROUND_NOT_NEEDED);
          assign_r(up_approx, minus_lb_u, ROUND_UP);
          // Deducing `-v - u <= -lb_v - ((-q) * ub_u + (1 + q) * lb_u)'.
          N& m_minus_v_minus_u = (n_v < n_u) ? m_u[n_v + 1] : m_v[n_u + 1];
          add_assign_r(m_minus_v_minus_u, minus_lb_v, up_approx, ROUND_UP);
        }
      }
    }
  }
}

template <typename T>
void
Octagonal_Shape<T>
::forget_all_octagonal_constraints(const dimension_type v_id) {
  PPL_ASSERT(v_id < space_dim);
  const dimension_type n_v = 2*v_id;
  typename OR_Matrix<N>::row_iterator m_iter = matrix.row_begin() + n_v;
  typename OR_Matrix<N>::row_reference_type r_v = *m_iter;
  typename OR_Matrix<N>::row_reference_type r_cv = *(++m_iter);
  for (dimension_type h = m_iter.row_size(); h-- > 0; ) {
    assign_r(r_v[h], PLUS_INFINITY, ROUND_NOT_NEEDED);
    assign_r(r_cv[h], PLUS_INFINITY, ROUND_NOT_NEEDED);
  }
  ++m_iter;
  for (typename OR_Matrix<N>::row_iterator m_end = matrix.row_end();
       m_iter != m_end; ++m_iter) {
    typename OR_Matrix<N>::row_reference_type r = *m_iter;
    assign_r(r[n_v], PLUS_INFINITY, ROUND_NOT_NEEDED);
    assign_r(r[n_v + 1], PLUS_INFINITY, ROUND_NOT_NEEDED);
  }
}

template <typename T>
void
Octagonal_Shape<T>
::forget_binary_octagonal_constraints(const dimension_type v_id) {
  PPL_ASSERT(v_id < space_dim);
  const dimension_type n_v = 2*v_id;
  typename OR_Matrix<N>::row_iterator m_iter = matrix.row_begin() + n_v;
  typename OR_Matrix<N>::row_reference_type r_v = *m_iter;
  typename OR_Matrix<N>::row_reference_type r_cv = *(++m_iter);
  for (dimension_type k = n_v; k-- > 0; ) {
    assign_r(r_v[k], PLUS_INFINITY, ROUND_NOT_NEEDED);
    assign_r(r_cv[k], PLUS_INFINITY, ROUND_NOT_NEEDED);
  }
  ++m_iter;
  for (typename OR_Matrix<N>::row_iterator m_end = matrix.row_end();
       m_iter != m_end; ++m_iter) {
    typename OR_Matrix<N>::row_reference_type r = *m_iter;
    assign_r(r[n_v], PLUS_INFINITY, ROUND_NOT_NEEDED);
    assign_r(r[n_v + 1], PLUS_INFINITY, ROUND_NOT_NEEDED);
  }
}

template <typename T>
void
Octagonal_Shape<T>::unconstrain(const Variable var) {
  // Dimension-compatibility check.
  const dimension_type var_id = var.id();
  if (space_dimension() < var_id + 1) {
    throw_dimension_incompatible("unconstrain(var)", var_id + 1);
  }

  // Enforce strong closure for precision.
  strong_closure_assign();

  // If the shape is empty, this is a no-op.
  if (marked_empty()) {
    return;
  }

  forget_all_octagonal_constraints(var_id);
  // Strong closure is preserved.
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::unconstrain(const Variables_Set& vars) {
  // The cylindrification with respect to no dimensions is a no-op.
  // This case captures the only legal cylindrification in a 0-dim space.
  if (vars.empty()) {
    return;
  }

  // Dimension-compatibility check.
  const dimension_type min_space_dim = vars.space_dimension();
  if (space_dimension() < min_space_dim) {
    throw_dimension_incompatible("unconstrain(vs)", min_space_dim);
  }

  // Enforce strong closure for precision.
  strong_closure_assign();

  // If the shape is empty, this is a no-op.
  if (marked_empty()) {
    return;
  }

  for (Variables_Set::const_iterator vsi = vars.begin(),
         vsi_end = vars.end(); vsi != vsi_end; ++vsi) {
    forget_all_octagonal_constraints(*vsi);
  }
  // Strong closure is preserved.
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::refine(const Variable var,
                           const Relation_Symbol relsym,
                           const Linear_Expression& expr,
                           Coefficient_traits::const_reference denominator) {
  PPL_ASSERT(denominator != 0);
  PPL_ASSERT(space_dim >= expr.space_dimension());
  const dimension_type var_id = var.id();
  PPL_ASSERT(var_id <= space_dim);
  PPL_ASSERT(expr.coefficient(var) == 0);
  PPL_ASSERT(relsym != LESS_THAN && relsym != GREATER_THAN);

  const Coefficient& b = expr.inhomogeneous_term();
  // Number of non-zero coefficients in `expr': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;

  // Variable index of the last non-zero coefficient in `expr', if any.
  dimension_type w_id = expr.last_nonzero();

  if (w_id != 0) {
    ++t;
    if (!expr.all_zeroes(1, w_id)) {
      ++t;
    }
    --w_id;
  }

  // Now we know the form of `expr':
  // - If t == 0, then expr == b, with `b' a constant;
  // - If t == 1, then expr == a*j + b, where `j != v';
  // - If t == 2, then `expr' is of the general form.
  typedef typename OR_Matrix<N>::row_iterator row_iterator;
  typedef typename OR_Matrix<N>::row_reference_type row_reference;
  typedef typename OR_Matrix<N>::const_row_iterator Row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type Row_reference;

  const row_iterator m_begin = matrix.row_begin();
  const dimension_type n_var = 2*var_id;
  PPL_DIRTY_TEMP_COEFFICIENT(minus_denom);
  neg_assign(minus_denom, denominator);

  // Since we are only able to record octagonal differences, we can
  // precisely deal with the case of a single variable only if its
  // coefficient (taking into account the denominator) is 1.
  // If this is not the case, we fall back to the general case
  // so as to over-approximate the constraint.
  if (t == 1 && expr.coefficient(Variable(w_id)) != denominator
      && expr.coefficient(Variable(w_id)) != minus_denom) {
    t = 2;
  }
  if (t == 0) {
    // Case 1: expr == b.
    PPL_DIRTY_TEMP_COEFFICIENT(two_b);
    two_b = 2*b;
    switch (relsym) {
    case EQUAL:
      // Add the constraint `var == b/denominator'.
      add_octagonal_constraint(n_var + 1, n_var, two_b, denominator);
      add_octagonal_constraint(n_var, n_var + 1, two_b, minus_denom);
      break;
    case LESS_OR_EQUAL:
      // Add the constraint `var <= b/denominator'.
      add_octagonal_constraint(n_var + 1, n_var, two_b, denominator);
      break;
    case GREATER_OR_EQUAL:
      // Add the constraint `var >= b/denominator',
      // i.e., `-var <= -b/denominator',
      add_octagonal_constraint(n_var, n_var + 1, two_b, minus_denom);
      break;
    default:
      // We already dealt with the other cases.
      PPL_UNREACHABLE;
      break;
    }
  }
  else if (t == 1) {
    // Value of the one and only non-zero coefficient in `expr'.
    const Coefficient& w_coeff = expr.coefficient(Variable(w_id));
    const dimension_type n_w = 2*w_id;
    switch (relsym) {
    case EQUAL:
      if (w_coeff == denominator) {
        // Add the new constraint `var - w = b/denominator'.
        if (var_id < w_id) {
          add_octagonal_constraint(n_w, n_var, b, denominator);
          add_octagonal_constraint(n_w + 1, n_var + 1, b, minus_denom);
        }
        else {
          add_octagonal_constraint(n_var + 1, n_w + 1, b, denominator);
          add_octagonal_constraint(n_var, n_w, b, minus_denom);
        }
      }
      else {
        // Add the new constraint `var + w = b/denominator'.
        if (var_id < w_id) {
          add_octagonal_constraint(n_w + 1, n_var, b, denominator);
          add_octagonal_constraint(n_w, n_var + 1, b, minus_denom);
        }
        else {
          add_octagonal_constraint(n_var + 1, n_w, b, denominator);
          add_octagonal_constraint(n_var, n_w + 1, b, minus_denom);
        }
      }
      break;
    case LESS_OR_EQUAL:
      {
        PPL_DIRTY_TEMP(N, d);
        div_round_up(d, b, denominator);
        // Note that: `w_id != v', so that `expr' is of the form
        // w_coeff * w + b, with `w_id != v'.
        if (w_coeff == denominator) {
          // Add the new constraints `v - w <= b/denominator'.
          if (var_id < w_id) {
            add_octagonal_constraint(n_w, n_var, d);
          }
          else {
            add_octagonal_constraint(n_var + 1, n_w + 1, d);
          }
        }
        else if (w_coeff == minus_denom) {
          // Add the new constraints `v + w <= b/denominator'.
          if (var_id < w_id) {
            add_octagonal_constraint(n_w + 1, n_var, d);
          }
          else {
            add_octagonal_constraint(n_var + 1, n_w, d);
          }
        }
        break;
      }

    case GREATER_OR_EQUAL:
      {
        PPL_DIRTY_TEMP(N, d);
        div_round_up(d, b, minus_denom);
        // Note that: `w_id != v', so that `expr' is of the form
        // w_coeff * w + b, with `w_id != v'.
        if (w_coeff == denominator) {
          // Add the new constraint `v - w >= b/denominator',
          // i.e.,  `-v + w <= -b/denominator'.
          if (var_id < w_id) {
            add_octagonal_constraint(n_w + 1, n_var + 1, d);
          }
          else {
            add_octagonal_constraint(n_var, n_w, d);
          }
        }
        else if (w_coeff == minus_denom) {
          // Add the new constraints `v + w >= b/denominator',
          // i.e.,  `-v - w <= -b/denominator'.
          if (var_id < w_id) {
            add_octagonal_constraint(n_w, n_var + 1, d);
          }
          else {
            add_octagonal_constraint(n_var, n_w + 1, d);
          }
        }
        break;
      }

    default:
      // We already dealt with the other cases.
      PPL_UNREACHABLE;
      break;
    }
  }
  else {
    // Here t == 2, so that
    // expr == a_1*x_1 + a_2*x_2 + ... + a_n*x_n + b, where n >= 2.
    const bool is_sc = (denominator > 0);
    PPL_DIRTY_TEMP_COEFFICIENT(minus_b);
    neg_assign(minus_b, b);
    const Coefficient& sc_b = is_sc ? b : minus_b;
    const Coefficient& minus_sc_b = is_sc ? minus_b : b;
    const Coefficient& sc_denom = is_sc ? denominator : minus_denom;
    const Coefficient& minus_sc_denom = is_sc ? minus_denom : denominator;
    // NOTE: here, for optimization purposes, `minus_expr' is only assigned
    // when `denominator' is negative. Do not use it unless you are sure
    // it has been correctly assigned.
    Linear_Expression minus_expr;
    if (!is_sc) {
      minus_expr = -expr;
    }
    const Linear_Expression& sc_expr = is_sc ? expr : minus_expr;

    PPL_DIRTY_TEMP(N, sum);
    // Index of variable that is unbounded in `this'.
    PPL_UNINITIALIZED(dimension_type, pinf_index);
    // Number of unbounded variables found.
    dimension_type pinf_count = 0;

    switch (relsym) {
    case EQUAL:
      {
        PPL_DIRTY_TEMP(N, neg_sum);
        // Index of variable that is unbounded in `this'.
        PPL_UNINITIALIZED(dimension_type, neg_pinf_index);
        // Number of unbounded variables found.
        dimension_type neg_pinf_count = 0;

        // Approximate the inhomogeneous term.
        assign_r(sum, sc_b, ROUND_UP);
        assign_r(neg_sum, minus_sc_b, ROUND_UP);

        // Approximate the homogeneous part of `sc_expr'.
        PPL_DIRTY_TEMP(N, coeff_i);
        PPL_DIRTY_TEMP(N, half);
        PPL_DIRTY_TEMP_COEFFICIENT(minus_sc_i);
        PPL_DIRTY_TEMP(N, minus_coeff_i);
        // Note: indices above `w' can be disregarded, as they all have
        // a zero coefficient in `sc_expr'.
        for (Row_iterator m_iter = m_begin,
               m_iter_end = m_begin + (2 * w_id + 2);
             m_iter != m_iter_end; ) {
          const dimension_type n_i = m_iter.index();
          const dimension_type id = n_i/2;
          Row_reference m_i = *m_iter;
          ++m_iter;
          Row_reference m_ci = *m_iter;
          ++m_iter;
          const Coefficient& sc_i = sc_expr.coefficient(Variable(id));
          const int sign_i = sgn(sc_i);
          if (sign_i > 0) {
            assign_r(coeff_i, sc_i, ROUND_UP);
            // Approximating `sc_expr'.
            if (pinf_count <= 1) {
              const N& double_approx_i = m_ci[n_i];
              if (!is_plus_infinity(double_approx_i)) {
                // Let half = double_approx_i / 2.
                div_2exp_assign_r(half, double_approx_i, 1, ROUND_UP);
                add_mul_assign_r(sum, coeff_i, half, ROUND_UP);
              }
              else {
                ++pinf_count;
                pinf_index = id;
              }
            }
            // Approximating `-sc_expr'.
            if (neg_pinf_count <= 1) {
              const N& double_approx_minus_i = m_i[n_i + 1];
              if (!is_plus_infinity(double_approx_minus_i)) {
                // Let half = double_approx_minus_i / 2.
                div_2exp_assign_r(half, double_approx_minus_i, 1, ROUND_UP);
                add_mul_assign_r(neg_sum, coeff_i, half, ROUND_UP);
              }
              else {
                ++neg_pinf_count;
                neg_pinf_index = id;
              }
            }
          }
          else if (sign_i < 0) {
            neg_assign_r(minus_sc_i, sc_i, ROUND_NOT_NEEDED);
            assign_r(minus_coeff_i, minus_sc_i, ROUND_UP);
            // Approximating `sc_expr'.
            if (pinf_count <= 1) {
              const N& double_approx_minus_i = m_i[n_i + 1];
              if (!is_plus_infinity(double_approx_minus_i)) {
                // Let half = double_approx_minus_i / 2.
                div_2exp_assign_r(half, double_approx_minus_i, 1, ROUND_UP);
                add_mul_assign_r(sum, minus_coeff_i, half, ROUND_UP);
              }
              else {
                ++pinf_count;
                pinf_index = id;
              }
            }
            // Approximating `-sc_expr'.
            if (neg_pinf_count <= 1) {
              const N& double_approx_i = m_ci[n_i];
              if (!is_plus_infinity(double_approx_i)) {
                // Let half = double_approx_i / 2.
                div_2exp_assign_r(half, double_approx_i, 1, ROUND_UP);
                add_mul_assign_r(neg_sum, minus_coeff_i, half, ROUND_UP);
              }
              else {
                ++neg_pinf_count;
                neg_pinf_index = id;
              }
            }
          }
        }
        // Return immediately if no approximation could be computed.
        if (pinf_count > 1 && neg_pinf_count > 1) {
          PPL_ASSERT(OK());
          return;
        }

        // In the following, strong closure will be definitely lost.
        reset_strongly_closed();

        // Exploit the upper approximation, if possible.
        if (pinf_count <= 1) {
          // Compute quotient (if needed).
          if (sc_denom != 1) {
            // Before computing quotients, the denominator should be
            // approximated towards zero. Since `sc_denom' is known to be
            // positive, this amounts to rounding downwards, which is
            // achieved as usual by rounding upwards `minus_sc_denom'
            // and negating again the result.
            PPL_DIRTY_TEMP(N, down_sc_denom);
            assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
            neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
            div_assign_r(sum, sum, down_sc_denom, ROUND_UP);
          }
          // Add the upper bound constraint, if meaningful.
          if (pinf_count == 0) {
            // Add the constraint `v <= sum'.
            PPL_DIRTY_TEMP(N, double_sum);
            mul_2exp_assign_r(double_sum, sum, 1, ROUND_UP);
            matrix[n_var + 1][n_var] = double_sum;
            // Deduce constraints of the form `v +/- u', where `u != v'.
            deduce_v_pm_u_bounds(var_id, w_id, sc_expr, sc_denom, sum);
          }
          else {
            // Here `pinf_count == 1'.
            if (pinf_index != var_id) {
              const Coefficient& ppi
                = sc_expr.coefficient(Variable(pinf_index));
              if (ppi == sc_denom) {
                // Add the constraint `v - pinf_index <= sum'.
                if (var_id < pinf_index) {
                  matrix[2*pinf_index][n_var] = sum;
                }
                else {
                  matrix[n_var + 1][2*pinf_index + 1] = sum;
                }
              }
              else {
                if (ppi == minus_sc_denom) {
                  // Add the constraint `v + pinf_index <= sum'.
                  if (var_id < pinf_index) {
                    matrix[2*pinf_index + 1][n_var] = sum;
                  }
                  else {
                    matrix[n_var + 1][2*pinf_index] = sum;
                  }
                }
              }
            }
          }
        }

        // Exploit the lower approximation, if possible.
        if (neg_pinf_count <= 1) {
          // Compute quotient (if needed).
          if (sc_denom != 1) {
            // Before computing quotients, the denominator should be
            // approximated towards zero. Since `sc_denom' is known to be
            // positive, this amounts to rounding downwards, which is
            // achieved as usual by rounding upwards `minus_sc_denom'
            // and negating again the result.
            PPL_DIRTY_TEMP(N, down_sc_denom);
            assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
            neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
            div_assign_r(neg_sum, neg_sum, down_sc_denom, ROUND_UP);
          }
          // Add the lower bound constraint, if meaningful.
          if (neg_pinf_count == 0) {
            // Add the constraint `v >= -neg_sum', i.e., `-v <= neg_sum'.
            PPL_DIRTY_TEMP(N, double_neg_sum);
            mul_2exp_assign_r(double_neg_sum, neg_sum, 1, ROUND_UP);
            matrix[n_var][n_var + 1] = double_neg_sum;
            // Deduce constraints of the form `-v +/- u', where `u != v'.
            deduce_minus_v_pm_u_bounds(var_id, w_id, sc_expr, sc_denom,
                                       neg_sum);
          }
          else {
            // Here `neg_pinf_count == 1'.
            if (neg_pinf_index != var_id) {
              const Coefficient& npi
                = sc_expr.coefficient(Variable(neg_pinf_index));
              if (npi == sc_denom) {
                // Add the constraint `v - neg_pinf_index >= -neg_sum',
                // i.e., `neg_pinf_index - v <= neg_sum'.
                if (neg_pinf_index < var_id) {
                  matrix[n_var][2*neg_pinf_index] = neg_sum;
                }
                else {
                  matrix[2*neg_pinf_index + 1][n_var + 1] = neg_sum;
                }
              }
              else {
                if (npi == minus_sc_denom) {
                  // Add the constraint `v + neg_pinf_index >= -neg_sum',
                  // i.e., `-neg_pinf_index - v <= neg_sum'.
                  if (neg_pinf_index < var_id) {
                    matrix[n_var][2*neg_pinf_index + 1] = neg_sum;
                  }
                  else {
                    matrix[2*neg_pinf_index][n_var + 1] = neg_sum;
                  }
                }
              }
            }
          }
        }
        break;
      }

    case LESS_OR_EQUAL:
      {
        // Compute an upper approximation for `expr' into `sum',
        // taking into account the sign of `denominator'.

        // Approximate the inhomogeneous term.
        assign_r(sum, sc_b, ROUND_UP);

        // Approximate the homogeneous part of `sc_expr'.
        PPL_DIRTY_TEMP(N, coeff_i);
        PPL_DIRTY_TEMP(N, approx_i);
        PPL_DIRTY_TEMP_COEFFICIENT(minus_sc_i);
        // Note: indices above `w_id' can be disregarded, as they all have
        // a zero coefficient in `expr'.
        for (row_iterator m_iter = m_begin,
               m_iter_end = m_begin + (2 * w_id + 2);
             m_iter != m_iter_end; ) {
          const dimension_type n_i = m_iter.index();
          const dimension_type id = n_i/2;
          row_reference m_i = *m_iter;
          ++m_iter;
          row_reference m_ci = *m_iter;
          ++m_iter;
          const Coefficient& sc_i = sc_expr.coefficient(Variable(id));
          const int sign_i = sgn(sc_i);
          if (sign_i == 0) {
            continue;
          }
          // Choose carefully: we are approximating `sc_expr'.
          const N& double_approx_i = (sign_i > 0) ? m_ci[n_i] : m_i[n_i + 1];
          if (is_plus_infinity(double_approx_i)) {
            if (++pinf_count > 1) {
              break;
            }
            pinf_index = id;
            continue;
          }
          if (sign_i > 0) {
            assign_r(coeff_i, sc_i, ROUND_UP);
          }
          else {
            neg_assign(minus_sc_i, sc_i);
            assign_r(coeff_i, minus_sc_i, ROUND_UP);
          }
          div_2exp_assign_r(approx_i, double_approx_i, 1, ROUND_UP);
          add_mul_assign_r(sum, coeff_i, approx_i, ROUND_UP);
        }
        // Divide by the (sign corrected) denominator (if needed).
        if (sc_denom != 1) {
          // Before computing the quotient, the denominator should be
          // approximated towards zero. Since `sc_denom' is known to be
          // positive, this amounts to rounding downwards, which is achieved
          // by rounding upwards `minus_sc-denom' and negating again the result.
          PPL_DIRTY_TEMP(N, down_sc_denom);
          assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
          neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
          div_assign_r(sum, sum, down_sc_denom, ROUND_UP);
        }

        if (pinf_count == 0) {
          // Add the constraint `v <= sum'.
          PPL_DIRTY_TEMP(N, double_sum);
          mul_2exp_assign_r(double_sum, sum, 1, ROUND_UP);
          add_octagonal_constraint(n_var + 1, n_var, double_sum);
          // Deduce constraints of the form `v +/- u', where `u != v'.
          deduce_v_pm_u_bounds(var_id, w_id, sc_expr, sc_denom, sum);
        }
        else if (pinf_count == 1) {
          dimension_type pinf_ind = 2*pinf_index;
          if (expr.coefficient(Variable(pinf_index)) == denominator ) {
            // Add the constraint `v - pinf_index <= sum'.
            if (var_id < pinf_index) {
              add_octagonal_constraint(pinf_ind, n_var, sum);
            }
            else {
              add_octagonal_constraint(n_var + 1, pinf_ind + 1, sum);
            }
          }
          else {
            if (expr.coefficient(Variable(pinf_index)) == minus_denom) {
              // Add the constraint `v + pinf_index <= sum'.
              if (var_id < pinf_index) {
                add_octagonal_constraint(pinf_ind + 1, n_var, sum);
              }
              else {
                add_octagonal_constraint(n_var + 1, pinf_ind, sum);
              }
            }
          }
        }
        break;
      }

    case GREATER_OR_EQUAL:
      {
        // Compute an upper approximation for `-sc_expr' into `sum'.
        // Note: approximating `-sc_expr' from above and then negating the
        // result is the same as approximating `sc_expr' from below.

        // Approximate the inhomogeneous term.
        assign_r(sum, minus_sc_b, ROUND_UP);

        // Approximate the homogeneous part of `-sc_expr'.
        PPL_DIRTY_TEMP(N, coeff_i);
        PPL_DIRTY_TEMP(N, approx_i);
        PPL_DIRTY_TEMP_COEFFICIENT(minus_sc_i);
        for (row_iterator m_iter = m_begin,
               m_iter_end = m_begin + (2 * w_id + 2);
             m_iter != m_iter_end; ) {
          const dimension_type n_i = m_iter.index();
          const dimension_type id = n_i/2;
          row_reference m_i = *m_iter;
          ++m_iter;
          row_reference m_ci = *m_iter;
          ++m_iter;
          const Coefficient& sc_i = sc_expr.coefficient(Variable(id));
          const int sign_i = sgn(sc_i);
          if (sign_i == 0) {
            continue;
          }
          // Choose carefully: we are approximating `-sc_expr'.
          const N& double_approx_i = (sign_i > 0) ? m_i[n_i + 1] : m_ci[n_i];
          if (is_plus_infinity(double_approx_i)) {
            if (++pinf_count > 1) {
              break;
            }
            pinf_index = id;
            continue;
          }
          if (sign_i > 0) {
            assign_r(coeff_i, sc_i, ROUND_UP);
          }
          else {
            neg_assign(minus_sc_i, sc_i);
            assign_r(coeff_i, minus_sc_i, ROUND_UP);
          }
          div_2exp_assign_r(approx_i, double_approx_i, 1, ROUND_UP);
          add_mul_assign_r(sum, coeff_i, approx_i, ROUND_UP);
        }

        // Divide by the (sign corrected) denominator (if needed).
        if (sc_denom != 1) {
          // Before computing the quotient, the denominator should be
          // approximated towards zero. Since `sc_denom' is known to be
          // positive, this amounts to rounding downwards, which is
          // achieved by rounding upwards `minus_sc_denom' and
          // negating again the result.
          PPL_DIRTY_TEMP(N, down_sc_denom);
          assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
          neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
          div_assign_r(sum, sum, down_sc_denom, ROUND_UP);
        }

        if (pinf_count == 0) {
          // Add the constraint `v >= -neg_sum', i.e., `-v <= neg_sum'.
          PPL_DIRTY_TEMP(N, double_sum);
          mul_2exp_assign_r(double_sum, sum, 1, ROUND_UP);
          add_octagonal_constraint(n_var, n_var + 1, double_sum);
          // Deduce constraints of the form `-v +/- u', where `u != v'.
          deduce_minus_v_pm_u_bounds(var_id, pinf_index, sc_expr, sc_denom,
                                     sum);
        }
        else if (pinf_count == 1) {
          dimension_type pinf_ind = 2*pinf_index;
          if (expr.coefficient(Variable(pinf_index)) == denominator) {
            // Add the constraint `v - pinf_index >= -sum',
            // i.e., `pinf_index - v <= sum'.
            if (pinf_index < var_id) {
              add_octagonal_constraint(n_var, pinf_ind, sum);
            }
            else {
              add_octagonal_constraint(pinf_ind + 1, n_var, sum);
            }
          }
          else {
            if (expr.coefficient(Variable(pinf_index)) == minus_denom) {
              // Add the constraint `v + pinf_index >= -sum',
              // i.e., `-pinf_index - v <= sum'.
              if (pinf_index < var_id) {
                add_octagonal_constraint(n_var, pinf_ind + 1, sum);
              }
              else {
                add_octagonal_constraint(pinf_ind, n_var + 1, sum);
              }
            }
          }
        }
        break;
      }

    default:
      // We already dealt with the other cases.
      PPL_UNREACHABLE;
      break;
    }
  }
}

template <typename T>
void
Octagonal_Shape<T>::affine_image(const Variable var,
                                 const Linear_Expression& expr,
                                 Coefficient_traits::const_reference
                                 denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("affine_image(v, e, d)", "d == 0");
  }

  // Dimension-compatibility checks.
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible("affine_image(v, e, d)", "e", expr);
  }

  // `var' should be one of the dimensions of the octagon.
  const dimension_type var_id = var.id();
  if (space_dim < var_id + 1) {
    throw_dimension_incompatible("affine_image(v, e, d)", var_id + 1);
  }

  strong_closure_assign();
  // The image of an empty octagon is empty too.
  if (marked_empty()) {
    return;
  }

  // Number of non-zero coefficients in `expr': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;
  // Variable-index of the last non-zero coefficient in `expr', if any.
  dimension_type w_id = expr.last_nonzero();

  if (w_id != 0) {
    ++t;
    if (!expr.all_zeroes(1, w_id)) {
      ++t;
    }
    --w_id;
  }

  typedef typename OR_Matrix<N>::row_iterator row_iterator;
  typedef typename OR_Matrix<N>::row_reference_type row_reference;
  typedef typename OR_Matrix<N>::const_row_iterator Row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type Row_reference;
  using std::swap;

  const dimension_type n_var = 2*var_id;
  const Coefficient& b = expr.inhomogeneous_term();
  PPL_DIRTY_TEMP_COEFFICIENT(minus_denom);
  neg_assign_r(minus_denom, denominator, ROUND_NOT_NEEDED);

  // `w' is the variable with index `w_id'.
  // Now we know the form of `expr':
  // - If t == 0, then expr == b, with `b' a constant;
  // - If t == 1, then expr == a*w + b, where `w' can be `v' or another
  //   variable; in this second case we have to check whether `a' is
  //   equal to `denominator' or `-denominator', since otherwise we have
  //   to fall back on the general form;
  // - If t == 2, the `expr' is of the general form.

  if (t == 0) {
    // Case 1: expr == b.
    // Remove all constraints on `var'.
    forget_all_octagonal_constraints(var_id);
    PPL_DIRTY_TEMP_COEFFICIENT(two_b);
    two_b = 2*b;
    // Add the constraint `var == b/denominator'.
    add_octagonal_constraint(n_var + 1, n_var, two_b, denominator);
    add_octagonal_constraint(n_var, n_var + 1, two_b, minus_denom);
    PPL_ASSERT(OK());
    return;
  }

  if (t == 1) {
    // The one and only non-zero homogeneous coefficient in `expr'.
    const Coefficient& w_coeff = expr.coefficient(Variable(w_id));
    if (w_coeff == denominator || w_coeff == minus_denom) {
      // Case 2: expr = w_coeff*w + b, with w_coeff = +/- denominator.
      if (w_id == var_id) {
        // Here `expr' is of the form: +/- denominator * v + b.
        const bool sign_symmetry = (w_coeff != denominator);
        if (!sign_symmetry && b == 0) {
          // The transformation is the identity function.
          return;
        }
        // Translate all the constraints on `var' adding or
        // subtracting the value `b/denominator'.
        PPL_DIRTY_TEMP(N, d);
        div_round_up(d, b, denominator);
        PPL_DIRTY_TEMP(N, minus_d);
        div_round_up(minus_d, b, minus_denom);
        if (sign_symmetry) {
          swap(d, minus_d);
        }
        const row_iterator m_begin = matrix.row_begin();
        const row_iterator m_end = matrix.row_end();
        row_iterator m_iter = m_begin + n_var;
        row_reference m_v = *m_iter;
        ++m_iter;
        row_reference m_cv = *m_iter;
        ++m_iter;
        // NOTE: delay update of unary constraints on `var'.
        for (dimension_type j = n_var; j-- > 0; ) {
          N& m_v_j = m_v[j];
          add_assign_r(m_v_j, m_v_j, minus_d, ROUND_UP);
          N& m_cv_j = m_cv[j];
          add_assign_r(m_cv_j, m_cv_j, d, ROUND_UP);
          if (sign_symmetry) {
            swap(m_v_j, m_cv_j);
          }
        }
        for ( ; m_iter != m_end; ++m_iter) {
          row_reference m_i = *m_iter;
          N& m_i_v = m_i[n_var];
          add_assign_r(m_i_v, m_i_v, d, ROUND_UP);
          N& m_i_cv = m_i[n_var + 1];
          add_assign_r(m_i_cv, m_i_cv, minus_d, ROUND_UP);
          if (sign_symmetry) {
            swap(m_i_v, m_i_cv);
          }
        }
        // Now update unary constraints on var.
        mul_2exp_assign_r(d, d, 1, ROUND_UP);
        N& m_cv_v = m_cv[n_var];
        add_assign_r(m_cv_v, m_cv_v, d, ROUND_UP);
        mul_2exp_assign_r(minus_d, minus_d, 1, ROUND_UP);
        N& m_v_cv = m_v[n_var + 1];
        add_assign_r(m_v_cv, m_v_cv, minus_d, ROUND_UP);
        if (sign_symmetry) {
          swap(m_cv_v, m_v_cv);
        }
        // Note: strong closure is preserved.
      }
      else {
        // Here `w != var', so that `expr' is of the form
        // +/-denominator * w + b.
        // Remove all constraints on `var'.
        forget_all_octagonal_constraints(var_id);
        const dimension_type n_w = 2*w_id;
        // Add the new constraint `var - w = b/denominator'.
        if (w_coeff == denominator) {
          if (var_id < w_id) {
            add_octagonal_constraint(n_w, n_var, b, denominator);
            add_octagonal_constraint(n_w + 1, n_var + 1, b, minus_denom);
          }
          else {
            add_octagonal_constraint(n_var + 1, n_w + 1, b, denominator);
            add_octagonal_constraint(n_var, n_w, b, minus_denom);
          }
        }
        else {
          // Add the new constraint `var + w = b/denominator'.
          if (var_id < w_id) {
            add_octagonal_constraint(n_w + 1, n_var, b, denominator);
            add_octagonal_constraint(n_w, n_var + 1, b, minus_denom);
          }
          else {
            add_octagonal_constraint(n_var + 1, n_w, b, denominator);
            add_octagonal_constraint(n_var, n_w + 1, b, minus_denom);
          }
        }
        incremental_strong_closure_assign(var);
      }
      PPL_ASSERT(OK());
      return;
    }
  }

  // General case.
  // Either t == 2, so that
  // expr == a_1*x_1 + a_2*x_2 + ... + a_n*x_n + b, where n >= 2,
  // or t == 1, expr == a*w + b, but a <> +/- denominator.
  // We will remove all the constraints on `var' and add back
  // constraints providing upper and lower bounds for `var'.

  // Compute upper approximations for `expr' and `-expr'
  // into `pos_sum' and `neg_sum', respectively, taking into account
  // the sign of `denominator'.
  // Note: approximating `-expr' from above and then negating the
  // result is the same as approximating `expr' from below.
  const bool is_sc = (denominator > 0);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_b);
  neg_assign_r(minus_b, b, ROUND_NOT_NEEDED);

  const Coefficient& sc_b = is_sc ? b : minus_b;
  const Coefficient& minus_sc_b = is_sc ? minus_b : b;
  const Coefficient& sc_denom = is_sc ? denominator : minus_denom;
  const Coefficient& minus_sc_denom = is_sc ? minus_denom : denominator;
  // NOTE: here, for optimization purposes, `minus_expr' is only assigned
  // when `denominator' is negative. Do not use it unless you are sure
  // it has been correctly assigned.
  Linear_Expression minus_expr;
  if (!is_sc) {
    minus_expr = -expr;
  }
  const Linear_Expression& sc_expr = is_sc ? expr : minus_expr;

  PPL_DIRTY_TEMP(N, pos_sum);
  PPL_DIRTY_TEMP(N, neg_sum);
  // Indices of the variables that are unbounded in `this->matrix'.
  PPL_UNINITIALIZED(dimension_type, pos_pinf_index);
  PPL_UNINITIALIZED(dimension_type, neg_pinf_index);
  // Number of unbounded variables found.
  dimension_type pos_pinf_count = 0;
  dimension_type neg_pinf_count = 0;

  // Approximate the inhomogeneous term.
  assign_r(pos_sum, sc_b, ROUND_UP);
  assign_r(neg_sum, minus_sc_b, ROUND_UP);

  // Approximate the homogeneous part of `sc_expr'.
  PPL_DIRTY_TEMP(N, coeff_i);
  PPL_DIRTY_TEMP(N, minus_coeff_i);
  PPL_DIRTY_TEMP(N, half);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_sc_i);
  // Note: indices above `w' can be disregarded, as they all have
  // a zero coefficient in `sc_expr'.
  const row_iterator m_begin = matrix.row_begin();
  for (Row_iterator m_iter = m_begin, m_iter_end = m_begin + (2 * w_id + 2);
       m_iter != m_iter_end; ) {
    const dimension_type n_i = m_iter.index();
    const dimension_type id = n_i/2;
    Row_reference m_i = *m_iter;
    ++m_iter;
    Row_reference m_ci = *m_iter;
    ++m_iter;
    const Coefficient& sc_i = sc_expr.coefficient(Variable(id));
    const int sign_i = sgn(sc_i);
    if (sign_i > 0) {
      assign_r(coeff_i, sc_i, ROUND_UP);
      // Approximating `sc_expr'.
      if (pos_pinf_count <= 1) {
        const N& double_up_approx_i = m_ci[n_i];
        if (!is_plus_infinity(double_up_approx_i)) {
          // Let half = double_up_approx_i / 2.
          div_2exp_assign_r(half, double_up_approx_i, 1, ROUND_UP);
          add_mul_assign_r(pos_sum, coeff_i, half, ROUND_UP);
        }
        else {
          ++pos_pinf_count;
          pos_pinf_index = id;
        }
      }
      // Approximating `-sc_expr'.
      if (neg_pinf_count <= 1) {
        const N& double_up_approx_minus_i = m_i[n_i + 1];
        if (!is_plus_infinity(double_up_approx_minus_i)) {
          // Let half = double_up_approx_minus_i / 2.
          div_2exp_assign_r(half, double_up_approx_minus_i, 1, ROUND_UP);
          add_mul_assign_r(neg_sum, coeff_i, half, ROUND_UP);
        }
        else {
          ++neg_pinf_count;
          neg_pinf_index = id;
        }
      }
    }
    else if (sign_i < 0) {
      neg_assign_r(minus_sc_i, sc_i, ROUND_NOT_NEEDED);
      assign_r(minus_coeff_i, minus_sc_i, ROUND_UP);
      // Approximating `sc_expr'.
      if (pos_pinf_count <= 1) {
        const N& double_up_approx_minus_i = m_i[n_i + 1];
        if (!is_plus_infinity(double_up_approx_minus_i)) {
          // Let half = double_up_approx_minus_i / 2.
          div_2exp_assign_r(half, double_up_approx_minus_i, 1, ROUND_UP);
          add_mul_assign_r(pos_sum, minus_coeff_i, half, ROUND_UP);
        }
        else {
          ++pos_pinf_count;
          pos_pinf_index = id;
        }
      }
      // Approximating `-sc_expr'.
      if (neg_pinf_count <= 1) {
        const N& double_up_approx_i = m_ci[n_i];
        if (!is_plus_infinity(double_up_approx_i)) {
          // Let half = double_up_approx_i / 2.
          div_2exp_assign_r(half, double_up_approx_i, 1, ROUND_UP);
          add_mul_assign_r(neg_sum, minus_coeff_i, half, ROUND_UP);
        }
        else {
          ++neg_pinf_count;
          neg_pinf_index = id;
        }
      }
    }
  }

  // Remove all constraints on `var'.
  forget_all_octagonal_constraints(var_id);
  // Return immediately if no approximation could be computed.
  if (pos_pinf_count > 1 && neg_pinf_count > 1) {
    PPL_ASSERT(OK());
    return;
  }

  // In the following, strong closure will be definitely lost.
  reset_strongly_closed();

  // Exploit the upper approximation, if possible.
  if (pos_pinf_count <= 1) {
    // Compute quotient (if needed).
    if (sc_denom != 1) {
      // Before computing quotients, the denominator should be approximated
      // towards zero. Since `sc_denom' is known to be positive, this amounts to
      // rounding downwards, which is achieved as usual by rounding upwards
      // `minus_sc_denom' and negating again the result.
      PPL_DIRTY_TEMP(N, down_sc_denom);
      assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
      neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
      div_assign_r(pos_sum, pos_sum, down_sc_denom, ROUND_UP);
    }
    // Add the upper bound constraint, if meaningful.
    if (pos_pinf_count == 0) {
      // Add the constraint `v <= pos_sum'.
      PPL_DIRTY_TEMP(N, double_pos_sum);
      mul_2exp_assign_r(double_pos_sum, pos_sum, 1, ROUND_UP);
      matrix[n_var + 1][n_var] = double_pos_sum;
      // Deduce constraints of the form `v +/- u', where `u != v'.
      deduce_v_pm_u_bounds(var_id, w_id, sc_expr, sc_denom, pos_sum);
    }
    else {
      // Here `pos_pinf_count == 1'.
      if (pos_pinf_index != var_id) {
        const Coefficient& ppi = sc_expr.coefficient(Variable(pos_pinf_index));
        if (ppi == sc_denom) {
          // Add the constraint `v - pos_pinf_index <= pos_sum'.
          if (var_id < pos_pinf_index) {
            matrix[2*pos_pinf_index][n_var] = pos_sum;
          }
          else {
            matrix[n_var + 1][2*pos_pinf_index + 1] = pos_sum;
          }
        }
        else {
          if (ppi == minus_sc_denom) {
            // Add the constraint `v + pos_pinf_index <= pos_sum'.
            if (var_id < pos_pinf_index) {
              matrix[2*pos_pinf_index + 1][n_var] = pos_sum;
            }
            else {
              matrix[n_var + 1][2*pos_pinf_index] = pos_sum;
            }
          }
        }
      }
    }
  }

  // Exploit the lower approximation, if possible.
  if (neg_pinf_count <= 1) {
    // Compute quotient (if needed).
    if (sc_denom != 1) {
      // Before computing quotients, the denominator should be approximated
      // towards zero. Since `sc_denom' is known to be positive, this amounts to
      // rounding downwards, which is achieved as usual by rounding upwards
      // `minus_sc_denom' and negating again the result.
      PPL_DIRTY_TEMP(N, down_sc_denom);
      assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
      neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
      div_assign_r(neg_sum, neg_sum, down_sc_denom, ROUND_UP);
    }
    // Add the lower bound constraint, if meaningful.
    if (neg_pinf_count == 0) {
      // Add the constraint `v >= -neg_sum', i.e., `-v <= neg_sum'.
      PPL_DIRTY_TEMP(N, double_neg_sum);
      mul_2exp_assign_r(double_neg_sum, neg_sum, 1, ROUND_UP);
      matrix[n_var][n_var + 1] = double_neg_sum;
      // Deduce constraints of the form `-v +/- u', where `u != v'.
      deduce_minus_v_pm_u_bounds(var_id, w_id, sc_expr, sc_denom, neg_sum);
    }
    else {
      // Here `neg_pinf_count == 1'.
      if (neg_pinf_index != var_id) {
        const Coefficient& npi = sc_expr.coefficient(Variable(neg_pinf_index));
        if (npi == sc_denom) {
          // Add the constraint `v - neg_pinf_index >= -neg_sum',
          // i.e., `neg_pinf_index - v <= neg_sum'.
          if (neg_pinf_index < var_id) {
            matrix[n_var][2*neg_pinf_index] = neg_sum;
          }
          else {
            matrix[2*neg_pinf_index + 1][n_var + 1] = neg_sum;
          }
        }
        else {
          if (npi == minus_sc_denom) {
            // Add the constraint `v + neg_pinf_index >= -neg_sum',
            // i.e., `-neg_pinf_index - v <= neg_sum'.
            if (neg_pinf_index < var_id) {
              matrix[n_var][2*neg_pinf_index + 1] = neg_sum;
            }
            else {
              matrix[2*neg_pinf_index][n_var + 1] = neg_sum;
            }
          }
        }
      }
    }
  }

  incremental_strong_closure_assign(var);
  PPL_ASSERT(OK());
}

template <typename T>
template <typename Interval_Info>
void
Octagonal_Shape<T>::affine_form_image(const Variable var,
                    const Linear_Form< Interval<T, Interval_Info> >& lf) {
  // Check that T is a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<T>::is_exact,
    "Octagonal_Shape<T>::affine_form_image(Variable, Linear_Form):"
    " T is not a floating point type.");

  // Dimension-compatibility checks.
  // The dimension of `lf' should not be greater than the dimension
  // of `*this'.
  const dimension_type lf_space_dim = lf.space_dimension();
  if (space_dim < lf_space_dim) {
    throw_dimension_incompatible("affine_form_image(v, l)", "l", lf);
  }

  // `var' should be one of the dimensions of the octagon.
  const dimension_type var_id = var.id();
  if (space_dim < var_id + 1) {
    throw_dimension_incompatible("affine_form_image(v, l)", var.id() + 1);
  }

  strong_closure_assign();
  // The image of an empty octagon is empty too.
  if (marked_empty()) {
    return;
  }

  // Number of non-zero coefficients in `lf': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;
  // Variable-index of the last non-zero coefficient in `lf', if any.
  dimension_type w_id = 0;

  // Get information about the number of non-zero coefficients in `lf'.
  for (dimension_type i = lf_space_dim; i-- > 0; ) {
    if (lf.coefficient(Variable(i)) != 0) {
      if (t++ == 1) {
        break;
      }
      else {
        w_id = i;
      }
    }
  }

  typedef typename OR_Matrix<N>::row_iterator row_iterator;
  typedef typename OR_Matrix<N>::row_reference_type row_reference;
  typedef Interval<T, Interval_Info> FP_Interval_Type;
  using std::swap;

  const dimension_type n_var = 2*var_id;
  const FP_Interval_Type& b = lf.inhomogeneous_term();

  // `w' is the variable with index `w_id'.
  // Now we know the form of `lf':
  // - If t == 0, then lf == [lb, ub];
  // - If t == 1, then lf == a*w + [lb, ub], where `w' can be `v' or another
  //   variable;
  // - If t == 2, the `lf' is of the general form.

  PPL_DIRTY_TEMP(N, b_ub);
  assign_r(b_ub, b.upper(), ROUND_NOT_NEEDED);
  PPL_DIRTY_TEMP(N, b_mlb);
  neg_assign_r(b_mlb, b.lower(), ROUND_NOT_NEEDED);

  if (t == 0) {
    // Case 1: lf = [lb, ub].
    forget_all_octagonal_constraints(var_id);
    mul_2exp_assign_r(b_mlb, b_mlb, 1, ROUND_UP);
    mul_2exp_assign_r(b_ub, b_ub, 1, ROUND_UP);
    // Add the constraint `var >= lb && var <= ub'.
    add_octagonal_constraint(n_var + 1, n_var, b_ub);
    add_octagonal_constraint(n_var, n_var + 1, b_mlb);
    PPL_ASSERT(OK());
    return;
  }

  // True if `b' is in [0, 0].
  bool is_b_zero = (b_mlb == 0 && b_ub == 0);

  if (t == 1) {
    // The one and only non-zero homogeneous coefficient in `lf'.
    const FP_Interval_Type& w_coeff = lf.coefficient(Variable(w_id));
    // True if `w_coeff' is in [1, 1].
    bool is_w_coeff_one = (w_coeff == 1);
    // True if `w_coeff' is in [-1, -1].
    bool is_w_coeff_minus_one = (w_coeff == -1);
    if (is_w_coeff_one || is_w_coeff_minus_one) {
      // Case 2: lf = w_coeff*w + b, with w_coeff = [+/-1, +/-1].
      if (w_id == var_id) {
        // Here lf = w_coeff*v + b, with w_coeff = [+/-1, +/-1].
        if (is_w_coeff_one && is_b_zero) {
          // The transformation is the identity function.
          return;
        }
        // Translate all the constraints on `var' by adding the value
        // `b_ub' or subtracting the value `b_lb'.
        if (is_w_coeff_minus_one) {
          swap(b_ub, b_mlb);
        }
        const row_iterator m_begin = matrix.row_begin();
        const row_iterator m_end = matrix.row_end();
        row_iterator m_iter = m_begin + n_var;
        row_reference m_v = *m_iter;
        ++m_iter;
        row_reference m_cv = *m_iter;
        ++m_iter;
        // NOTE: delay update of unary constraints on `var'.
        for (dimension_type j = n_var; j-- > 0; ) {
          N& m_v_j = m_v[j];
          add_assign_r(m_v_j, m_v_j, b_mlb, ROUND_UP);
          N& m_cv_j = m_cv[j];
          add_assign_r(m_cv_j, m_cv_j, b_ub, ROUND_UP);
          if (is_w_coeff_minus_one) {
            swap(m_v_j, m_cv_j);
          }
        }
        for ( ; m_iter != m_end; ++m_iter) {
          row_reference m_i = *m_iter;
          N& m_i_v = m_i[n_var];
          add_assign_r(m_i_v, m_i_v, b_ub, ROUND_UP);
          N& m_i_cv = m_i[n_var + 1];
          add_assign_r(m_i_cv, m_i_cv, b_mlb, ROUND_UP);
          if (is_w_coeff_minus_one) {
            swap(m_i_v, m_i_cv);
          }
        }
        // Now update unary constraints on var.
        mul_2exp_assign_r(b_ub, b_ub, 1, ROUND_UP);
        N& m_cv_v = m_cv[n_var];
        add_assign_r(m_cv_v, m_cv_v, b_ub, ROUND_UP);
        mul_2exp_assign_r(b_mlb, b_mlb, 1, ROUND_UP);
        N& m_v_cv = m_v[n_var + 1];
        add_assign_r(m_v_cv, m_v_cv, b_mlb, ROUND_UP);
        if (is_w_coeff_minus_one) {
          swap(m_cv_v, m_v_cv);
        }
        // Note: strong closure is preserved.
      }
      else {
        // Here `w != var', so that `lf' is of the form
        // [+/-1, +/-1] * w + b.
        // Remove all constraints on `var'.
        forget_all_octagonal_constraints(var_id);
        const dimension_type n_w = 2*w_id;
        if (is_w_coeff_one) {
          // Add the new constraints `var - w >= b_lb'
          // `and var - w <= b_ub'.
          if (var_id < w_id) {
            add_octagonal_constraint(n_w, n_var, b_ub);
            add_octagonal_constraint(n_w + 1, n_var + 1, b_mlb);
          }
          else {
            add_octagonal_constraint(n_var + 1, n_w + 1, b_ub);
            add_octagonal_constraint(n_var, n_w, b_mlb);
          }
        }
        else {
          // Add the new constraints `var + w >= b_lb'
          // `and var + w <= b_ub'.
          if (var_id < w_id) {
            add_octagonal_constraint(n_w + 1, n_var, b_ub);
            add_octagonal_constraint(n_w, n_var + 1, b_mlb);
          }
          else {
            add_octagonal_constraint(n_var + 1, n_w, b_ub);
            add_octagonal_constraint(n_var, n_w + 1, b_mlb);
          }
        }
        incremental_strong_closure_assign(var);
      }
      PPL_ASSERT(OK());
      return;
    }
  }

  // General case.
  // Either t == 2, so that
  // expr == i_1*x_1 + i_2*x_2 + ... + i_n*x_n + b, where n >= 2,
  // or t == 1, expr == i*w + b, but i <> [+/-1, +/-1].

  // In the following, strong closure will be definitely lost.
  reset_strongly_closed();

  Linear_Form<FP_Interval_Type> minus_lf(lf);
  minus_lf.negate();

  // Declare temporaries outside the loop.
  PPL_DIRTY_TEMP(N, upper_bound);

  row_iterator m_iter = matrix.row_begin();
  m_iter += n_var;
  row_reference var_ite = *m_iter;
  ++m_iter;
  row_reference var_cv_ite = *m_iter;
  ++m_iter;
  row_iterator m_end = matrix.row_end();

  // Update binary constraints on var FIRST.
  for (dimension_type curr_var = var_id,
         n_curr_var = n_var - 2; curr_var-- > 0; ) {
    Variable current(curr_var);
    linear_form_upper_bound(lf + current, upper_bound);
    assign_r(var_cv_ite[n_curr_var], upper_bound, ROUND_NOT_NEEDED);
    linear_form_upper_bound(lf - current, upper_bound);
    assign_r(var_cv_ite[n_curr_var + 1], upper_bound, ROUND_NOT_NEEDED);
    linear_form_upper_bound(minus_lf + current, upper_bound);
    assign_r(var_ite[n_curr_var], upper_bound, ROUND_NOT_NEEDED);
    linear_form_upper_bound(minus_lf - current, upper_bound);
    assign_r(var_ite[n_curr_var + 1], upper_bound, ROUND_NOT_NEEDED);
    n_curr_var -= 2;
  }
  for (dimension_type curr_var = var_id + 1; m_iter != m_end; ++m_iter) {
    row_reference m_v_ite = *m_iter;
    ++m_iter;
    row_reference m_cv_ite = *m_iter;
    Variable current(curr_var);
    linear_form_upper_bound(lf + current, upper_bound);
    assign_r(m_cv_ite[n_var], upper_bound, ROUND_NOT_NEEDED);
    linear_form_upper_bound(lf - current, upper_bound);
    assign_r(m_v_ite[n_var], upper_bound, ROUND_NOT_NEEDED);
    linear_form_upper_bound(minus_lf + current, upper_bound);
    assign_r(m_cv_ite[n_var + 1], upper_bound, ROUND_NOT_NEEDED);
    linear_form_upper_bound(minus_lf - current, upper_bound);
    assign_r(m_v_ite[n_var + 1], upper_bound, ROUND_NOT_NEEDED);
    ++curr_var;
  }

  // Finally, update unary constraints on var.
  PPL_DIRTY_TEMP(N, lf_ub);
  linear_form_upper_bound(lf, lf_ub);
  PPL_DIRTY_TEMP(N, minus_lf_ub);
  linear_form_upper_bound(minus_lf, minus_lf_ub);
  mul_2exp_assign_r(lf_ub, lf_ub, 1, ROUND_UP);
  assign_r(matrix[n_var + 1][n_var], lf_ub, ROUND_NOT_NEEDED);
  mul_2exp_assign_r(minus_lf_ub, minus_lf_ub, 1, ROUND_UP);
  assign_r(matrix[n_var][n_var + 1], minus_lf_ub, ROUND_NOT_NEEDED);

  PPL_ASSERT(OK());
}

template <typename T>
template <typename Interval_Info>
void
Octagonal_Shape<T>::
linear_form_upper_bound(const Linear_Form< Interval<T, Interval_Info> >& lf,
                        N& result) const {

  // Check that T is a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<T>::is_exact,
                     "Octagonal_Shape<T>::linear_form_upper_bound:"
                     " T not a floating point type.");

  const dimension_type lf_space_dimension = lf.space_dimension();
  PPL_ASSERT(lf_space_dimension <= space_dim);

  typedef Interval<T, Interval_Info> FP_Interval_Type;

  PPL_DIRTY_TEMP(N, curr_lb);
  PPL_DIRTY_TEMP(N, curr_ub);
  PPL_DIRTY_TEMP(N, curr_var_ub);
  PPL_DIRTY_TEMP(N, curr_minus_var_ub);

  PPL_DIRTY_TEMP(N, first_comparison_term);
  PPL_DIRTY_TEMP(N, second_comparison_term);

  PPL_DIRTY_TEMP(N, negator);

  assign_r(result, lf.inhomogeneous_term().upper(), ROUND_NOT_NEEDED);

  for (dimension_type curr_var = 0, n_var = 0; curr_var < lf_space_dimension;
       ++curr_var) {
    const FP_Interval_Type& curr_coefficient =
                            lf.coefficient(Variable(curr_var));
    assign_r(curr_lb, curr_coefficient.lower(), ROUND_NOT_NEEDED);
    assign_r(curr_ub, curr_coefficient.upper(), ROUND_NOT_NEEDED);
    if (curr_lb != 0 || curr_ub != 0) {
      assign_r(curr_var_ub, matrix[n_var + 1][n_var], ROUND_NOT_NEEDED);
      div_2exp_assign_r(curr_var_ub, curr_var_ub, 1, ROUND_UP);
      neg_assign_r(curr_minus_var_ub, matrix[n_var][n_var + 1],
                   ROUND_NOT_NEEDED);
      div_2exp_assign_r(curr_minus_var_ub, curr_minus_var_ub, 1, ROUND_DOWN);
      // Optimize the most common case: curr = +/-[1, 1].
      if (curr_lb == 1 && curr_ub == 1) {
        add_assign_r(result, result, std::max(curr_var_ub, curr_minus_var_ub),
                     ROUND_UP);
      }
      else if (curr_lb == -1 && curr_ub == -1) {
        neg_assign_r(negator, std::min(curr_var_ub, curr_minus_var_ub),
                     ROUND_NOT_NEEDED);
        add_assign_r(result, result, negator, ROUND_UP);
      }
      else {
        // Next addend will be the maximum of four quantities.
        assign_r(first_comparison_term, 0, ROUND_NOT_NEEDED);
        assign_r(second_comparison_term, 0, ROUND_NOT_NEEDED);
        add_mul_assign_r(first_comparison_term, curr_var_ub, curr_ub,
                         ROUND_UP);
        add_mul_assign_r(second_comparison_term, curr_var_ub, curr_lb,
                         ROUND_UP);
        assign_r(first_comparison_term, std::max(first_comparison_term,
                                                 second_comparison_term),
                 ROUND_NOT_NEEDED);
        assign_r(second_comparison_term, 0, ROUND_NOT_NEEDED);
        add_mul_assign_r(second_comparison_term, curr_minus_var_ub, curr_ub,
                         ROUND_UP);
        assign_r(first_comparison_term, std::max(first_comparison_term,
                                                 second_comparison_term),
                 ROUND_NOT_NEEDED);
        assign_r(second_comparison_term, 0, ROUND_NOT_NEEDED);
        add_mul_assign_r(second_comparison_term, curr_minus_var_ub, curr_lb,
                         ROUND_UP);
        assign_r(first_comparison_term, std::max(first_comparison_term,
                                                 second_comparison_term),
                 ROUND_NOT_NEEDED);

        add_assign_r(result, result, first_comparison_term, ROUND_UP);
      }
    }

    n_var += 2;
  }
}

template <typename T>
void
Octagonal_Shape<T>::
interval_coefficient_upper_bound(const N& var_ub, const N& minus_var_ub,
                                 const N& int_ub, const N& int_lb,
                                 N& result) {

  // Check that T is a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<T>::is_exact,
                     "Octagonal_Shape<T>::interval_coefficient_upper_bound:"
                     " T not a floating point type.");

  // NOTE: we store the first comparison term directly into result.
  PPL_DIRTY_TEMP(N, second_comparison_term);
  PPL_DIRTY_TEMP(N, third_comparison_term);
  PPL_DIRTY_TEMP(N, fourth_comparison_term);

  assign_r(result, 0, ROUND_NOT_NEEDED);
  assign_r(second_comparison_term, 0, ROUND_NOT_NEEDED);
  assign_r(third_comparison_term, 0, ROUND_NOT_NEEDED);
  assign_r(fourth_comparison_term, 0, ROUND_NOT_NEEDED);

  add_mul_assign_r(result, var_ub, int_ub, ROUND_UP);
  add_mul_assign_r(second_comparison_term, minus_var_ub, int_ub, ROUND_UP);
  add_mul_assign_r(third_comparison_term, var_ub, int_lb, ROUND_UP);
  add_mul_assign_r(fourth_comparison_term, minus_var_ub, int_lb, ROUND_UP);

  assign_r(result, std::max(result, second_comparison_term), ROUND_NOT_NEEDED);
  assign_r(result, std::max(result, third_comparison_term), ROUND_NOT_NEEDED);
  assign_r(result, std::max(result, fourth_comparison_term), ROUND_NOT_NEEDED);
}

template <typename T>
void
Octagonal_Shape<T>::affine_preimage(const Variable var,
                                    const Linear_Expression& expr,
                                    Coefficient_traits::const_reference
                                    denominator) {

  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("affine_preimage(v, e, d)", "d == 0");
  }

  // Dimension-compatibility checks.
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible("affine_preimage(v, e, d)", "e", expr);
  }

  // `var' should be one of the dimensions of the octagon.
  dimension_type var_id = var.id();
  if (space_dim < var_id + 1) {
    throw_dimension_incompatible("affine_preimage(v, e, d)", var_id + 1);
  }

  strong_closure_assign();
  // The image of an empty octagon is empty too.
  if (marked_empty()) {
    return;
  }

  const Coefficient& b = expr.inhomogeneous_term();

  // Number of non-zero coefficients in `expr': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;

  // Variable-index of the last non-zero coefficient in `expr', if any.
  dimension_type w_id = expr.last_nonzero();

  if (w_id != 0) {
    ++t;
    if (!expr.all_zeroes(1, w_id)) {
      ++t;
    }
    --w_id;
  }

  // `w' is the variable with index `w_id'.
  // Now we know the form of `expr':
  // - If t == 0, then expr == b, with `b' a constant;
  // - If t == 1, then expr == a*w + b, where `w' can be `v' or another
  //   variable; in this second case we have to check whether `a' is
  //   equal to `denominator' or `-denominator', since otherwise we have
  //   to fall back on the general form;
  // - If t == 2, the `expr' is of the general form.

  if (t == 0) {
    // Case 1: expr = n; remove all constraints on `var'.
    forget_all_octagonal_constraints(var_id);
    PPL_ASSERT(OK());
    return;
  }

  if (t == 1) {
    // Value of the one and only non-zero coefficient in `expr'.
    const Coefficient& w_coeff = expr.coefficient(Variable(w_id));
    if (w_coeff == denominator || w_coeff == -denominator) {
      // Case 2: expr = w_coeff*w + b, with w_coeff = +/- denominator.
      if (w_id == var_id) {
        // Apply affine_image() on the inverse of this transformation.
        affine_image(var, denominator*var - b, w_coeff);
      }
      else {
        // `expr == w_coeff*w + b', where `w != var'.
        // Remove all constraints on `var'.
        forget_all_octagonal_constraints(var_id);
        PPL_ASSERT(OK());
      }
      return;
    }
  }
  // General case.
  // Either t == 2, so that
  // expr = a_1*x_1 + a_2*x_2 + ... + a_n*x_n + b, where n >= 2,
  // or t = 1, expr = a*w + b, but a <> +/- denominator.
  const Coefficient& coeff_v = expr.coefficient(var);
  if (coeff_v != 0) {
    if (coeff_v > 0) {
      // The transformation is invertible.
      Linear_Expression inverse = ((coeff_v + denominator)*var);
      inverse -= expr;
      affine_image(var, inverse, coeff_v);
    }
    else {
      // The transformation is invertible.
      PPL_DIRTY_TEMP_COEFFICIENT(minus_coeff_v);
      neg_assign(minus_coeff_v, coeff_v);
      Linear_Expression inverse = ((minus_coeff_v - denominator)*var);
      inverse += expr;
      affine_image(var, inverse, minus_coeff_v);
    }
  }
  else {
    // The transformation is not invertible: all constraints on `var' are lost.
    forget_all_octagonal_constraints(var_id);
    PPL_ASSERT(OK());
  }
}

template <typename T>
void
Octagonal_Shape<T>
::generalized_affine_image(const Variable var,
                           const Relation_Symbol relsym,
                           const Linear_Expression&  expr ,
                           Coefficient_traits::const_reference denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("generalized_affine_image(v, r, e, d)", "d == 0");
  }

  // Dimension-compatibility checks.
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible("generalized_affine_image(v, r, e, d)", "e",
                                 expr);
  }

  // `var' should be one of the dimensions of the octagon.
  dimension_type var_id = var.id();
  if (space_dim < var_id + 1) {
    throw_dimension_incompatible("generalized_affine_image(v, r, e, d)",
                                 var_id + 1);
  }

  // The relation symbol cannot be a strict relation symbol.
  if (relsym == LESS_THAN || relsym == GREATER_THAN) {
    throw_invalid_argument("generalized_affine_image(v, r, e, d)",
                           "r is a strict relation symbol");
  }
  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL) {
    throw_invalid_argument("generalized_affine_image(v, r, e, d)",
                           "r is the disequality relation symbol");
  }

  if (relsym == EQUAL) {
    // The relation symbol is "=":
    // this is just an affine image computation.
    affine_image(var, expr, denominator);
    return;
  }

  strong_closure_assign();
  // The image of an empty octagon is empty too.
  if (marked_empty()) {
    return;
  }

  // Number of non-zero coefficients in `expr': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;
  // Variable-index of the last non-zero coefficient in `expr', if any.
  dimension_type w_id = expr.last_nonzero();

  if (w_id != 0) {
    ++t;
    if (!expr.all_zeroes(1, w_id)) {
      ++t;
    }
    --w_id;
  }

  typedef typename OR_Matrix<N>::row_iterator row_iterator;
  typedef typename OR_Matrix<N>::row_reference_type row_reference;
  typedef typename OR_Matrix<N>::const_row_iterator Row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type Row_reference;

  const row_iterator m_begin = matrix.row_begin();
  const row_iterator m_end = matrix.row_end();
  const dimension_type n_var = 2*var_id;
  const Coefficient& b = expr.inhomogeneous_term();
  PPL_DIRTY_TEMP_COEFFICIENT(minus_denom);
  neg_assign_r(minus_denom, denominator, ROUND_NOT_NEEDED);

  // `w' is the variable with index `w_id'.
  // Now we know the form of `expr':
  // - If t == 0, then expr == b, with `b' a constant;
  // - If t == 1, then expr == a*w + b, where `w' can be `v' or another
  //   variable; in this second case we have to check whether `a' is
  //   equal to `denominator' or `-denominator', since otherwise we have
  //   to fall back on the general form;
  // - If t == 2, the `expr' is of the general form.

  if (t == 0) {
    // Case 1: expr = b.
    PPL_DIRTY_TEMP_COEFFICIENT(two_b);
    two_b = 2*b;
    // Remove all constraints on `var'.
    forget_all_octagonal_constraints(var_id);
    // Strong closure is lost.
    reset_strongly_closed();
    switch (relsym) {
    case LESS_OR_EQUAL:
      // Add the constraint `var <= b/denominator'.
      add_octagonal_constraint(n_var + 1, n_var, two_b, denominator);
      break;
    case GREATER_OR_EQUAL:
      // Add the constraint `var >= n/denominator',
      // i.e., `-var <= -b/denominator'.
      add_octagonal_constraint(n_var, n_var + 1, two_b, minus_denom);
      break;
    default:
      // We already dealt with the other cases.
      PPL_UNREACHABLE;
      break;
    }
    PPL_ASSERT(OK());
    return;
  }

  if (t == 1) {
    // The one and only non-zero homogeneous coefficient in `expr'.
    const Coefficient& w_coeff = expr.coefficient(Variable(w_id));
    if (w_coeff == denominator || w_coeff == minus_denom) {
      // Case 2: expr == w_coeff*w + b, with w_coeff == +/- denominator.
      switch (relsym) {
      case LESS_OR_EQUAL:
        {
          PPL_DIRTY_TEMP(N, d);
          div_round_up(d, b, denominator);
          if (w_id == var_id) {
            // Here `expr' is of the form: +/- denominator * v + b.
            // Strong closure is not preserved.
            reset_strongly_closed();
            if (w_coeff == denominator) {
              // Translate all the constraints of the form `v - w <= cost'
              // into the constraint `v - w <= cost + b/denominator';
              // forget each constraint `w - v <= cost1'.
              row_iterator m_iter = m_begin + n_var;
              row_reference m_v = *m_iter;
              N& m_v_cv = m_v[n_var + 1];
              ++m_iter;
              row_reference m_cv = *m_iter;
              N& m_cv_v = m_cv[n_var];
              ++m_iter;
              // NOTE: delay update of m_v_cv and m_cv_v.
              for ( ; m_iter != m_end; ++m_iter) {
                row_reference m_i = *m_iter;
                N& m_i_v = m_i[n_var];
                add_assign_r(m_i_v, m_i_v, d, ROUND_UP);
                assign_r(m_i[n_var + 1], PLUS_INFINITY, ROUND_NOT_NEEDED);
              }
              for (dimension_type k = n_var; k-- > 0; ) {
                assign_r(m_v[k], PLUS_INFINITY, ROUND_NOT_NEEDED);
                add_assign_r(m_cv[k], m_cv[k], d, ROUND_UP);
              }
              mul_2exp_assign_r(d, d, 1, ROUND_UP);
              add_assign_r(m_cv_v, m_cv_v, d, ROUND_UP);
              assign_r(m_v_cv, PLUS_INFINITY, ROUND_NOT_NEEDED);
            }
            else {
              // Here `w_coeff == -denominator'.
              // `expr' is of the form: -a*var + b.
              N& m_v_cv = matrix[n_var][n_var + 1];
              mul_2exp_assign_r(d, d, 1, ROUND_UP);
              add_assign_r(matrix[n_var + 1][n_var], m_v_cv, d, ROUND_UP);
              assign_r(m_v_cv, PLUS_INFINITY, ROUND_NOT_NEEDED);
              forget_binary_octagonal_constraints(var_id);
            }
          }
          else {
            // Here `w != v', so that `expr' is the form
            // +/- denominator*w + b.
            // Remove all constraints on `v'.
            forget_all_octagonal_constraints(var_id);
            const dimension_type n_w = 2*w_id;
            if (w_coeff == denominator) {
              // Add the new constraint `v - w <= b/denominator'.
              if (var_id < w_id) {
                add_octagonal_constraint(n_w, n_var, b, denominator);
              }
              else {
                add_octagonal_constraint(n_var + 1, n_w + 1, b, denominator);
              }
            }
            else {
              // Add the new constraint `v + w <= b/denominator'.
              if (var_id < w_id) {
                add_octagonal_constraint(n_w + 1, n_var, b, denominator);
              }
              else {
                add_octagonal_constraint(n_var + 1, n_w, b, denominator);
              }
            }
          }
          break;
        }

      case GREATER_OR_EQUAL:
        {
          PPL_DIRTY_TEMP(N, d);
          div_round_up(d, b, minus_denom);
          if (w_id == var_id) {
            // Here `expr' is of the form: +/- denominator * v + b.
            // Strong closure is not preserved.
            reset_strongly_closed();
            if (w_coeff == denominator) {
              // Translate each constraint `w - v <= cost'
              // into the constraint `w - v <= cost - b/denominator';
              // forget each constraint `v - w <= cost1'.
              row_iterator m_iter = m_begin + n_var;
              row_reference m_v = *m_iter;
              N& m_v_cv = m_v[n_var + 1];
              ++m_iter;
              row_reference m_cv = *m_iter;
              N& m_cv_v = m_cv[n_var];
              ++m_iter;
              // NOTE: delay update of m_v_cv and m_cv_v.
              for ( ; m_iter != m_end; ++m_iter) {
                row_reference m_i = *m_iter;
                assign_r(m_i[n_var], PLUS_INFINITY, ROUND_NOT_NEEDED);
                add_assign_r(m_i[n_var + 1], m_i[n_var + 1], d, ROUND_UP);
              }
              for (dimension_type k = n_var; k-- > 0; ) {
                add_assign_r(m_v[k], m_v[k], d, ROUND_UP);
                assign_r(m_cv[k], PLUS_INFINITY, ROUND_NOT_NEEDED);
              }
              mul_2exp_assign_r(d, d, 1, ROUND_UP);
              add_assign_r(m_v_cv, m_v_cv, d, ROUND_UP);
              assign_r(m_cv_v, PLUS_INFINITY, ROUND_NOT_NEEDED);
            }
            else {
              // Here `w_coeff == -denominator'.
              // `expr' is of the form: -a*var + b.
              N& m_cv_v = matrix[n_var + 1][n_var];
              mul_2exp_assign_r(d, d, 1, ROUND_UP);
              add_assign_r(matrix[n_var][n_var + 1], m_cv_v, d, ROUND_UP);
              assign_r(m_cv_v, PLUS_INFINITY, ROUND_NOT_NEEDED);
              forget_binary_octagonal_constraints(var_id);
            }
          }
          else {
            // Here `w != v', so that `expr' is of the form
            // +/-denominator * w + b, with `w != v'.
            // Remove all constraints on `v'.
            forget_all_octagonal_constraints(var_id);
            const dimension_type n_w = 2*w_id;
            // We have got an expression of the following form:
            // var1 + n, with `var1' != `var'.
            // We remove all constraints of the form `var (+/- var1) >= const'
            // and we add the new constraint `var +/- var1 >= n/denominator'.
            if (w_coeff == denominator) {
              // Add the new constraint `var - w >= b/denominator',
              // i.e., `w - var <= -b/denominator'.
              if (var_id < w_id) {
                add_octagonal_constraint(n_w + 1, n_var + 1, b, minus_denom);
              }
              else {
                add_octagonal_constraint(n_var, n_w, b, minus_denom);
              }
            }
            else {
              // Add the new constraint `var + w >= b/denominator',
              // i.e., `-w - var <= -b/denominator'.
              if (var_id < w_id) {
                add_octagonal_constraint(n_w, n_var + 1, b, minus_denom);
              }
              else {
                add_octagonal_constraint(n_var, n_w + 1, b, minus_denom);
              }
            }
          }
          break;
        }

      default:
        // We already dealt with the other cases.
        PPL_UNREACHABLE;
        break;
      }
      PPL_ASSERT(OK());
      return;
    }
  }

  // General case.
  // Either t == 2, so that
  // expr == a_1*x_1 + a_2*x_2 + ... + a_n*x_n + b, where n >= 2,
  // or t == 1, expr == a*w + b, but a <> +/- denominator.
  // We will remove all the constraints on `v' and add back
  // a constraint providing an upper or a lower bound for `v'
  // (depending on `relsym').
  const bool is_sc = (denominator > 0);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_b);
  neg_assign(minus_b, b);
  const Coefficient& sc_b = is_sc ? b : minus_b;
  const Coefficient& minus_sc_b = is_sc ? minus_b : b;
  const Coefficient& sc_denom = is_sc ? denominator : minus_denom;
  const Coefficient& minus_sc_denom = is_sc ? minus_denom : denominator;
  // NOTE: here, for optimization purposes, `minus_expr' is only assigned
  // when `denominator' is negative. Do not use it unless you are sure
  // it has been correctly assigned.
  Linear_Expression minus_expr;
  if (!is_sc) {
    minus_expr = -expr;
  }
  const Linear_Expression& sc_expr = is_sc ? expr : minus_expr;

  PPL_DIRTY_TEMP(N, sum);
  // Index of variable that is unbounded in `this->matrix'.
  PPL_UNINITIALIZED(dimension_type, pinf_index);
  // Number of unbounded variables found.
  dimension_type pinf_count = 0;

  switch (relsym) {
  case LESS_OR_EQUAL:
    {
      // Compute an upper approximation for `sc_expr' into `sum'.

      // Approximate the inhomogeneous term.
      assign_r(sum, sc_b, ROUND_UP);
      // Approximate the homogeneous part of `sc_expr'.
      PPL_DIRTY_TEMP(N, coeff_i);
      PPL_DIRTY_TEMP(N, approx_i);
      PPL_DIRTY_TEMP_COEFFICIENT(minus_sc_i);
      // Note: indices above `w' can be disregarded, as they all have
      // a zero coefficient in `sc_expr'.
      for (Row_iterator m_iter = m_begin, m_iter_end = m_begin + (2 * w_id + 2);
           m_iter != m_iter_end; ) {
        const dimension_type n_i = m_iter.index();
        const dimension_type id = n_i/2;
        Row_reference m_i = *m_iter;
        ++m_iter;
        Row_reference m_ci = *m_iter;
        ++m_iter;
        const Coefficient& sc_i = sc_expr.coefficient(Variable(id));
        const int sign_i = sgn(sc_i);
        if (sign_i == 0) {
          continue;
        }
        // Choose carefully: we are approximating `sc_expr'.
        const N& double_approx_i = (sign_i > 0) ? m_ci[n_i] : m_i[n_i + 1];
        if (is_plus_infinity(double_approx_i)) {
          if (++pinf_count > 1) {
            break;
          }
          pinf_index = id;
          continue;
        }
        if (sign_i > 0) {
          assign_r(coeff_i, sc_i, ROUND_UP);
        }
        else {
          neg_assign(minus_sc_i, sc_i);
          assign_r(coeff_i, minus_sc_i, ROUND_UP);
        }
        div_2exp_assign_r(approx_i, double_approx_i, 1, ROUND_UP);
        add_mul_assign_r(sum, coeff_i, approx_i, ROUND_UP);
      }
      // Remove all constraints on `v'.
      forget_all_octagonal_constraints(var_id);
      reset_strongly_closed();
      // Return immediately if no approximation could be computed.
      if (pinf_count > 1) {
        PPL_ASSERT(OK());
        return;
      }

      // Divide by the (sign corrected) denominator (if needed).
      if (sc_denom != 1) {
        // Before computing the quotient, the denominator should be
        // approximated towards zero. Since `sc_denom' is known to be
        // positive, this amounts to rounding downwards, which is
        // achieved as usual by rounding upwards
        // `minus_sc_denom' and negating again the result.
        PPL_DIRTY_TEMP(N, down_sc_denom);
        assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
        neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
        div_assign_r(sum, sum, down_sc_denom, ROUND_UP);
      }

      if (pinf_count == 0) {
        // Add the constraint `v <= pos_sum'.
        PPL_DIRTY_TEMP(N, double_sum);
        mul_2exp_assign_r(double_sum, sum, 1, ROUND_UP);
        matrix[n_var + 1][n_var] = double_sum;
        // Deduce constraints of the form `v +/- u', where `u != v'.
        deduce_v_pm_u_bounds(var_id, w_id, sc_expr, sc_denom, sum);
      }
      else if (pinf_count == 1) {
        if (pinf_index != var_id) {
          const Coefficient& pi = expr.coefficient(Variable(pinf_index));
          if (pi == denominator ) {
            // Add the constraint `v - pinf_index <= sum'.
            if (var_id < pinf_index) {
              matrix[2*pinf_index][n_var] = sum;
            }
            else {
              matrix[n_var + 1][2*pinf_index + 1] = sum;
            }
          }
          else {
            if (pi == minus_denom) {
              // Add the constraint `v + pinf_index <= sum'.
              if (var_id < pinf_index) {
                matrix[2*pinf_index + 1][n_var] = sum;
              }
              else {
                matrix[n_var + 1][2*pinf_index] = sum;
              }
            }
          }
        }
      }
      break;
    }

  case GREATER_OR_EQUAL:
    {
      // Compute an upper approximation for `-sc_expr' into `sum'.
      // Note: approximating `-sc_expr' from above and then negating the
      // result is the same as approximating `sc_expr' from below.

      // Approximate the inhomogeneous term.
      assign_r(sum, minus_sc_b, ROUND_UP);
      PPL_DIRTY_TEMP(N, coeff_i);
      PPL_DIRTY_TEMP_COEFFICIENT(minus_sc_i);
      PPL_DIRTY_TEMP(N, approx_i);
      // Approximate the homogeneous part of `-sc_expr'.
      for (Row_iterator m_iter = m_begin, m_iter_end = m_begin + (2 * w_id + 2);
           m_iter != m_iter_end; ) {
        const dimension_type n_i = m_iter.index();
        const dimension_type id = n_i/2;
        Row_reference m_i = *m_iter;
        ++m_iter;
        Row_reference m_ci = *m_iter;
        ++m_iter;
        const Coefficient& sc_i = sc_expr.coefficient(Variable(id));
        const int sign_i = sgn(sc_i);
        if (sign_i == 0) {
          continue;
        }
        // Choose carefully: we are approximating `-sc_expr'.
        const N& double_approx_i = (sign_i > 0) ? m_i[n_i + 1] : m_ci[n_i];
        if (is_plus_infinity(double_approx_i)) {
          if (++pinf_count > 1) {
            break;
          }
          pinf_index = id;
          continue;
        }
        if (sign_i > 0) {
          assign_r(coeff_i, sc_i, ROUND_UP);
        }
        else {
          neg_assign(minus_sc_i, sc_i);
          assign_r(coeff_i, minus_sc_i, ROUND_UP);
        }
        div_2exp_assign_r(approx_i, double_approx_i, 1, ROUND_UP);
        add_mul_assign_r(sum, coeff_i, approx_i, ROUND_UP);
      }

      // Remove all constraints on `var'.
      forget_all_octagonal_constraints(var_id);
      reset_strongly_closed();
      // Return immediately if no approximation could be computed.
      if (pinf_count > 1) {
        PPL_ASSERT(OK());
        return;
      }

      // Divide by the (sign corrected) denominator (if needed).
      if (sc_denom != 1) {
        // Before computing the quotient, the denominator should be
        // approximated towards zero. Since `sc_denom' is known to be
        // positive, this amounts to rounding downwards, which is
        // achieved as usual by rounding upwards
        // `minus_sc_denom' and negating again the result.
        PPL_DIRTY_TEMP(N, down_sc_denom);
        assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
        neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
        div_assign_r(sum, sum, down_sc_denom, ROUND_UP);
      }

      if (pinf_count == 0) {
        // Add the constraint `v >= -neg_sum', i.e., `-v <= neg_sum'.
        PPL_DIRTY_TEMP(N, double_sum);
        mul_2exp_assign_r(double_sum, sum, 1, ROUND_UP);
        matrix[n_var][n_var + 1] = double_sum;
        // Deduce constraints of the form `-v +/- u', where `u != v'.
        deduce_minus_v_pm_u_bounds(var_id, pinf_index, sc_expr, sc_denom, sum);
      }
      else if (pinf_count == 1) {
        if (pinf_index != var_id) {
          const Coefficient& pi = expr.coefficient(Variable(pinf_index));
          if (pi == denominator) {
            // Add the constraint `v - pinf_index >= -sum',
            // i.e., `pinf_index - v <= sum'.
            if (pinf_index < var_id) {
              matrix[n_var][2*pinf_index] = sum;
            }
            else {
              matrix[2*pinf_index + 1][n_var + 1] = sum;
            }
          }
          else {
            if (pi == minus_denom) {
              // Add the constraint `v + pinf_index >= -sum',
              // i.e., `-pinf_index - v <= sum'.
              if (pinf_index < var_id) {
                matrix[n_var][2*pinf_index + 1] = sum;
              }
              else {
                matrix[2*pinf_index][n_var + 1] = sum;
              }
            }
          }
        }
      }
      break;
    }

  default:
    // We already dealt with the other cases.
    PPL_UNREACHABLE;
    break;
  }
  incremental_strong_closure_assign(var);
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::generalized_affine_image(const Linear_Expression& lhs,
                                             const Relation_Symbol relsym,
                                             const Linear_Expression& rhs) {
  // Dimension-compatibility checks.
  // The dimension of `lhs' should not be greater than the dimension
  // of `*this'.
  dimension_type lhs_space_dim = lhs.space_dimension();
  if (space_dim < lhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_image(e1, r, e2)",
                                 "e1", lhs);
  }
  // The dimension of `rhs' should not be greater than the dimension
  // of `*this'.
  const dimension_type rhs_space_dim = rhs.space_dimension();
  if (space_dim < rhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_image(e1, r, e2)",
                                 "e2", rhs);
  }

  // Strict relation symbols are not admitted for octagons.
  if (relsym == LESS_THAN || relsym == GREATER_THAN) {
    throw_invalid_argument("generalized_affine_image(e1, r, e2)",
                           "r is a strict relation symbol");
  }
  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL) {
    throw_invalid_argument("generalized_affine_image(e1, r, e2)",
                           "r is the disequality relation symbol");
  }

  strong_closure_assign();
  // The image of an empty octagon is empty.
  if (marked_empty()) {
    return;
  }

  // Number of non-zero coefficients in `lhs': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t_lhs = 0;
  // Index of the last non-zero coefficient in `lhs', if any.
  dimension_type j_lhs = lhs.last_nonzero();

  if (j_lhs != 0) {
    ++t_lhs;
    if (!lhs.all_zeroes(1, j_lhs)) {
      ++t_lhs;
    }
    --j_lhs;
  }

  const Coefficient& b_lhs = lhs.inhomogeneous_term();

  if (t_lhs == 0) {
    // `lhs' is a constant.
    // In principle, it is sufficient to add the constraint `lhs relsym rhs'.
    // Note that this constraint is an octagonal difference if `t_rhs <= 1'
    // or `t_rhs > 1' and `rhs == a*v - a*w + b_rhs' or
    // `rhs == a*v + a*w + b_rhs'. If `rhs' is of a
    // more general form, it will be simply ignored.
    // TODO: if it is not an octagonal difference, should we compute
    // approximations for this constraint?
    switch (relsym) {
    case LESS_OR_EQUAL:
      refine_no_check(lhs <= rhs);
      break;
    case EQUAL:
      refine_no_check(lhs == rhs);
      break;
    case GREATER_OR_EQUAL:
      refine_no_check(lhs >= rhs);
      break;
    default:
      // We already dealt with the other cases.
      PPL_UNREACHABLE;
      break;
    }
  }

  else if (t_lhs == 1) {
    // Here `lhs == a_lhs * v + b_lhs'.
    // Independently from the form of `rhs', we can exploit the
    // method computing generalized affine images for a single variable.
    Variable v(j_lhs);
    // Compute a sign-corrected relation symbol.
    const Coefficient& denom = lhs.coefficient(v);
    Relation_Symbol new_relsym = relsym;
    if (denom < 0) {
      if (relsym == LESS_OR_EQUAL) {
        new_relsym = GREATER_OR_EQUAL;
      }
      else if (relsym == GREATER_OR_EQUAL) {
        new_relsym = LESS_OR_EQUAL;
      }
    }
    Linear_Expression expr = rhs - b_lhs;
    generalized_affine_image(v, new_relsym, expr, denom);
  }
  else {
    // Here `lhs' is of the general form, having at least two variables.
    // Compute the set of variables occurring in `lhs'.
    std::vector<Variable> lhs_vars;
    for (Linear_Expression::const_iterator i = lhs.begin(), i_end = lhs.end();
          i != i_end; ++i) {
      lhs_vars.push_back(i.variable());
    }

    const dimension_type num_common_dims = std::min(lhs_space_dim, rhs_space_dim);
    if (!lhs.have_a_common_variable(rhs, Variable(0), Variable(num_common_dims))) {
      // `lhs' and `rhs' variables are disjoint.
      // Existentially quantify all variables in the lhs.
      for (dimension_type i = lhs_vars.size(); i-- > 0; ) {
        dimension_type lhs_vars_i = lhs_vars[i].id();
        forget_all_octagonal_constraints(lhs_vars_i);
      }
      // Constrain the left hand side expression so that it is related to
      // the right hand side expression as dictated by `relsym'.
      // TODO: if the following constraint is NOT an octagonal difference,
      // it will be simply ignored. Should we compute approximations for it?
      switch (relsym) {
      case LESS_OR_EQUAL:
        refine_no_check(lhs <= rhs);
        break;
      case EQUAL:
        refine_no_check(lhs == rhs);
        break;
      case GREATER_OR_EQUAL:
        refine_no_check(lhs >= rhs);
        break;
      default:
        // We already dealt with the other cases.
        PPL_UNREACHABLE;
        break;
      }
    }
    else {
      // Some variables in `lhs' also occur in `rhs'.

#if 1 // Simplified computation (see the TODO note below).

      for (dimension_type i = lhs_vars.size(); i-- > 0; ) {
        dimension_type lhs_vars_i = lhs_vars[i].id();
        forget_all_octagonal_constraints(lhs_vars_i);
      }

#else // Currently unnecessarily complex computation.

      // More accurate computation that is worth doing only if
      // the following TODO note is accurately dealt with.

      // To ease the computation, we add an additional dimension.
      const Variable new_var(space_dim);
      add_space_dimensions_and_embed(1);
      // Constrain the new dimension to be equal to `rhs'.
      // NOTE: calling affine_image() instead of refine_no_check()
      // ensures some approximation is tried even when the constraint
      // is not an octagonal constraint.
      affine_image(new_var, rhs);
      // Existentially quantify all variables in the lhs.
      // NOTE: enforce strong closure for precision.
      strong_closure_assign();
      PPL_ASSERT(!marked_empty());
      for (dimension_type i = lhs_vars.size(); i-- > 0; ) {
        dimension_type lhs_vars_i = lhs_vars[i].id();
        forget_all_octagonal_constraints(lhs_vars_i);
      }
      // Constrain the new dimension so that it is related to
      // the left hand side as dictated by `relsym'.
      // TODO: each one of the following constraints is definitely NOT
      // an octagonal difference (since it has 3 variables at least).
      // Thus, the method refine_no_check() will simply ignore it.
      // Should we compute approximations for this constraint?
      switch (relsym) {
      case LESS_OR_EQUAL:
        refine_no_check(lhs <= new_var);
        break;
      case EQUAL:
        refine_no_check(lhs == new_var);
        break;
      case GREATER_OR_EQUAL:
        refine_no_check(lhs >= new_var);
        break;
      default:
        // We already dealt with the other cases.
        PPL_UNREACHABLE;
        break;
      }
      // Remove the temporarily added dimension.
      remove_higher_space_dimensions(space_dim-1);
#endif // Currently unnecessarily complex computation.
    }
  }

  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::bounded_affine_image(const Variable var,
                                         const Linear_Expression& lb_expr,
                                         const Linear_Expression& ub_expr,
                                         Coefficient_traits::const_reference
                                         denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("bounded_affine_image(v, lb, ub, d)", "d == 0");
  }

  // `var' should be one of the dimensions of the octagon.
  const dimension_type var_id = var.id();
  if (space_dim < var_id + 1) {
    throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)",
                                 var_id + 1);
  }

  // The dimension of `lb_expr' and `ub_expr' should not be
  // greater than the dimension of `*this'.
  const dimension_type lb_space_dim = lb_expr.space_dimension();
  if (space_dim < lb_space_dim) {
    throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)",
                                 "lb", lb_expr);
  }
  const dimension_type ub_space_dim = ub_expr.space_dimension();
  if (space_dim < ub_space_dim) {
    throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)",
                                 "ub", ub_expr);
  }

  strong_closure_assign();
  // The image of an empty octagon is empty too.
  if (marked_empty()) {
    return;
  }

  // Number of non-zero coefficients in `lb_expr': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;
  // Variable-index of the last non-zero coefficient in `lb_expr', if any.
  dimension_type w_id = lb_expr.last_nonzero();

  if (w_id != 0) {
    ++t;
    if (!lb_expr.all_zeroes(1, w_id)) {
      ++t;
    }
    --w_id;
  }

  typedef typename OR_Matrix<N>::row_iterator row_iterator;
  typedef typename OR_Matrix<N>::const_row_iterator Row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type Row_reference;

  const row_iterator m_begin = matrix.row_begin();
  const dimension_type n_var = 2*var_id;
  const Coefficient& b = lb_expr.inhomogeneous_term();
  PPL_DIRTY_TEMP_COEFFICIENT(minus_denom);
  neg_assign_r(minus_denom, denominator, ROUND_NOT_NEEDED);

  // `w' is the variable with index `w_id'.
  // Now we know the form of `lb_expr':
  // - If t == 0, then lb_expr == b, with `b' a constant;
  // - If t == 1, then lb_expr == a*w + b, where `w' can be `v' or another
  //   variable; in this second case we have to check whether `a' is
  //   equal to `denominator' or `-denominator', since otherwise we have
  //   to fall back on the general form;
  // - If t == 2, the `lb_expr' is of the general form.

  if (t == 0) {
    // Case 1: lb_expr == b.
    generalized_affine_image(var,
                             LESS_OR_EQUAL,
                             ub_expr,
                             denominator);
    PPL_DIRTY_TEMP_COEFFICIENT(two_b);
    two_b = 2*b;
    // Add the constraint `var >= b/denominator'.
    add_octagonal_constraint(n_var, n_var + 1, two_b, minus_denom);
    PPL_ASSERT(OK());
    return;
  }

  if (t == 1) {
    // The one and only non-zero homogeneous coefficient in `lb_expr'.
    const Coefficient& w_coeff = lb_expr.coefficient(Variable(w_id));
    if (w_coeff == denominator || w_coeff == minus_denom) {
      // Case 2: lb_expr = w_coeff*w + b, with w_coeff = +/- denominator.
      if (w_id == var_id) {
        // Here `var' occurs in `lb_expr'.
        // To ease the computation, we add an additional dimension.
        const Variable new_var(space_dim);
        add_space_dimensions_and_embed(1);
        // Constrain the new dimension to be equal to `lb_expr'.
        // Here `lb_expr' is of the form: +/- denominator * v + b.
        affine_image(new_var, lb_expr, denominator);
        // Enforce the strong closure for precision.
        strong_closure_assign();
        PPL_ASSERT(!marked_empty());
        // Apply the affine upper bound.
        generalized_affine_image(var,
                                 LESS_OR_EQUAL,
                                 ub_expr,
                                 denominator);
        // Now apply the affine lower bound, as recorded in `new_var'
        refine_no_check(var >= new_var);
        // Remove the temporarily added dimension.
        remove_higher_space_dimensions(space_dim-1);
        return;
      }
      else {
        // Apply the affine upper bound.
        generalized_affine_image(var,
                                 LESS_OR_EQUAL,
                                 ub_expr,
                                 denominator);
        // Here `w != var', so that `lb_expr' is of the form
        // +/-denominator * w + b.
        const dimension_type n_w = 2*w_id;
        // Add the new constraint `var - w >= b/denominator'.
        if (w_coeff == denominator) {
          if (var_id < w_id) {
            add_octagonal_constraint(n_w + 1, n_var + 1, b, minus_denom);
          }
          else {
            add_octagonal_constraint(n_var, n_w, b, minus_denom);
          }
        }
        else {
          // Add the new constraint `var + w >= b/denominator'.
          if (var_id < w_id) {
            add_octagonal_constraint(n_w, n_var + 1, b, minus_denom);
          }
          else {
            add_octagonal_constraint(n_var, n_w + 1, b, minus_denom);
          }
        }
        PPL_ASSERT(OK());
        return;
      }
    }
  }

  // General case.
  // Either t == 2, so that
  // expr == a_1*x_1 + a_2*x_2 + ... + a_n*x_n + b, where n >= 2,
  // or t == 1, expr == a*w + b, but a <> +/- denominator.
  // We will remove all the constraints on `var' and add back
  // constraints providing upper and lower bounds for `var'.

  // Compute upper approximations for `expr' and `-expr'
  // into `pos_sum' and `neg_sum', respectively, taking into account
  // the sign of `denominator'.
  // Note: approximating `-expr' from above and then negating the
  // result is the same as approximating `expr' from below.
  const bool is_sc = (denominator > 0);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_b);
  neg_assign_r(minus_b, b, ROUND_NOT_NEEDED);

  const Coefficient& minus_sc_b = is_sc ? minus_b : b;
  const Coefficient& sc_denom = is_sc ? denominator : minus_denom;
  const Coefficient& minus_sc_denom = is_sc ? minus_denom : denominator;
  // NOTE: here, for optimization purposes, `minus_expr' is only assigned
  // when `denominator' is negative. Do not use it unless you are sure
  // it has been correctly assigned.
  Linear_Expression minus_expr;
  if (!is_sc) {
    minus_expr = -lb_expr;
  }
  const Linear_Expression& sc_expr = is_sc ? lb_expr : minus_expr;

  PPL_DIRTY_TEMP(N, neg_sum);
  // Indices of the variables that are unbounded in `this->matrix'.
  PPL_UNINITIALIZED(dimension_type, neg_pinf_index);
  // Number of unbounded variables found.
  dimension_type neg_pinf_count = 0;

  // Approximate the inhomogeneous term.
  assign_r(neg_sum, minus_sc_b, ROUND_UP);

  // Approximate the homogeneous part of `sc_expr'.
  PPL_DIRTY_TEMP(N, coeff_i);
  PPL_DIRTY_TEMP(N, minus_coeff_i);
  PPL_DIRTY_TEMP(N, half);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_sc_i);
  // Note: indices above `w' can be disregarded, as they all have
  // a zero coefficient in `sc_expr'.
  for (Row_iterator m_iter = m_begin, m_iter_end = m_begin + (2 * w_id + 2);
       m_iter != m_iter_end; ) {
    const dimension_type n_i = m_iter.index();
    const dimension_type id = n_i/2;
    Row_reference m_i = *m_iter;
    ++m_iter;
    Row_reference m_ci = *m_iter;
    ++m_iter;
    const Coefficient& sc_i = sc_expr.coefficient(Variable(id));
    const int sign_i = sgn(sc_i);
    if (sign_i > 0) {
      assign_r(coeff_i, sc_i, ROUND_UP);
      // Approximating `-sc_expr'.
      if (neg_pinf_count <= 1) {
        const N& double_up_approx_minus_i = m_i[n_i + 1];
        if (!is_plus_infinity(double_up_approx_minus_i)) {
          // Let half = double_up_approx_minus_i / 2.
          div_2exp_assign_r(half, double_up_approx_minus_i, 1, ROUND_UP);
          add_mul_assign_r(neg_sum, coeff_i, half, ROUND_UP);
        }
        else {
          ++neg_pinf_count;
          neg_pinf_index = id;
        }
      }
    }
    else if (sign_i < 0) {
      neg_assign_r(minus_sc_i, sc_i, ROUND_NOT_NEEDED);
      assign_r(minus_coeff_i, minus_sc_i, ROUND_UP);
      // Approximating `-sc_expr'.
      if (neg_pinf_count <= 1) {
        const N& double_up_approx_i = m_ci[n_i];
        if (!is_plus_infinity(double_up_approx_i)) {
          // Let half = double_up_approx_i / 2.
          div_2exp_assign_r(half, double_up_approx_i, 1, ROUND_UP);
          add_mul_assign_r(neg_sum, minus_coeff_i, half, ROUND_UP);
        }
        else {
          ++neg_pinf_count;
          neg_pinf_index = id;
        }
      }
    }
  }

  // Apply the affine upper bound.
  generalized_affine_image(var,
                           LESS_OR_EQUAL,
                           ub_expr,
                           denominator);

  // Return immediately if no approximation could be computed.
  if (neg_pinf_count > 1) {
    return;
  }

  // In the following, strong closure will be definitely lost.
  reset_strongly_closed();

  // Exploit the lower approximation, if possible.
  if (neg_pinf_count <= 1) {
    // Compute quotient (if needed).
    if (sc_denom != 1) {
      // Before computing quotients, the denominator should be approximated
      // towards zero. Since `sc_denom' is known to be positive, this amounts to
      // rounding downwards, which is achieved as usual by rounding upwards
      // `minus_sc_denom' and negating again the result.
      PPL_DIRTY_TEMP(N, down_sc_denom);
      assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
      neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
      div_assign_r(neg_sum, neg_sum, down_sc_denom, ROUND_UP);
    }
    // Add the lower bound constraint, if meaningful.
    if (neg_pinf_count == 0) {
      // Add the constraint `v >= -neg_sum', i.e., `-v <= neg_sum'.
      PPL_DIRTY_TEMP(N, double_neg_sum);
      mul_2exp_assign_r(double_neg_sum, neg_sum, 1, ROUND_UP);
      matrix[n_var][n_var + 1] = double_neg_sum;
      // Deduce constraints of the form `-v +/- u', where `u != v'.
      deduce_minus_v_pm_u_bounds(var_id, w_id, sc_expr, sc_denom, neg_sum);
    }
    else
      // Here `neg_pinf_count == 1'.
      if (neg_pinf_index != var_id) {
        const Coefficient& npi = sc_expr.coefficient(Variable(neg_pinf_index));
        if (npi == sc_denom) {
          // Add the constraint `v - neg_pinf_index >= -neg_sum',
          // i.e., `neg_pinf_index - v <= neg_sum'.
          if (neg_pinf_index < var_id) {
            matrix[n_var][2*neg_pinf_index] = neg_sum;
          }
          else {
            matrix[2*neg_pinf_index + 1][n_var + 1] = neg_sum;
          }
        }
        else {
          if (npi == minus_sc_denom) {
            // Add the constraint `v + neg_pinf_index >= -neg_sum',
            // i.e., `-neg_pinf_index - v <= neg_sum'.
            if (neg_pinf_index < var_id) {
              matrix[n_var][2*neg_pinf_index + 1] = neg_sum;
            }
            else {
              matrix[2*neg_pinf_index][n_var + 1] = neg_sum;
            }
          }
        }
      }
  }

  PPL_ASSERT(OK());
}


template <typename T>
void
Octagonal_Shape<T>
::generalized_affine_preimage(const Variable var,
                              const Relation_Symbol relsym,
                              const Linear_Expression& expr,
                              Coefficient_traits::const_reference
                              denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("generalized_affine_preimage(v, r, e, d)", "d == 0");
  }

  // Dimension-compatibility checks.
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible("generalized_affine_preimage(v, r, e, d)",
                                 "e", expr);
  }

  // `var' should be one of the dimensions of the octagon.
  const dimension_type var_id = var.id();
  if (space_dim < var_id + 1) {
    throw_dimension_incompatible("generalized_affine_preimage(v, r, e, d)",
                                 var_id + 1);
  }

  // The relation symbol cannot be a strict relation symbol.
  if (relsym == LESS_THAN || relsym == GREATER_THAN) {
    throw_invalid_argument("generalized_affine_preimage(v, r, e, d)",
                           "r is a strict relation symbol");
  }
  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL) {
    throw_invalid_argument("generalized_affine_preimage(v, r, e, d)",
                           "r is the disequality relation symbol");
  }

  if (relsym == EQUAL) {
    // The relation symbol is "=":
    // this is just an affine preimage computation.
    affine_preimage(var, expr, denominator);
    return;
  }

  // The image of an empty octagon is empty too.
  strong_closure_assign();
  if (marked_empty()) {
    return;
  }

  // Check whether the preimage of this affine relation can be easily
  // computed as the image of its inverse relation.
  const Coefficient& expr_v = expr.coefficient(var);
  if (expr_v != 0) {
    const Relation_Symbol reversed_relsym = (relsym == LESS_OR_EQUAL)
      ? GREATER_OR_EQUAL : LESS_OR_EQUAL;
    const Linear_Expression inverse
      = expr - (expr_v + denominator)*var;
    PPL_DIRTY_TEMP_COEFFICIENT(inverse_denom);
    neg_assign(inverse_denom, expr_v);
    const Relation_Symbol inverse_relsym
      = (sgn(denominator) == sgn(inverse_denom)) ? relsym : reversed_relsym;
    generalized_affine_image(var, inverse_relsym, inverse, inverse_denom);
    return;
  }

  // Here `var_coefficient == 0', so that the preimage cannot
  // be easily computed by inverting the affine relation.
  // Shrink the Octagonal_Shape by adding the constraint induced
  // by the affine relation.
  refine(var, relsym, expr, denominator);

  // If the shrunk OS is empty, its preimage is empty too; ...
  if (is_empty()) {
    return;
  }
  // ...  otherwise, since the relation was not invertible,
  // we just forget all constraints on `var'.
  forget_all_octagonal_constraints(var_id);
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>
::generalized_affine_preimage(const Linear_Expression& lhs,
                              const Relation_Symbol relsym,
                              const Linear_Expression& rhs) {
  // Dimension-compatibility checks.
  // The dimension of `lhs' should not be greater than the dimension
  // of `*this'.
  dimension_type lhs_space_dim = lhs.space_dimension();
  if (space_dim < lhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_preimage(e1, r, e2)",
                                 "e1", lhs);
  }

  // The dimension of `rhs' should not be greater than the dimension
  // of `*this'.
  const dimension_type rhs_space_dim = rhs.space_dimension();
  if (space_dim < rhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_preimage(e1, r, e2)",
                                 "e2", rhs);
  }

  // Strict relation symbols are not admitted for octagons.
  if (relsym == LESS_THAN || relsym == GREATER_THAN) {
    throw_invalid_argument("generalized_affine_preimage(e1, r, e2)",
                           "r is a strict relation symbol");
  }
  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL) {
    throw_invalid_argument("generalized_affine_preimage(e1, r, e2)",
                           "r is the disequality relation symbol");
  }
  strong_closure_assign();
  // The image of an empty octagon is empty.
  if (marked_empty()) {
    return;
  }
  // Number of non-zero coefficients in `lhs': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t_lhs = 0;
  // Index of the last non-zero coefficient in `lhs', if any.
  dimension_type j_lhs = lhs.last_nonzero();

  if (j_lhs != 0) {
    ++t_lhs;
    if (!lhs.all_zeroes(1, j_lhs)) {
      ++t_lhs;
    }
    --j_lhs;
  }

  const Coefficient& b_lhs = lhs.inhomogeneous_term();

  // If all variables have a zero coefficient, then `lhs' is a constant:
  // in this case, preimage and image happen to be the same.
  if (t_lhs == 0) {
    generalized_affine_image(lhs, relsym, rhs);
    return;
  }

  else if (t_lhs == 1) {
    // Here `lhs == a_lhs * v + b_lhs'.
    // Independently from the form of `rhs', we can exploit the
    // method computing generalized affine preimages for a single variable.
    Variable v(j_lhs);
    // Compute a sign-corrected relation symbol.
    const Coefficient& denom = lhs.coefficient(v);
    Relation_Symbol new_relsym = relsym;
    if (denom < 0) {
      if (relsym == LESS_OR_EQUAL) {
        new_relsym = GREATER_OR_EQUAL;
      }
      else if (relsym == GREATER_OR_EQUAL) {
        new_relsym = LESS_OR_EQUAL;
      }
    }
    Linear_Expression expr = rhs - b_lhs;
    generalized_affine_preimage(v, new_relsym, expr, denom);
  }

  else {
    // Here `lhs' is of the general form, having at least two variables.
    // Compute the set of variables occurring in `lhs'.
    std::vector<Variable> lhs_vars;
    for (Linear_Expression::const_iterator i = lhs.begin(), i_end = lhs.end();
          i != i_end; ++i) {
      lhs_vars.push_back(i.variable());
    }

    const dimension_type num_common_dims = std::min(lhs_space_dim, rhs_space_dim);
    if (!lhs.have_a_common_variable(rhs, Variable(0), Variable(num_common_dims))) {
      // `lhs' and `rhs' variables are disjoint.
      // Constrain the left hand side expression so that it is related to
      // the right hand side expression as dictated by `relsym'.
      // TODO: if the following constraint is NOT an octagonal difference,
      // it will be simply ignored. Should we compute approximations for it?
      switch (relsym) {
      case LESS_OR_EQUAL:
        refine_no_check(lhs <= rhs);
        break;
      case EQUAL:
        refine_no_check(lhs == rhs);
        break;
      case GREATER_OR_EQUAL:
        refine_no_check(lhs >= rhs);
        break;
      default:
        // We already dealt with the other cases.
        PPL_UNREACHABLE;
        break;
      }

      // Any image of an empty octagon is empty.
      if (is_empty()) {
        return;
      }
      // Existentially quantify all variables in the lhs.
      for (dimension_type i = lhs_vars.size(); i-- > 0; ) {
        dimension_type lhs_vars_i = lhs_vars[i].id();
        forget_all_octagonal_constraints(lhs_vars_i);
      }
    }
    else {
      // Some variables in `lhs' also occur in `rhs'.

      // More accurate computation that is worth doing only if
      // the following TODO note is accurately dealt with.

      // To ease the computation, we add an additional dimension.
      const Variable new_var(space_dim);
      add_space_dimensions_and_embed(1);
      // Constrain the new dimension to be equal to `rhs'.
      // NOTE: calling affine_image() instead of refine_no_check()
      // ensures some approximation is tried even when the constraint
      // is not an octagonal difference.
      affine_image(new_var, lhs);
      // Existentially quantify all variables in the lhs.
      // NOTE: enforce strong closure for precision.
      strong_closure_assign();
      PPL_ASSERT(!marked_empty());
      for (dimension_type i = lhs_vars.size(); i-- > 0; ) {
        dimension_type lhs_vars_i = lhs_vars[i].id();
        forget_all_octagonal_constraints(lhs_vars_i);
      }
      // Constrain the new dimension so that it is related to
      // the left hand side as dictated by `relsym'.
      // Note: if `rhs == v + b_rhs' or `rhs == -v + b_rhs' or `rhs == b_rhs',
      // one of the following constraints will be added, because they
      // are octagonal differences.
      // Else the following constraints are NOT octagonal differences,
      // so the method refine_no_check() will ignore them.
      switch (relsym) {
      case LESS_OR_EQUAL:
        refine_no_check(new_var <= rhs);
        break;
      case EQUAL:
        refine_no_check(new_var == rhs);
        break;
      case GREATER_OR_EQUAL:
        refine_no_check(new_var >= rhs);
        break;
      default:
        // We already dealt with the other cases.
        PPL_UNREACHABLE;
        break;
      }
      // Remove the temporarily added dimension.
      remove_higher_space_dimensions(space_dim-1);
    }
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::bounded_affine_preimage(const Variable var,
                                            const Linear_Expression& lb_expr,
                                            const Linear_Expression& ub_expr,
                                            Coefficient_traits::const_reference
                                            denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("bounded_affine_preimage(v, lb, ub, d)", "d == 0");
  }

  // `var' should be one of the dimensions of the octagon.
  const dimension_type var_id = var.id();
  if (space_dim < var_id + 1) {
    throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)",
                                 var_id + 1);
  }

  // The dimension of `lb_expr' and `ub_expr' should not be
  // greater than the dimension of `*this'.
  const dimension_type lb_space_dim = lb_expr.space_dimension();
  if (space_dim < lb_space_dim) {
    throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)",
                                 "lb", lb_expr);
  }
  const dimension_type ub_space_dim = ub_expr.space_dimension();
  if (space_dim < ub_space_dim) {
    throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)",
                                 "ub", ub_expr);
  }

  strong_closure_assign();
  // The image of an empty octagon is empty too.
  if (marked_empty()) {
    return;
  }

  if (ub_expr.coefficient(var) == 0) {
    refine(var, LESS_OR_EQUAL, ub_expr, denominator);
    generalized_affine_preimage(var, GREATER_OR_EQUAL,
                                lb_expr, denominator);
    return;
  }
  if (lb_expr.coefficient(var) == 0) {
    refine(var, GREATER_OR_EQUAL, lb_expr, denominator);
    generalized_affine_preimage(var, LESS_OR_EQUAL,
                                ub_expr, denominator);
    return;
  }

  const Coefficient& expr_v = lb_expr.coefficient(var);
  // Here `var' occurs in `lb_expr' and `ub_expr'.
  // To ease the computation, we add an additional dimension.
  const Variable new_var(space_dim);
  add_space_dimensions_and_embed(1);
  const Linear_Expression lb_inverse
    = lb_expr - (expr_v + denominator)*var;
  PPL_DIRTY_TEMP_COEFFICIENT(inverse_denom);
  neg_assign(inverse_denom, expr_v);
  affine_image(new_var, lb_inverse, inverse_denom);
  strong_closure_assign();
  PPL_ASSERT(!marked_empty());
  generalized_affine_preimage(var, LESS_OR_EQUAL,
                              ub_expr, denominator);
  if (sgn(denominator) == sgn(inverse_denom)) {
    refine_no_check(var >= new_var) ;
  }
  else {
    refine_no_check(var <= new_var);
  }
  // Remove the temporarily added dimension.
  remove_higher_space_dimensions(space_dim-1);
}

template <typename T>
Constraint_System
Octagonal_Shape<T>::constraints() const {
  Constraint_System cs;
  cs.set_space_dimension(space_dim);

  if (space_dim == 0) {
    if (marked_empty()) {
      cs = Constraint_System::zero_dim_empty();
    }
    return cs;
  }

  if (marked_empty()) {
    cs.insert(Constraint::zero_dim_false());
    return cs;
  }

  typedef typename OR_Matrix<N>::const_row_iterator row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type row_reference;

  row_iterator m_begin = matrix.row_begin();
  row_iterator m_end = matrix.row_end();

  PPL_DIRTY_TEMP_COEFFICIENT(a);
  PPL_DIRTY_TEMP_COEFFICIENT(b);

  // Go through all the unary constraints in `matrix'.
  for (row_iterator i_iter = m_begin; i_iter != m_end; ) {
    const dimension_type i = i_iter.index();
    const Variable x(i/2);
    const N& c_i_ii = (*i_iter)[i + 1];
    ++i_iter;
    const N& c_ii_i = (*i_iter)[i];
    ++i_iter;
    // Go through unary constraints.
    if (is_additive_inverse(c_i_ii, c_ii_i)) {
      // We have a unary equality constraint.
      numer_denom(c_ii_i, b, a);
      a *= 2;
      cs.insert(a*x == b);
    }
    else {
      // We have 0, 1 or 2 inequality constraints.
      if (!is_plus_infinity(c_i_ii)) {
        numer_denom(c_i_ii, b, a);
        a *= 2;
        cs.insert(-a*x <= b);
      }
      if (!is_plus_infinity(c_ii_i)) {
        numer_denom(c_ii_i, b, a);
        a *= 2;
        cs.insert(a*x <= b);
      }
    }
  }
  //  Go through all the binary constraints in `matrix'.
  for (row_iterator i_iter = m_begin; i_iter != m_end; ) {
    const dimension_type i = i_iter.index();
    row_reference r_i = *i_iter;
    ++i_iter;
    row_reference r_ii = *i_iter;
    ++i_iter;
    const Variable y(i/2);
    for (dimension_type j = 0; j < i; j += 2) {
      const N& c_i_j = r_i[j];
      const N& c_ii_jj = r_ii[j + 1];
      const Variable x(j/2);
      if (is_additive_inverse(c_ii_jj, c_i_j)) {
        // We have an equality constraint of the form a*x - a*y = b.
        numer_denom(c_i_j, b, a);
        cs.insert(a*x - a*y == b);
      }
      else {
        // We have 0, 1 or 2 inequality constraints.
        if (!is_plus_infinity(c_i_j)) {
          numer_denom(c_i_j, b, a);
          cs.insert(a*x - a*y <= b);
        }
        if (!is_plus_infinity(c_ii_jj)) {
          numer_denom(c_ii_jj, b, a);
          cs.insert(a*y - a*x <= b);
        }
      }

      const N& c_ii_j = r_ii[j];
      const N& c_i_jj = r_i[j + 1];
      if (is_additive_inverse(c_i_jj, c_ii_j)) {
        // We have an equality constraint of the form a*x + a*y = b.
        numer_denom(c_ii_j, b, a);
        cs.insert(a*x + a*y == b);
      }
      else {
        // We have 0, 1 or 2 inequality constraints.
        if (!is_plus_infinity(c_i_jj)) {
          numer_denom(c_i_jj, b, a);
          cs.insert(-a*x - a*y <= b);
        }
        if (!is_plus_infinity(c_ii_j)) {
          numer_denom(c_ii_j, b, a);
          cs.insert(a*x + a*y <= b);
        }
      }
    }
  }
  return cs;
}

template <typename T>
void
Octagonal_Shape<T>::expand_space_dimension(Variable var, dimension_type m) {
  // `var' should be one of the dimensions of the vector space.
  const dimension_type var_id = var.id();
  if (var_id + 1 > space_dim) {
    throw_dimension_incompatible("expand_space_dimension(v, m)", var_id + 1);
  }

  // The space dimension of the resulting octagon should not
  // overflow the maximum allowed space dimension.
  if (m > max_space_dimension() - space_dim) {
    throw_invalid_argument("expand_dimension(v, m)",
                           "adding m new space dimensions exceeds "
                           "the maximum allowed space dimension");
  }
  // Nothing to do, if no dimensions must be added.
  if (m == 0) {
    return;
  }
  // Keep track of the dimension before adding the new ones.
  const dimension_type old_num_rows = matrix.num_rows();

  // Add the required new dimensions.
  add_space_dimensions_and_embed(m);

  // For each constraints involving variable `var', we add a
  // similar constraint with the new variable substituted for
  // variable `var'.
  typedef typename OR_Matrix<N>::row_iterator row_iterator;
  typedef typename OR_Matrix<N>::row_reference_type row_reference;
  typedef typename OR_Matrix<N>::const_row_iterator Row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type Row_reference;

  const row_iterator m_begin = matrix.row_begin();
  const row_iterator m_end = matrix.row_end();
  const dimension_type n_var = 2*var_id;
  Row_iterator v_iter = m_begin + n_var;
  Row_reference m_v = *v_iter;
  Row_reference m_cv = *(v_iter + 1);

  for (row_iterator i_iter = m_begin + old_num_rows; i_iter != m_end;
       i_iter += 2) {
    row_reference m_i = *i_iter;
    row_reference m_ci = *(i_iter + 1);
    const dimension_type i = i_iter.index();
    const dimension_type ci = i + 1;
    m_i[ci] = m_v[n_var + 1];
    m_ci[i] = m_cv[n_var];
    for (dimension_type j = 0; j < n_var; ++j) {
      m_i[j] = m_v[j];
      m_ci[j] = m_cv[j];
    }
    for (dimension_type j = n_var + 2; j < old_num_rows; ++j) {
      row_iterator j_iter = m_begin + j;
      row_reference m_cj = (j % 2 != 0) ? *(j_iter-1) : *(j_iter + 1);
      m_i[j] = m_cj[n_var + 1];
      m_ci[j] = m_cj[n_var];
    }
  }
  // In general, adding a constraint does not preserve the strong closure
  // of the octagon.
  if (marked_strongly_closed()) {
    reset_strongly_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>::fold_space_dimensions(const Variables_Set& vars,
                                          Variable dest) {
  // `dest' should be one of the dimensions of the octagon.
  if (dest.space_dimension() > space_dim) {
    throw_dimension_incompatible("fold_space_dimensions(vs, v)", "v", dest);
  }
  // The folding of no dimensions is a no-op.
  if (vars.empty()) {
    return;
  }
  // All variables in `vars' should be dimensions of the octagon.
  if (vars.space_dimension() > space_dim) {
    throw_dimension_incompatible("fold_space_dimensions(vs, v)",
                                 vars.space_dimension());
  }

  // Moreover, `dest.id()' should not occur in `vars'.
  if (vars.find(dest.id()) != vars.end()) {
    throw_invalid_argument("fold_space_dimensions(vs, v)",
                           "v should not occur in vs");
  }

  // Recompute the elements of the row and the column corresponding
  // to variable `dest' by taking the join of their value with the
  // value of the corresponding elements in the row and column of the
  // variable `vars'.
  typedef typename OR_Matrix<N>::row_iterator row_iterator;
  typedef typename OR_Matrix<N>::row_reference_type row_reference;

  const row_iterator m_begin = matrix.row_begin();

  strong_closure_assign();
  const dimension_type n_rows = matrix.num_rows();
  const dimension_type n_dest = 2*dest.id();
  row_iterator v_iter = m_begin + n_dest;
  row_reference m_v = *v_iter;
  row_reference m_cv = *(v_iter + 1);
  for (Variables_Set::const_iterator i = vars.begin(),
         vs_end = vars.end(); i != vs_end; ++i) {
    const dimension_type tbf_id = *i;
    const dimension_type tbf_var = 2*tbf_id;
    row_iterator tbf_iter = m_begin + tbf_var;
    row_reference m_tbf = *tbf_iter;
    row_reference m_ctbf = *(tbf_iter + 1);
    max_assign(m_v[n_dest + 1], m_tbf[tbf_var + 1]);
    max_assign(m_cv[n_dest], m_ctbf[tbf_var]);

    const dimension_type min_id = std::min(n_dest, tbf_var);
    const dimension_type max_id = std::max(n_dest, tbf_var);

    using namespace Implementation::Octagonal_Shapes;
    for (dimension_type j = 0; j < min_id; ++j) {
      const dimension_type cj = coherent_index(j);
      max_assign(m_v[j], m_tbf[j]);
      max_assign(m_cv[j], m_ctbf[j]);
      max_assign(m_cv[cj], m_ctbf[cj]);
      max_assign(m_v[cj], m_tbf[cj]);
    }
    for (dimension_type j = min_id + 2; j < max_id; ++j) {
      const dimension_type cj = coherent_index(j);
      row_iterator j_iter = m_begin + j;
      row_reference m_j = *j_iter;
      row_reference m_cj = (j % 2 != 0) ? *(j_iter-1) : *(j_iter + 1);
      if (n_dest == min_id) {
        max_assign(m_cj[n_dest + 1], m_tbf[j]);
        max_assign(m_cj[n_dest], m_ctbf[j]);
        max_assign(m_j[n_dest], m_ctbf[cj]);
        max_assign(m_j[n_dest + 1], m_tbf[cj]);
      }
      else {
        max_assign(m_v[j], m_cj[tbf_var + 1]);
        max_assign(m_cv[j], m_cj[tbf_var]);
        max_assign(m_cv[cj], m_j[tbf_var]);
        max_assign(m_v[cj], m_j[tbf_var + 1]);
      }
    }
    for (dimension_type j = max_id + 2; j < n_rows; ++j) {
      row_iterator j_iter = m_begin + j;
      row_reference m_j = *j_iter;
      row_reference m_cj = (j % 2 != 0) ? *(j_iter-1) : *(j_iter + 1);
      max_assign(m_cj[n_dest + 1], m_cj[tbf_var + 1]);
      max_assign(m_cj[n_dest], m_cj[tbf_var]);
      max_assign(m_j[n_dest], m_j[tbf_var]);
      max_assign(m_j[n_dest + 1], m_j[tbf_var + 1]);
    }
  }
  remove_space_dimensions(vars);
}

template <typename T>
bool
Octagonal_Shape<T>::upper_bound_assign_if_exact(const Octagonal_Shape& y) {
  // FIXME, CHECKME: what about inexact computations?

  // Declare a const reference to *this (to avoid accidental modifications).
  const Octagonal_Shape& x = *this;
  const dimension_type x_space_dim = x.space_dimension();

  if (x_space_dim != y.space_dimension()) {
    throw_dimension_incompatible("upper_bound_assign_if_exact(y)", y);
  }

  // The zero-dim case is trivial.
  if (x_space_dim == 0) {
    upper_bound_assign(y);
    return true;
  }
  // If `x' or `y' is (known to be) empty, the upper bound is exact.
  if (x.marked_empty()) {
    *this = y;
    return true;
  }
  else if (y.is_empty()) {
    return true;
  }
  else if (x.is_empty()) {
    *this = y;
    return true;
  }

  // Here both `x' and `y' are known to be non-empty.
  PPL_ASSERT(x.marked_strongly_closed());
  PPL_ASSERT(y.marked_strongly_closed());
  // Pre-compute the upper bound of `x' and `y'.
  Octagonal_Shape<T> ub(x);
  ub.upper_bound_assign(y);

  // Compute redundancy information for x and y.
  // TODO: provide a nicer data structure for redundancy.
  std::vector<Bit_Row> x_non_red;
  x.non_redundant_matrix_entries(x_non_red);
  std::vector<Bit_Row> y_non_red;
  y.non_redundant_matrix_entries(y_non_red);

  PPL_DIRTY_TEMP(N, lhs);
  PPL_DIRTY_TEMP(N, lhs_copy);
  PPL_DIRTY_TEMP(N, rhs);
  PPL_DIRTY_TEMP(N, temp_zero);
  assign_r(temp_zero, 0, ROUND_NOT_NEEDED);

  typedef typename OR_Matrix<N>::const_row_iterator row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type row_reference;
  const dimension_type n_rows = x.matrix.num_rows();
  const row_iterator x_m_begin = x.matrix.row_begin();
  const row_iterator y_m_begin = y.matrix.row_begin();
  const row_iterator ub_m_begin = ub.matrix.row_begin();

  for (dimension_type i = n_rows; i-- > 0; ) {
    const Bit_Row& x_non_red_i = x_non_red[i];
    using namespace Implementation::Octagonal_Shapes;
    const dimension_type ci = coherent_index(i);
    const dimension_type row_size_i = OR_Matrix<N>::row_size(i);
    row_reference x_i = *(x_m_begin + i);
    row_reference y_i = *(y_m_begin + i);
    row_reference ub_i = *(ub_m_begin + i);
    const N& ub_i_ci = ub_i[ci];
    for (dimension_type j = row_size_i; j-- > 0; ) {
      // Check redundancy of x_i_j.
      if (!x_non_red_i[j]) {
        continue;
      }
      const N& x_i_j = x_i[j];
      // Check 1st condition in BHZ09 theorem.
      if (x_i_j >= y_i[j]) {
        continue;
      }

      const dimension_type cj = coherent_index(j);
      const dimension_type row_size_cj = OR_Matrix<N>::row_size(cj);
      row_reference ub_cj = *(ub_m_begin + cj);
      const N& ub_cj_j = ub_cj[j];
      for (dimension_type k = 0; k < n_rows; ++k) {
        const Bit_Row& y_non_red_k = y_non_red[k];
        const dimension_type ck = coherent_index(k);
        const dimension_type row_size_k = OR_Matrix<N>::row_size(k);
        row_reference x_k = *(x_m_begin + k);
        row_reference y_k = *(y_m_begin + k);
        row_reference ub_k = *(ub_m_begin + k);
        const N& ub_k_ck = ub_k[ck];
        // Be careful: for each index h, the diagonal element m[h][h]
        // is (by convention) +infty in our implementation; however,
        // BHZ09 theorem assumes that it is equal to 0.
        const N& ub_k_j
          = (k == j)
          ? temp_zero
          : ((j < row_size_k) ? ub_k[j] : ub_cj[ck]);
        const N& ub_i_ck
          = (i == ck)
          ? temp_zero
          : ((ck < row_size_i) ? ub_i[ck] : ub_k[ci]);

        for (dimension_type ell = row_size_k; ell-- > 0; ) {
          // Check redundancy of y_k_ell.
          if (!y_non_red_k[ell]) {
            continue;
          }
          const N& y_k_ell = y_k[ell];
          // Check 2nd condition in BHZ09 theorem.
          if (y_k_ell >= x_k[ell]) {
            continue;
          }
          const dimension_type cell = coherent_index(ell);
          row_reference ub_cell = *(ub_m_begin + cell);
          const N& ub_i_ell
            = (i == ell)
            ? temp_zero
            : ((ell < row_size_i) ? ub_i[ell] : ub_cell[ci]);
          const N& ub_cj_ell
            = (cj == ell)
            ? temp_zero
            : ((ell < row_size_cj) ? ub_cj[ell] : ub_cell[j]);
          // Check 3rd condition in BHZ09 theorem.
          add_assign_r(lhs, x_i_j, y_k_ell, ROUND_UP);
          add_assign_r(rhs, ub_i_ell, ub_k_j, ROUND_UP);
          if (lhs >= rhs) {
            continue;
          }
          // Check 4th condition in BHZ09 theorem.
          add_assign_r(rhs, ub_i_ck, ub_cj_ell, ROUND_UP);
          if (lhs >= rhs) {
            continue;
          }
          // Check 5th condition in BHZ09 theorem.
          assign_r(lhs_copy, lhs, ROUND_NOT_NEEDED);
          add_assign_r(lhs, lhs_copy, x_i_j, ROUND_UP);
          add_assign_r(rhs, ub_i_ell, ub_i_ck, ROUND_UP);
          add_assign_r(rhs, rhs, ub_cj_j, ROUND_UP);
          if (lhs >= rhs) {
            continue;
          }
          // Check 6th condition in BHZ09 theorem.
          add_assign_r(rhs, ub_k_j, ub_cj_ell, ROUND_UP);
          add_assign_r(rhs, rhs, ub_i_ci, ROUND_UP);
          if (lhs >= rhs) {
            continue;
          }
          // Check 7th condition of BHZ09 theorem.
          add_assign_r(lhs, lhs_copy, y_k_ell, ROUND_UP);
          add_assign_r(rhs, ub_i_ell, ub_cj_ell, ROUND_UP);
          add_assign_r(rhs, rhs, ub_k_ck, ROUND_UP);
          if (lhs >= rhs) {
            continue;
          }
          // Check 8th (last) condition in BHZ09 theorem.
          add_assign_r(rhs, ub_k_j, ub_i_ck, ROUND_UP);
          add_assign_r(rhs, rhs, ub_cell[ell], ROUND_UP);
          if (lhs < rhs) {
            // All 8 conditions are satisfied:
            // upper bound is not exact.
            return false;
          }
        }
      }
    }
  }

  // The upper bound of x and y is indeed exact.
  m_swap(ub);
  PPL_ASSERT(OK());
  return true;
}

template <typename T>
bool
Octagonal_Shape<T>
::integer_upper_bound_assign_if_exact(const Octagonal_Shape& y) {
  PPL_COMPILE_TIME_CHECK(std::numeric_limits<T>::is_integer,
                         "Octagonal_Shape<T>::"
                         "integer_upper_bound_assign_if_exact(y):"
                         " T in not an integer datatype.");
  // Declare a const reference to *this (to avoid accidental modifications).
  const Octagonal_Shape& x = *this;
  const dimension_type x_space_dim = x.space_dimension();

  if (x_space_dim != y.space_dimension()) {
    throw_dimension_incompatible("integer_upper_bound_assign_if_exact(y)", y);
  }
  // The zero-dim case is trivial.
  if (x_space_dim == 0) {
    upper_bound_assign(y);
    return true;
  }

  // If `x' or `y' is (known to) contain no integral point,
  // then the integer upper bound can be computed exactly by tight closure.
  if (x.marked_empty()) {
    *this = y;
    tight_closure_assign();
    return true;
  }
  else if (y.marked_empty()) {
    tight_closure_assign();
    return true;
  }
  else if (x.is_empty() || x.tight_coherence_would_make_empty()) {
    *this = y;
    tight_closure_assign();
    return true;
  }
  else if (y.is_empty() || y.tight_coherence_would_make_empty()) {
    tight_closure_assign();
    return true;
  }

  // Here both `x' and `y' are known to be non-empty (and Z-consistent).
  PPL_ASSERT(x.marked_strongly_closed());
  PPL_ASSERT(y.marked_strongly_closed());
  // Pre-compute the integer upper bound of `x' and `y':
  // have to take copies, since tight closure might modify the rational shape.
  Octagonal_Shape<T> tx(x);
  tx.tight_closure_assign();
  Octagonal_Shape<T> ty(y);
  ty.tight_closure_assign();
  Octagonal_Shape<T> ub(tx);
  ub.upper_bound_assign(ty);

  // Compute redundancy information for tx and ty.
  // TODO: provide a nicer data structure for redundancy.
  // NOTE: there is no need to identify all redundancies, since this is
  // an optimization; hence we reuse the strong-reduction helper methods.
  std::vector<Bit_Row> tx_non_red;
  tx.non_redundant_matrix_entries(tx_non_red);
  std::vector<Bit_Row> ty_non_red;
  ty.non_redundant_matrix_entries(ty_non_red);

  PPL_DIRTY_TEMP(N, lhs_i_j);
  PPL_DIRTY_TEMP(N, lhs_k_ell);
  PPL_DIRTY_TEMP(N, lhs);
  PPL_DIRTY_TEMP(N, lhs_copy);
  PPL_DIRTY_TEMP(N, rhs);
  PPL_DIRTY_TEMP(N, temp_zero);
  assign_r(temp_zero, 0, ROUND_NOT_NEEDED);
  PPL_DIRTY_TEMP(N, temp_one);
  assign_r(temp_one, 1, ROUND_NOT_NEEDED);
  PPL_DIRTY_TEMP(N, temp_two);
  assign_r(temp_two, 2, ROUND_NOT_NEEDED);

  typedef typename OR_Matrix<N>::const_row_iterator row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type row_reference;
  const dimension_type n_rows = tx.matrix.num_rows();
  const row_iterator tx_m_begin = tx.matrix.row_begin();
  const row_iterator ty_m_begin = ty.matrix.row_begin();
  const row_iterator ub_m_begin = ub.matrix.row_begin();

  for (dimension_type i = n_rows; i-- > 0; ) {
    const Bit_Row& tx_non_red_i = tx_non_red[i];
    using namespace Implementation::Octagonal_Shapes;
    const dimension_type ci = coherent_index(i);
    const dimension_type row_size_i = OR_Matrix<N>::row_size(i);
    row_reference tx_i = *(tx_m_begin + i);
    row_reference ty_i = *(ty_m_begin + i);
    row_reference ub_i = *(ub_m_begin + i);
    const N& ub_i_ci = ub_i[ci];
    for (dimension_type j = row_size_i; j-- > 0; ) {
      // Check redundancy of tx_i_j.
      if (!tx_non_red_i[j]) {
        continue;
      }
      const N& tx_i_j = tx_i[j];
      const dimension_type cj = coherent_index(j);
      const N& eps_i_j = (i == cj) ? temp_two : temp_one;
      // Check condition 1a in BHZ09 Theorem 6.8.
      add_assign_r(lhs_i_j, tx_i_j, eps_i_j, ROUND_NOT_NEEDED);
      if (lhs_i_j > ty_i[j]) {
        continue;
      }
      const dimension_type row_size_cj = OR_Matrix<N>::row_size(cj);
      row_reference ub_cj = *(ub_m_begin + cj);
      const N& ub_cj_j = ub_cj[j];
      for (dimension_type k = 0; k < n_rows; ++k) {
        const Bit_Row& ty_non_red_k = ty_non_red[k];
        const dimension_type ck = coherent_index(k);
        const dimension_type row_size_k = OR_Matrix<N>::row_size(k);
        row_reference tx_k = *(tx_m_begin + k);
        row_reference ty_k = *(ty_m_begin + k);
        row_reference ub_k = *(ub_m_begin + k);
        const N& ub_k_ck = ub_k[ck];
        // Be careful: for each index h, the diagonal element m[h][h]
        // is (by convention) +infty in our implementation; however,
        // BHZ09 theorem assumes that it is equal to 0.
        const N& ub_k_j
          = (k == j)
          ? temp_zero
          : ((j < row_size_k) ? ub_k[j] : ub_cj[ck]);
        const N& ub_i_ck
          = (i == ck)
          ? temp_zero
          : ((ck < row_size_i) ? ub_i[ck] : ub_k[ci]);

        for (dimension_type ell = row_size_k; ell-- > 0; ) {
          // Check redundancy of y_k_ell.
          if (!ty_non_red_k[ell]) {
            continue;
          }
          const N& ty_k_ell = ty_k[ell];
          const dimension_type cell = coherent_index(ell);
          const N& eps_k_ell = (k == cell) ? temp_two : temp_one;
          // Check condition 1b in BHZ09 Theorem 6.8.
          add_assign_r(lhs_k_ell, ty_k_ell, eps_k_ell, ROUND_NOT_NEEDED);
          if (lhs_k_ell > tx_k[ell]) {
            continue;
          }
          row_reference ub_cell = *(ub_m_begin + cell);
          const N& ub_i_ell
            = (i == ell)
            ? temp_zero
            : ((ell < row_size_i) ? ub_i[ell] : ub_cell[ci]);
          const N& ub_cj_ell
            = (cj == ell)
            ? temp_zero
            : ((ell < row_size_cj) ? ub_cj[ell] : ub_cell[j]);
          // Check condition 2a in BHZ09 Theorem 6.8.
          add_assign_r(lhs, lhs_i_j, lhs_k_ell, ROUND_NOT_NEEDED);
          add_assign_r(rhs, ub_i_ell, ub_k_j, ROUND_NOT_NEEDED);
          if (lhs > rhs) {
            continue;
          }
          // Check condition 2b in BHZ09 Theorem 6.8.
          add_assign_r(rhs, ub_i_ck, ub_cj_ell, ROUND_NOT_NEEDED);
          if (lhs > rhs) {
            continue;
          }
          // Check condition 3a in BHZ09 Theorem 6.8.
          assign_r(lhs_copy, lhs, ROUND_NOT_NEEDED);
          add_assign_r(lhs, lhs, lhs_i_j, ROUND_NOT_NEEDED);
          add_assign_r(rhs, ub_i_ell, ub_i_ck, ROUND_NOT_NEEDED);
          add_assign_r(rhs, rhs, ub_cj_j, ROUND_NOT_NEEDED);
          if (lhs > rhs) {
            continue;
          }
          // Check condition 3b in BHZ09 Theorem 6.8.
          add_assign_r(rhs, ub_k_j, ub_cj_ell, ROUND_NOT_NEEDED);
          add_assign_r(rhs, rhs, ub_i_ci, ROUND_NOT_NEEDED);
          if (lhs > rhs) {
            continue;
          }
          // Check condition 4a in BHZ09 Theorem 6.8.
          add_assign_r(lhs, lhs_copy, lhs_k_ell, ROUND_NOT_NEEDED);
          add_assign_r(rhs, ub_i_ell, ub_cj_ell, ROUND_NOT_NEEDED);
          add_assign_r(rhs, rhs, ub_k_ck, ROUND_NOT_NEEDED);
          if (lhs > rhs) {
            continue;
          }
          // Check condition 4b in BHZ09 Theorem 6.8.
          add_assign_r(rhs, ub_k_j, ub_i_ck, ROUND_NOT_NEEDED);
          add_assign_r(rhs, rhs, ub_cell[ell], ROUND_NOT_NEEDED);
          if (lhs <= rhs) {
            // All 8 conditions are satisfied:
            // integer upper bound is not exact.
            return false;
          }
        }
      }
    }
  }

  // The upper bound of x and y is indeed exact.
  m_swap(ub);
  PPL_ASSERT(OK());
  return true;
}

template <typename T>
void
Octagonal_Shape<T>::drop_some_non_integer_points(Complexity_Class) {
  if (std::numeric_limits<T>::is_integer) {
    return;
  }

  strong_closure_assign();
  if (space_dim == 0 || marked_empty()) {
    return;
  }

  for (typename OR_Matrix<N>::element_iterator i = matrix.element_begin(),
         i_end = matrix.element_end(); i != i_end; ++i) {
    drop_some_non_integer_points_helper(*i);
  }

  // Unary constraints should have an even integer boundary.
  PPL_DIRTY_TEMP(N, temp_one);
  assign_r(temp_one, 1, ROUND_NOT_NEEDED);
  for (dimension_type i = 0; i < 2*space_dim; i += 2) {
    const dimension_type ci = i + 1;
    N& mat_i_ci = matrix[i][ci];
    if (!is_plus_infinity(mat_i_ci) && !is_even(mat_i_ci)) {
      sub_assign_r(mat_i_ci, mat_i_ci, temp_one, ROUND_UP);
      reset_strongly_closed();
    }
    N& mat_ci_i = matrix[ci][i];
    if (!is_plus_infinity(mat_ci_i) && !is_even(mat_ci_i)) {
      sub_assign_r(mat_ci_i, mat_ci_i, temp_one, ROUND_UP);
      reset_strongly_closed();
    }
  }

  PPL_ASSERT(OK());
}

template <typename T>
void
Octagonal_Shape<T>
::drop_some_non_integer_points(const Variables_Set& vars,
                               Complexity_Class) {
  // Dimension-compatibility check.
  const dimension_type min_space_dim = vars.space_dimension();
  if (space_dimension() < min_space_dim) {
    throw_dimension_incompatible("drop_some_non_integer_points(vs, cmpl)",
                                 min_space_dim);
  }

  if (std::numeric_limits<T>::is_integer || min_space_dim == 0) {
    return;
  }

  strong_closure_assign();
  if (marked_empty()) {
    return;
  }

  PPL_DIRTY_TEMP(N, temp_one);
  assign_r(temp_one, 1, ROUND_NOT_NEEDED);

  const Variables_Set::const_iterator v_begin = vars.begin();
  const Variables_Set::const_iterator v_end = vars.end();
  PPL_ASSERT(v_begin != v_end);
  typedef typename OR_Matrix<N>::row_reference_type row_reference;
  for (Variables_Set::const_iterator v_i = v_begin; v_i != v_end; ++v_i) {
    const dimension_type i = 2 * (*v_i);
    const dimension_type ci = i + 1;
    row_reference m_i = matrix[i];
    row_reference m_ci = matrix[ci];

    // Unary constraints: should be even integers.
    N& m_i_ci = m_i[ci];
    if (!is_plus_infinity(m_i_ci)) {
      drop_some_non_integer_points_helper(m_i_ci);
      if (!is_even(m_i_ci)) {
        sub_assign_r(m_i_ci, m_i_ci, temp_one, ROUND_UP);
        reset_strongly_closed();
      }
    }
    N& m_ci_i = m_ci[i];
    if (!is_plus_infinity(m_ci_i)) {
      drop_some_non_integer_points_helper(m_ci_i);
      if (!is_even(m_ci_i)) {
        sub_assign_r(m_ci_i, m_ci_i, temp_one, ROUND_UP);
        reset_strongly_closed();
      }
    }

    // Binary constraints (note: only consider j < i).
    for (Variables_Set::const_iterator v_j = v_begin; v_j != v_i; ++v_j) {
      const dimension_type j = 2 * (*v_j);
      const dimension_type cj = j + 1;
      drop_some_non_integer_points_helper(m_i[j]);
      drop_some_non_integer_points_helper(m_i[cj]);
      drop_some_non_integer_points_helper(m_ci[j]);
      drop_some_non_integer_points_helper(m_ci[cj]);
    }
  }
  PPL_ASSERT(OK());
}

template <typename T>
template <typename U>
void
Octagonal_Shape<T>
::export_interval_constraints(U& dest) const {
  if (space_dim > dest.space_dimension()) {
    throw std::invalid_argument(
               "Octagonal_Shape<T>::export_interval_constraints");
  }

  strong_closure_assign();

  if (marked_empty()) {
    dest.set_empty();
    return;
  }

  PPL_DIRTY_TEMP(N, lb);
  PPL_DIRTY_TEMP(N, ub);
  for (dimension_type i = space_dim; i-- > 0; ) {
    const dimension_type ii = 2*i;
    const dimension_type cii = ii + 1;

    // Set the upper bound.
    const N& twice_ub = matrix[cii][ii];
    if (!is_plus_infinity(twice_ub)) {
      assign_r(ub, twice_ub, ROUND_NOT_NEEDED);
      div_2exp_assign_r(ub, ub, 1, ROUND_UP);
      // FIXME: passing a raw value may not be general enough.
      if (!dest.restrict_upper(i, ub.raw_value())) {
        return;
      }
    }

    // Set the lower bound.
    const N& twice_lb = matrix[ii][cii];
    if (!is_plus_infinity(twice_lb)) {
      assign_r(lb, twice_lb, ROUND_NOT_NEEDED);
      neg_assign_r(lb, lb, ROUND_NOT_NEEDED);
      div_2exp_assign_r(lb, lb, 1, ROUND_DOWN);
      // FIXME: passing a raw value may not be general enough.
      if (!dest.restrict_lower(i, lb.raw_value())) {
        return;
      }
    }
  }

}

/*! \relates Parma_Polyhedra_Library::Octagonal_Shape */
template <typename T>
std::ostream&
IO_Operators::operator<<(std::ostream& s, const Octagonal_Shape<T>& oct) {
  // Handle special cases first.
  if (oct.marked_empty()) {
    s << "false";
    return s;
  }
  if (oct.is_universe()) {
    s << "true";
    return s;
  }

  typedef typename Octagonal_Shape<T>::coefficient_type N;
  typedef typename OR_Matrix<N>::const_row_iterator row_iterator;
  typedef typename OR_Matrix<N>::const_row_reference_type row_reference;

  // Records whether or not we still have to print the first constraint.
  bool first = true;

  row_iterator m_begin = oct.matrix.row_begin();
  row_iterator m_end = oct.matrix.row_end();

  // Temporaries.
  PPL_DIRTY_TEMP(N, negation);
  PPL_DIRTY_TEMP(N, half);
  // Go through all the unary constraints.
  // (Note: loop iterator is incremented in the loop body.)
  for (row_iterator i_iter = m_begin; i_iter != m_end; ) {
    const dimension_type i = i_iter.index();
    const Variable v_i(i/2);
    const N& c_i_ii = (*i_iter)[i + 1];
    ++i_iter;
    const N& c_ii_i = (*i_iter)[i];
    ++i_iter;
    // Check whether or not it is an equality constraint.
    if (is_additive_inverse(c_i_ii, c_ii_i)) {
      // It is an equality.
      PPL_ASSERT(!is_plus_infinity(c_i_ii) && !is_plus_infinity(c_ii_i));
      if (first) {
        first = false;
      }
      else {
        s << ", ";
      }
      // If the value bound can NOT be divided by 2 exactly,
      // then we output the constraint `2*v_i = bound'.
      if (div_2exp_assign_r(half, c_ii_i, 1,
                            ROUND_UP | ROUND_STRICT_RELATION)
          == V_EQ) {
        s << v_i << " = " << half;
      }
      else {
        s << "2*" << v_i << " = " << c_ii_i;
      }
    }
    else {
      // We will print unary non-strict inequalities, if any.
      if (!is_plus_infinity(c_i_ii)) {
        if (first) {
          first = false;
        }
        else {
          s << ", ";
        }
        neg_assign_r(negation, c_i_ii, ROUND_NOT_NEEDED);
        // If the value bound can NOT be divided by 2 exactly,
        // then we output the constraint `2*v_i >= negation'.
        if (div_2exp_assign_r(half, negation, 1,
                              ROUND_UP | ROUND_STRICT_RELATION)
            == V_EQ) {
          s << v_i << " >= " << half;
        }
        else {
          s << "2*" << v_i << " >= " << negation;
        }
      }
      if (!is_plus_infinity(c_ii_i)) {
        if (first) {
          first = false;
        }
        else {
          s << ", ";
        }
        // If the value bound can NOT be divided by 2 exactly,
        // then we output the constraint `2*v_i <= bound'.
        if (div_2exp_assign_r(half, c_ii_i, 1,
                              ROUND_UP | ROUND_STRICT_RELATION)
            == V_EQ) {
          s << v_i << " <= " << half;
        }
        else {
          s << "2*" << v_i << " <= " << c_ii_i;
        }
      }
    }
  }

  // Go through all the binary constraints.
  // (Note: loop iterator is incremented in the loop body.)
  for (row_iterator i_iter = m_begin; i_iter != m_end; ) {
    const dimension_type i = i_iter.index();
    const Variable v_i(i/2);
    row_reference r_i = *i_iter;
    ++i_iter;
    row_reference r_ii = *i_iter;
    ++i_iter;

    for (dimension_type j = 0; j < i; j += 2) {
      const Variable v_j(j/2);
      // Print binary differences.
      const N& c_ii_jj = r_ii[j + 1];
      const N& c_i_j = r_i[j];
      // Check whether or not it is an equality constraint.
      if (is_additive_inverse(c_ii_jj, c_i_j)) {
        // It is an equality.
        PPL_ASSERT(!is_plus_infinity(c_i_j) && !is_plus_infinity(c_ii_jj));
        if (first) {
          first = false;
        }
        else {
          s << ", ";
        }
        if (sgn(c_i_j) >= 0) {
          s << v_j << " - " << v_i << " = " << c_i_j;
        }
        else {
          s << v_i << " - " << v_j << " = " << c_ii_jj;
        }
      }
      else {
        // We will print non-strict inequalities, if any.
        if (!is_plus_infinity(c_i_j)) {
          if (first) {
            first = false;
          }
          else {
            s << ", ";
          }
          if (sgn(c_i_j) >= 0) {
            s << v_j << " - " << v_i << " <= " << c_i_j;
          }
          else {
            neg_assign_r(negation, c_i_j, ROUND_DOWN);
            s << v_i << " - " << v_j << " >= " << negation;
          }
        }
        if (!is_plus_infinity(c_ii_jj)) {
          if (first) {
            first = false;
          }
          else {
            s << ", ";
          }
          if (sgn(c_ii_jj) >= 0) {
            s << v_i << " - " << v_j << " <= " << c_ii_jj;
          }
          else {
            neg_assign_r(negation, c_ii_jj, ROUND_DOWN);
            s << v_j << " - " << v_i << " >= " << negation;
          }
        }
      }
      // Print binary sums.
      const N& c_i_jj = r_i[j + 1];
      const N& c_ii_j = r_ii[j];
      // Check whether or not it is an equality constraint.
      if (is_additive_inverse(c_i_jj, c_ii_j)) {
        // It is an equality.
        PPL_ASSERT(!is_plus_infinity(c_i_jj) && !is_plus_infinity(c_ii_j));
        if (first) {
          first = false;
        }
        else {
          s << ", ";
        }
        s << v_j << " + " << v_i << " = " << c_ii_j;
      }
      else {
        // We will print non-strict inequalities, if any.
        if (!is_plus_infinity(c_i_jj)) {
          if (first) {
            first = false;
          }
          else {
            s << ", ";
          }
          neg_assign_r(negation, c_i_jj, ROUND_DOWN);
          s << v_j << " + " << v_i << " >= " << negation;
        }
        if (!is_plus_infinity(c_ii_j)) {
          if (first) {
            first = false;
          }
          else {
            s << ", ";
          }
          s << v_j << " + " << v_i << " <= " << c_ii_j;
        }
      }
    }
  }
  return s;
}

template <typename T>
void
Octagonal_Shape<T>::ascii_dump(std::ostream& s) const {
  s << "space_dim "
    << space_dim
    << "\n";
  status.ascii_dump(s);
  s << "\n";
  matrix.ascii_dump(s);
}

PPL_OUTPUT_TEMPLATE_DEFINITIONS(T, Octagonal_Shape<T>)

template <typename T>
bool
Octagonal_Shape<T>::ascii_load(std::istream& s) {
  std::string str;

  if (!(s >> str) || str != "space_dim") {
    return false;
  }

  if (!(s >> space_dim)) {
    return false;
  }

  if (!status.ascii_load(s)) {
    return false;
  }

  if (!matrix.ascii_load(s)) {
    return false;
  }

  PPL_ASSERT(OK());
  return true;
}

template <typename T>
memory_size_type
Octagonal_Shape<T>::external_memory_in_bytes() const {
  return matrix.external_memory_in_bytes();
}

template <typename T>
bool
Octagonal_Shape<T>::OK() const {
  // Check whether the matrix is well-formed.
  if (!matrix.OK()) {
    return false;
  }

  // Check whether the status information is legal.
  if (!status.OK()) {
    return false;
  }

  // All empty octagons are OK.
  if (marked_empty()) {
    return true;
  }

  // 0-dim universe octagon is OK.
  if (space_dim == 0) {
    return true;
  }

  // MINUS_INFINITY cannot occur at all.
  for (typename OR_Matrix<N>::const_row_iterator i = matrix.row_begin(),
         matrix_row_end = matrix.row_end(); i != matrix_row_end; ++i) {
    typename OR_Matrix<N>::const_row_reference_type x_i = *i;
    for (dimension_type j = i.row_size(); j-- > 0; ) {
      if (is_minus_infinity(x_i[j])) {
#ifndef NDEBUG
        using namespace Parma_Polyhedra_Library::IO_Operators;
        std::cerr << "Octagonal_Shape::"
                  << "matrix[" << i.index() << "][" << j << "] = "
                  << x_i[j] << "!"
                  << std::endl;
#endif
        return false;
      }
    }
  }

  // On the main diagonal only PLUS_INFINITY can occur.
  for (typename OR_Matrix<N>::const_row_iterator i = matrix.row_begin(),
         m_end = matrix.row_end(); i != m_end; ++i) {
    typename OR_Matrix<N>::const_row_reference_type r = *i;
    const N& m_i_i = r[i.index()];
    if (!is_plus_infinity(m_i_i)) {
#ifndef NDEBUG
      const dimension_type j = i.index();
      using namespace Parma_Polyhedra_Library::IO_Operators;
      std::cerr << "Octagonal_Shape::matrix[" << j << "][" << j << "] = "
                << m_i_i << "!  (+inf was expected.)\n";
#endif
      return false;
    }
  }

  // The following tests might result in false alarms when using floating
  // point coefficients: they are only meaningful if the coefficient type
  // base is exact (since otherwise strong closure is approximated).
  if (std::numeric_limits<coefficient_type_base>::is_exact) {

    // Check whether the closure information is legal.
    if (marked_strongly_closed()) {
      Octagonal_Shape x = *this;
      x.reset_strongly_closed();
      x.strong_closure_assign();
      if (x.matrix != matrix) {
#ifndef NDEBUG
        std::cerr << "Octagonal_Shape is marked as strongly closed "
                  << "but it is not!\n";
#endif
        return false;
      }
    }

    // A closed octagon must be strong-coherent.
    if (marked_strongly_closed()) {
      if (!is_strong_coherent()) {
#ifndef NDEBUG
        std::cerr << "Octagonal_Shape is not strong-coherent!\n";
#endif
        return false;
      }
    }
  }

  // All checks passed.
  return true;
}


template <typename T>
void
Octagonal_Shape<T>
::throw_dimension_incompatible(const char* method,
                               const Octagonal_Shape& y) const {
  std::ostringstream s;
  s << "PPL::Octagonal_Shape::" << method << ":\n"
    << "this->space_dimension() == " << space_dimension()
    << ", y->space_dimension() == " << y.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
Octagonal_Shape<T>
::throw_dimension_incompatible(const char* method,
                               dimension_type required_dim) const {
  std::ostringstream s;
  s << "PPL::Octagonal_Shape::" << method << ":\n"
    << "this->space_dimension() == " << space_dimension()
    << ", required dimension == " << required_dim << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
Octagonal_Shape<T>::throw_dimension_incompatible(const char* method,
                                                 const Constraint& c) const {
  std::ostringstream s;
  s << "PPL::Octagonal_Shape::" << method << ":\n"
    << "this->space_dimension() == " << space_dimension()
    << ", c->space_dimension == " << c.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
Octagonal_Shape<T>::throw_dimension_incompatible(const char* method,
                                                 const Congruence& cg) const {
  std::ostringstream s;
  s << "PPL::Octagonal_Shape::" << method << ":\n"
    << "this->space_dimension() == " << space_dimension()
    << ", cg->space_dimension == " << cg.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
Octagonal_Shape<T>::throw_dimension_incompatible(const char* method,
                                                 const Generator& g) const {
  std::ostringstream s;
  s << "PPL::Octagonal_Shape::" << method << ":\n"
    << "this->space_dimension() == " << space_dimension()
    << ", g->space_dimension == " << g.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
Octagonal_Shape<T>::throw_constraint_incompatible(const char* method) {
  std::ostringstream s;
  s << "PPL::Octagonal_Shape::" << method << ":\n"
    << "the constraint is incompatible.";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
Octagonal_Shape<T>::throw_expression_too_complex(const char* method,
                                                 const Linear_Expression& le) {
  using namespace IO_Operators;
  std::ostringstream s;
  s << "PPL::Octagonal_Shape::" << method << ":\n"
    << le << " is too complex.";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
Octagonal_Shape<T>
::throw_dimension_incompatible(const char* method,
                               const char* le_name,
                               const Linear_Expression& le) const {
  std::ostringstream s;
  s << "PPL::Octagonal_Shape::" << method << ":\n"
    << "this->space_dimension() == " << space_dimension()
    << ", " << le_name << "->space_dimension() == "
    << le.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
template <typename C>
void
Octagonal_Shape<T>
::throw_dimension_incompatible(const char* method,
                               const char* lf_name,
                               const Linear_Form<C>& lf) const {
  std::ostringstream s;
  s << "PPL::Octagonal_Shape::" << method << ":\n"
    << "this->space_dimension() == " << space_dimension()
    << ", " << lf_name << "->space_dimension() == "
    << lf.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
Octagonal_Shape<T>::throw_invalid_argument(const char* method,
                                           const char* reason) {
  std::ostringstream s;
  s << "PPL::Octagonal_Shape::" << method << ":\n"
    << reason << ".";
  throw std::invalid_argument(s.str());
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Octagonal_Shape_defs.hh line 2329. */

/* Automatically generated from PPL source file ../src/BD_Shape_inlines.hh line 38. */
#include <vector>
#include <iostream>
#include <algorithm>

namespace Parma_Polyhedra_Library {

template <typename T>
inline dimension_type
BD_Shape<T>::max_space_dimension() {
  // One dimension is reserved to have a value of type dimension_type
  // that does not represent a legal dimension.
  return std::min(DB_Matrix<N>::max_num_rows() - 1,
                  DB_Matrix<N>::max_num_columns() - 1);
}

template <typename T>
inline bool
BD_Shape<T>::marked_zero_dim_univ() const {
  return status.test_zero_dim_univ();
}

template <typename T>
inline bool
BD_Shape<T>::marked_empty() const {
  return status.test_empty();
}

template <typename T>
inline bool
BD_Shape<T>::marked_shortest_path_closed() const {
  return status.test_shortest_path_closed();
}

template <typename T>
inline bool
BD_Shape<T>::marked_shortest_path_reduced() const {
  return status.test_shortest_path_reduced();
}

template <typename T>
inline void
BD_Shape<T>::set_zero_dim_univ() {
  status.set_zero_dim_univ();
}

template <typename T>
inline void
BD_Shape<T>::set_empty() {
  status.set_empty();
}

template <typename T>
inline void
BD_Shape<T>::set_shortest_path_closed() {
  status.set_shortest_path_closed();
}

template <typename T>
inline void
BD_Shape<T>::set_shortest_path_reduced() {
  status.set_shortest_path_reduced();
}

template <typename T>
inline void
BD_Shape<T>::reset_shortest_path_closed() {
  status.reset_shortest_path_closed();
}

template <typename T>
inline void
BD_Shape<T>::reset_shortest_path_reduced() {
  status.reset_shortest_path_reduced();
}

template <typename T>
inline
BD_Shape<T>::BD_Shape(const dimension_type num_dimensions,
                      const Degenerate_Element kind)
  : dbm(num_dimensions + 1), status(), redundancy_dbm() {
  if (kind == EMPTY) {
    set_empty();
  }
  else {
    if (num_dimensions > 0) {
      // A (non zero-dim) universe BDS is closed.
      set_shortest_path_closed();
    }
  }
  PPL_ASSERT(OK());
}

template <typename T>
inline
BD_Shape<T>::BD_Shape(const BD_Shape& y, Complexity_Class)
  : dbm(y.dbm), status(y.status), redundancy_dbm() {
  if (y.marked_shortest_path_reduced()) {
    redundancy_dbm = y.redundancy_dbm;
  }
}

template <typename T>
template <typename U>
inline
BD_Shape<T>::BD_Shape(const BD_Shape<U>& y, Complexity_Class)
  // For maximum precision, enforce shortest-path closure
  // before copying the DB matrix.
  : dbm((y.shortest_path_closure_assign(), y.dbm)),
    status(),
    redundancy_dbm() {
  // TODO: handle flags properly, possibly taking special cases into account.
  if (y.marked_empty()) {
    set_empty();
  }
  else if (y.marked_zero_dim_univ()) {
    set_zero_dim_univ();
  }
}

template <typename T>
inline Congruence_System
BD_Shape<T>::congruences() const {
  return minimized_congruences();
}

template <typename T>
inline void
BD_Shape<T>::add_constraints(const Constraint_System& cs) {
  for (Constraint_System::const_iterator i = cs.begin(),
         cs_end = cs.end(); i != cs_end; ++i) {
    add_constraint(*i);
  }
}

template <typename T>
inline void
BD_Shape<T>::add_recycled_constraints(Constraint_System& cs) {
  add_constraints(cs);
}

template <typename T>
inline void
BD_Shape<T>::add_congruences(const Congruence_System& cgs) {
  for (Congruence_System::const_iterator i = cgs.begin(),
         cgs_end = cgs.end(); i != cgs_end; ++i) {
    add_congruence(*i);
  }
}

template <typename T>
inline void
BD_Shape<T>::add_recycled_congruences(Congruence_System& cgs) {
  add_congruences(cgs);
}

template <typename T>
inline void
BD_Shape<T>::refine_with_constraint(const Constraint& c) {
  const dimension_type c_space_dim = c.space_dimension();
  // Dimension-compatibility check.
  if (c_space_dim > space_dimension()) {
    throw_dimension_incompatible("refine_with_constraint(c)", c);
  }

  if (!marked_empty()) {
    refine_no_check(c);
  }
}

template <typename T>
inline void
BD_Shape<T>::refine_with_constraints(const Constraint_System& cs) {
  // Dimension-compatibility check.
  if (cs.space_dimension() > space_dimension()) {
    throw_invalid_argument("refine_with_constraints(cs)",
                           "cs and *this are space-dimension incompatible");
  }

  for (Constraint_System::const_iterator i = cs.begin(),
         cs_end = cs.end(); !marked_empty() && i != cs_end; ++i) {
    refine_no_check(*i);
  }
}

template <typename T>
inline void
BD_Shape<T>::refine_with_congruence(const Congruence& cg) {
  const dimension_type cg_space_dim = cg.space_dimension();
  // Dimension-compatibility check.
  if (cg_space_dim > space_dimension()) {
    throw_dimension_incompatible("refine_with_congruence(cg)", cg);
  }

  if (!marked_empty()) {
    refine_no_check(cg);
  }
}

template <typename T>
void
BD_Shape<T>::refine_with_congruences(const Congruence_System& cgs) {
  // Dimension-compatibility check.
  if (cgs.space_dimension() > space_dimension()) {
    throw_invalid_argument("refine_with_congruences(cgs)",
                           "cgs and *this are space-dimension incompatible");
  }

  for (Congruence_System::const_iterator i = cgs.begin(),
         cgs_end = cgs.end(); !marked_empty() && i != cgs_end; ++i) {
    refine_no_check(*i);
  }
}

template <typename T>
inline void
BD_Shape<T>::refine_no_check(const Congruence& cg) {
  PPL_ASSERT(!marked_empty());
  PPL_ASSERT(cg.space_dimension() <= space_dimension());

  if (cg.is_proper_congruence()) {
    if (cg.is_inconsistent()) {
      set_empty();
    }
    // Other proper congruences are just ignored.
    return;
  }

  PPL_ASSERT(cg.is_equality());
  Constraint c(cg);
  refine_no_check(c);
}

template <typename T>
inline bool
BD_Shape<T>::can_recycle_constraint_systems() {
  return false;
}


template <typename T>
inline bool
BD_Shape<T>::can_recycle_congruence_systems() {
  return false;
}

template <typename T>
inline
BD_Shape<T>::BD_Shape(const Constraint_System& cs)
  : dbm(cs.space_dimension() + 1), status(), redundancy_dbm() {
  if (cs.space_dimension() > 0) {
    // A (non zero-dim) universe BDS is shortest-path closed.
    set_shortest_path_closed();
  }
  add_constraints(cs);
}

template <typename T>
template <typename Interval>
inline
BD_Shape<T>::BD_Shape(const Box<Interval>& box,
                      Complexity_Class)
  : dbm(box.space_dimension() + 1), status(), redundancy_dbm() {
  // Check emptiness for maximum precision.
  if (box.is_empty()) {
    set_empty();
  }
  else if (box.space_dimension() > 0) {
    // A (non zero-dim) universe BDS is shortest-path closed.
    set_shortest_path_closed();
    refine_with_constraints(box.constraints());
  }
}

template <typename T>
inline
BD_Shape<T>::BD_Shape(const Grid& grid,
                      Complexity_Class)
  : dbm(grid.space_dimension() + 1), status(), redundancy_dbm() {
  if (grid.space_dimension() > 0) {
    // A (non zero-dim) universe BDS is shortest-path closed.
    set_shortest_path_closed();
  }
  // Taking minimized congruences ensures maximum precision.
  refine_with_congruences(grid.minimized_congruences());
}

template <typename T>
template <typename U>
inline
BD_Shape<T>::BD_Shape(const Octagonal_Shape<U>& os,
                      Complexity_Class)
  : dbm(os.space_dimension() + 1), status(), redundancy_dbm() {
  // Check for emptiness for maximum precision.
  if (os.is_empty()) {
    set_empty();
  }
  else if (os.space_dimension() > 0) {
    // A (non zero-dim) universe BDS is shortest-path closed.
    set_shortest_path_closed();
    refine_with_constraints(os.constraints());
    // After refining, shortest-path closure is possibly lost
    // (even when `os' was strongly closed: recall that U
    // is possibly different from T).
  }
}

template <typename T>
inline BD_Shape<T>&
BD_Shape<T>::operator=(const BD_Shape& y) {
  dbm = y.dbm;
  status = y.status;
  if (y.marked_shortest_path_reduced()) {
    redundancy_dbm = y.redundancy_dbm;
  }
  return *this;
}

template <typename T>
inline
BD_Shape<T>::~BD_Shape() {
}

template <typename T>
inline void
BD_Shape<T>::m_swap(BD_Shape& y) {
  using std::swap;
  swap(dbm, y.dbm);
  swap(status, y.status);
  swap(redundancy_dbm, y.redundancy_dbm);
}

template <typename T>
inline dimension_type
BD_Shape<T>::space_dimension() const {
  return dbm.num_rows() - 1;
}

template <typename T>
inline bool
BD_Shape<T>::is_empty() const {
  shortest_path_closure_assign();
  return marked_empty();
}

template <typename T>
inline bool
BD_Shape<T>::bounds_from_above(const Linear_Expression& expr) const {
  return bounds(expr, true);
}

template <typename T>
inline bool
BD_Shape<T>::bounds_from_below(const Linear_Expression& expr) const {
  return bounds(expr, false);
}

template <typename T>
inline bool
BD_Shape<T>::maximize(const Linear_Expression& expr,
                      Coefficient& sup_n, Coefficient& sup_d,
                      bool& maximum) const {
  return max_min(expr, true, sup_n, sup_d, maximum);
}

template <typename T>
inline bool
BD_Shape<T>::maximize(const Linear_Expression& expr,
                      Coefficient& sup_n, Coefficient& sup_d, bool& maximum,
                      Generator& g) const {
  return max_min(expr, true, sup_n, sup_d, maximum, g);
}

template <typename T>
inline bool
BD_Shape<T>::minimize(const Linear_Expression& expr,
                      Coefficient& inf_n, Coefficient& inf_d,
                      bool& minimum) const {
  return max_min(expr, false, inf_n, inf_d, minimum);
}

template <typename T>
inline bool
BD_Shape<T>::minimize(const Linear_Expression& expr,
                      Coefficient& inf_n, Coefficient& inf_d, bool& minimum,
                      Generator& g) const {
  return max_min(expr, false, inf_n, inf_d, minimum, g);
}

template <typename T>
inline bool
BD_Shape<T>::is_topologically_closed() const {
  return true;
}

template <typename T>
inline bool
BD_Shape<T>::is_discrete() const {
  return affine_dimension() == 0;
}

template <typename T>
inline void
BD_Shape<T>::topological_closure_assign() {
}

/*! \relates BD_Shape */
template <typename T>
inline bool
operator==(const BD_Shape<T>& x, const BD_Shape<T>& y) {
  const dimension_type x_space_dim = x.space_dimension();
  // Dimension-compatibility check.
  if (x_space_dim != y.space_dimension()) {
    return false;
  }

  // Zero-dim BDSs are equal if and only if they are both empty or universe.
  if (x_space_dim == 0) {
    if (x.marked_empty()) {
      return y.marked_empty();
    }
    else {
      return !y.marked_empty();
    }
  }

  // The exact equivalence test requires shortest-path closure.
  x.shortest_path_closure_assign();
  y.shortest_path_closure_assign();

  // If one of two BDSs is empty, then they are equal
  // if and only if the other BDS is empty too.
  if (x.marked_empty()) {
    return y.marked_empty();
  }
  if (y.marked_empty()) {
    return false;
  }
  // Check for syntactic equivalence of the two (shortest-path closed)
  // systems of bounded differences.
  return x.dbm == y.dbm;
}

/*! \relates BD_Shape */
template <typename T>
inline bool
operator!=(const BD_Shape<T>& x, const BD_Shape<T>& y) {
  return !(x == y);
}

/*! \relates BD_Shape */
template <typename Temp, typename To, typename T>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const BD_Shape<T>& x,
                            const BD_Shape<T>& y,
                            const Rounding_Dir dir,
                            Temp& tmp0,
                            Temp& tmp1,
                            Temp& tmp2) {
  const dimension_type x_space_dim = x.space_dimension();
  // Dimension-compatibility check.
  if (x_space_dim != y.space_dimension()) {
    return false;
  }

  // Zero-dim BDSs are equal if and only if they are both empty or universe.
  if (x_space_dim == 0) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  // The distance computation requires shortest-path closure.
  x.shortest_path_closure_assign();
  y.shortest_path_closure_assign();

  // If one of two BDSs is empty, then they are equal if and only if
  // the other BDS is empty too.
  if (x.marked_empty() ||  y.marked_empty()) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  return rectilinear_distance_assign(r, x.dbm, y.dbm, dir, tmp0, tmp1, tmp2);
}

/*! \relates BD_Shape */
template <typename Temp, typename To, typename T>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const BD_Shape<T>& x,
                            const BD_Shape<T>& y,
                            const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return rectilinear_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates BD_Shape */
template <typename To, typename T>
inline bool
rectilinear_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                            const BD_Shape<T>& x,
                            const BD_Shape<T>& y,
                            const Rounding_Dir dir) {
  return rectilinear_distance_assign<To, To, T>(r, x, y, dir);
}

/*! \relates BD_Shape */
template <typename Temp, typename To, typename T>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const BD_Shape<T>& x,
                          const BD_Shape<T>& y,
                          const Rounding_Dir dir,
                          Temp& tmp0,
                          Temp& tmp1,
                          Temp& tmp2) {
  const dimension_type x_space_dim = x.space_dimension();
  // Dimension-compatibility check.
  if (x_space_dim != y.space_dimension()) {
    return false;
  }

  // Zero-dim BDSs are equal if and only if they are both empty or universe.
  if (x_space_dim == 0) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  // The distance computation requires shortest-path closure.
  x.shortest_path_closure_assign();
  y.shortest_path_closure_assign();

  // If one of two BDSs is empty, then they are equal if and only if
  // the other BDS is empty too.
  if (x.marked_empty() ||  y.marked_empty()) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  return euclidean_distance_assign(r, x.dbm, y.dbm, dir, tmp0, tmp1, tmp2);
}

/*! \relates BD_Shape */
template <typename Temp, typename To, typename T>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const BD_Shape<T>& x,
                          const BD_Shape<T>& y,
                          const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return euclidean_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates BD_Shape */
template <typename To, typename T>
inline bool
euclidean_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                          const BD_Shape<T>& x,
                          const BD_Shape<T>& y,
                          const Rounding_Dir dir) {
  return euclidean_distance_assign<To, To, T>(r, x, y, dir);
}

/*! \relates BD_Shape */
template <typename Temp, typename To, typename T>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const BD_Shape<T>& x,
                           const BD_Shape<T>& y,
                           const Rounding_Dir dir,
                           Temp& tmp0,
                           Temp& tmp1,
                           Temp& tmp2) {
  const dimension_type x_space_dim = x.space_dimension();
  // Dimension-compatibility check.
  if (x_space_dim != y.space_dimension()) {
    return false;
  }
  // Zero-dim BDSs are equal if and only if they are both empty or universe.
  if (x_space_dim == 0) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  // The distance computation requires shortest-path closure.
  x.shortest_path_closure_assign();
  y.shortest_path_closure_assign();

  // If one of two BDSs is empty, then they are equal if and only if
  // the other BDS is empty too.
  if (x.marked_empty() ||  y.marked_empty()) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  return l_infinity_distance_assign(r, x.dbm, y.dbm, dir, tmp0, tmp1, tmp2);
}

/*! \relates BD_Shape */
template <typename Temp, typename To, typename T>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const BD_Shape<T>& x,
                           const BD_Shape<T>& y,
                           const Rounding_Dir dir) {
  typedef Checked_Number<Temp, Extended_Number_Policy> Checked_Temp;
  PPL_DIRTY_TEMP(Checked_Temp, tmp0);
  PPL_DIRTY_TEMP(Checked_Temp, tmp1);
  PPL_DIRTY_TEMP(Checked_Temp, tmp2);
  return l_infinity_distance_assign(r, x, y, dir, tmp0, tmp1, tmp2);
}

/*! \relates BD_Shape */
template <typename To, typename T>
inline bool
l_infinity_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                           const BD_Shape<T>& x,
                           const BD_Shape<T>& y,
                           const Rounding_Dir dir) {
  return l_infinity_distance_assign<To, To, T>(r, x, y, dir);
}

template <typename T>
inline void
BD_Shape<T>::add_dbm_constraint(const dimension_type i,
                                const dimension_type j,
                                const N& k) {
  // Private method: the caller has to ensure the following.
  PPL_ASSERT(i <= space_dimension() && j <= space_dimension() && i != j);
  N& dbm_ij = dbm[i][j];
  if (dbm_ij > k) {
    dbm_ij = k;
    if (marked_shortest_path_closed()) {
      reset_shortest_path_closed();
    }
  }
}

template <typename T>
inline void
BD_Shape<T>::add_dbm_constraint(const dimension_type i,
                                const dimension_type j,
                                Coefficient_traits::const_reference numer,
                                Coefficient_traits::const_reference denom) {
  // Private method: the caller has to ensure the following.
  PPL_ASSERT(i <= space_dimension() && j <= space_dimension() && i != j);
  PPL_ASSERT(denom != 0);
  PPL_DIRTY_TEMP(N, k);
  div_round_up(k, numer, denom);
  add_dbm_constraint(i, j, k);
}

template <typename T>
inline void
BD_Shape<T>::time_elapse_assign(const BD_Shape& y) {
  // Dimension-compatibility check.
  if (space_dimension() != y.space_dimension()) {
    throw_dimension_incompatible("time_elapse_assign(y)", y);
  }
  // Compute time-elapse on polyhedra.
  // TODO: provide a direct implementation.
  C_Polyhedron ph_x(constraints());
  C_Polyhedron ph_y(y.constraints());
  ph_x.time_elapse_assign(ph_y);
  BD_Shape<T> x(ph_x);
  m_swap(x);
  PPL_ASSERT(OK());
}

template <typename T>
inline bool
BD_Shape<T>::strictly_contains(const BD_Shape& y) const {
  const BD_Shape<T>& x = *this;
  return x.contains(y) && !y.contains(x);
}

template <typename T>
inline bool
BD_Shape<T>::upper_bound_assign_if_exact(const BD_Shape& y) {
  if (space_dimension() != y.space_dimension()) {
    throw_dimension_incompatible("upper_bound_assign_if_exact(y)", y);
  }
#if 0
  return BFT00_upper_bound_assign_if_exact(y);
#else
  const bool integer_upper_bound = false;
  return BHZ09_upper_bound_assign_if_exact<integer_upper_bound>(y);
#endif
}

template <typename T>
inline bool
BD_Shape<T>::integer_upper_bound_assign_if_exact(const BD_Shape& y) {
  PPL_COMPILE_TIME_CHECK(std::numeric_limits<T>::is_integer,
                         "BD_Shape<T>::integer_upper_bound_assign_if_exact(y):"
                         " T in not an integer datatype.");
  if (space_dimension() != y.space_dimension()) {
    throw_dimension_incompatible("integer_upper_bound_assign_if_exact(y)", y);
  }
  const bool integer_upper_bound = true;
  return BHZ09_upper_bound_assign_if_exact<integer_upper_bound>(y);
}

template <typename T>
inline void
BD_Shape<T>
::remove_higher_space_dimensions(const dimension_type new_dimension) {
  // Dimension-compatibility check: the variable having
  // maximum index is the one occurring last in the set.
  const dimension_type space_dim = space_dimension();
  if (new_dimension > space_dim) {
    throw_dimension_incompatible("remove_higher_space_dimensions(nd)",
                                 new_dimension);
  }

  // The removal of no dimensions from any BDS is a no-op.
  // Note that this case also captures the only legal removal of
  // dimensions from a zero-dim space BDS.
  if (new_dimension == space_dim) {
    PPL_ASSERT(OK());
    return;
  }

  // Shortest-path closure is necessary as in remove_space_dimensions().
  shortest_path_closure_assign();
  dbm.resize_no_copy(new_dimension + 1);

  // Shortest-path closure is maintained.
  // TODO: see whether or not reduction can be (efficiently!) maintained too.
  if (marked_shortest_path_reduced()) {
    reset_shortest_path_reduced();
  }

  // If we removed _all_ dimensions from a non-empty BDS,
  // the zero-dim universe BDS has been obtained.
  if (new_dimension == 0 && !marked_empty()) {
    set_zero_dim_univ();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::wrap_assign(const Variables_Set& vars,
                         Bounded_Integer_Type_Width w,
                         Bounded_Integer_Type_Representation r,
                         Bounded_Integer_Type_Overflow o,
                         const Constraint_System* cs_p,
                         unsigned complexity_threshold,
                         bool wrap_individually) {
  Implementation::wrap_assign(*this,
                              vars, w, r, o, cs_p,
                              complexity_threshold, wrap_individually,
                              "BD_Shape");
}

template <typename T>
inline void
BD_Shape<T>::CC76_extrapolation_assign(const BD_Shape& y, unsigned* tp) {
  static N stop_points[] = {
    N(-2, ROUND_UP),
    N(-1, ROUND_UP),
    N( 0, ROUND_UP),
    N( 1, ROUND_UP),
    N( 2, ROUND_UP)
  };
  CC76_extrapolation_assign(y,
                            stop_points,
                            stop_points
                            + sizeof(stop_points)/sizeof(stop_points[0]),
                            tp);
}

template <typename T>
inline void
BD_Shape<T>::H79_widening_assign(const BD_Shape& y, unsigned* tp) {
  // Compute the H79 widening on polyhedra.
  // TODO: provide a direct implementation.
  C_Polyhedron ph_x(constraints());
  C_Polyhedron ph_y(y.constraints());
  ph_x.H79_widening_assign(ph_y, tp);
  BD_Shape x(ph_x);
  m_swap(x);
  PPL_ASSERT(OK());
}

template <typename T>
inline void
BD_Shape<T>::widening_assign(const BD_Shape& y, unsigned* tp) {
  H79_widening_assign(y, tp);
}

template <typename T>
inline void
BD_Shape<T>::limited_H79_extrapolation_assign(const BD_Shape& y,
                                              const Constraint_System& cs,
                                              unsigned* tp) {
  // Compute the limited H79 extrapolation on polyhedra.
  // TODO: provide a direct implementation.
  C_Polyhedron ph_x(constraints());
  C_Polyhedron ph_y(y.constraints());
  ph_x.limited_H79_extrapolation_assign(ph_y, cs, tp);
  BD_Shape x(ph_x);
  m_swap(x);
  PPL_ASSERT(OK());
}

template <typename T>
inline memory_size_type
BD_Shape<T>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename T>
inline int32_t
BD_Shape<T>::hash_code() const {
  return hash_code_from_dimension(space_dimension());
}

template <typename T>
template <typename Interval_Info>
inline void
BD_Shape<T>::generalized_refine_with_linear_form_inequality(
             const Linear_Form<Interval<T, Interval_Info> >& left,
             const Linear_Form<Interval<T, Interval_Info> >& right,
             const Relation_Symbol relsym) {
  switch (relsym) {
  case EQUAL:
    // TODO: see if we can handle this case more efficiently.
    refine_with_linear_form_inequality(left, right);
    refine_with_linear_form_inequality(right, left);
    break;
  case LESS_THAN:
  case LESS_OR_EQUAL:
    refine_with_linear_form_inequality(left, right);
    break;
  case GREATER_THAN:
  case GREATER_OR_EQUAL:
    refine_with_linear_form_inequality(right, left);
    break;
  case NOT_EQUAL:
    break;
  default:
    PPL_UNREACHABLE;
  }
}

template <typename T>
template <typename Interval_Info>
inline void
BD_Shape<T>
::refine_fp_interval_abstract_store(Box<Interval<T, Interval_Info> >&
                                    store) const {

  // Check that T is a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<T>::is_exact,
                     "BD_Shape<T>::refine_fp_interval_abstract_store:"
                     " T not a floating point type.");

  typedef Interval<T, Interval_Info> FP_Interval_Type;
  store.intersection_assign(Box<FP_Interval_Type>(*this));
}

template <typename T>
inline void
BD_Shape<T>::drop_some_non_integer_points_helper(N& elem) {
  if (!is_integer(elem)) {
    Result r = floor_assign_r(elem, elem, ROUND_DOWN);
    PPL_USED(r);
    PPL_ASSERT(r == V_EQ);
    reset_shortest_path_closed();
  }
}

/*! \relates BD_Shape */
template <typename T>
inline void
swap(BD_Shape<T>& x, BD_Shape<T>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/BD_Shape_templates.hh line 1. */
/* BD_Shape class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/BD_Shape_templates.hh line 40. */
#include <vector>
#include <deque>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <algorithm>

namespace Parma_Polyhedra_Library {

template <typename T>
BD_Shape<T>::BD_Shape(const Congruence_System& cgs)
  : dbm(cgs.space_dimension() + 1),
    status(),
    redundancy_dbm() {
  add_congruences(cgs);
}

template <typename T>
BD_Shape<T>::BD_Shape(const Generator_System& gs)
  : dbm(gs.space_dimension() + 1), status(), redundancy_dbm() {
  const Generator_System::const_iterator gs_begin = gs.begin();
  const Generator_System::const_iterator gs_end = gs.end();
  if (gs_begin == gs_end) {
    // An empty generator system defines the empty BD shape.
    set_empty();
    return;
  }

  const dimension_type space_dim = space_dimension();
  DB_Row<N>& dbm_0 = dbm[0];
  PPL_DIRTY_TEMP(N, tmp);

  bool dbm_initialized = false;
  bool point_seen = false;
  // Going through all the points and closure points.
  for (Generator_System::const_iterator gs_i = gs_begin;
       gs_i != gs_end; ++gs_i) {
    const Generator& g = *gs_i;
    switch (g.type()) {
    case Generator::POINT:
      point_seen = true;
      // Intentionally fall through.
    case Generator::CLOSURE_POINT:
      if (!dbm_initialized) {
        // When handling the first (closure) point, we initialize the DBM.
        dbm_initialized = true;
        const Coefficient& d = g.divisor();
        // TODO: Check if the following loop can be optimized used
        // Generator::expr_type::const_iterator.
        for (dimension_type i = space_dim; i > 0; --i) {
          const Coefficient& g_i = g.expression().get(Variable(i - 1));
          DB_Row<N>& dbm_i = dbm[i];
          for (dimension_type j = space_dim; j > 0; --j) {
            if (i != j) {
              const Coefficient& g_j = g.expression().get(Variable(j - 1));
              div_round_up(dbm_i[j], g_j - g_i, d);
            }
          }
          div_round_up(dbm_i[0], -g_i, d);
        }
        for (dimension_type j = space_dim; j > 0; --j) {
          const Coefficient& g_j = g.expression().get(Variable(j - 1));
          div_round_up(dbm_0[j], g_j, d);
        }
        // Note: no need to initialize the first element of the main diagonal.
      }
      else {
        // This is not the first point: the DBM already contains
        // valid values and we must compute maxima.
        const Coefficient& d = g.divisor();
        // TODO: Check if the following loop can be optimized used
        // Generator::expr_type::const_iterator.
        for (dimension_type i = space_dim; i > 0; --i) {
          const Coefficient& g_i = g.expression().get(Variable(i - 1));
          DB_Row<N>& dbm_i = dbm[i];
          // The loop correctly handles the case when i == j.
          for (dimension_type j = space_dim; j > 0; --j) {
            const Coefficient& g_j = g.expression().get(Variable(j - 1));
            div_round_up(tmp, g_j - g_i, d);
            max_assign(dbm_i[j], tmp);
          }
          div_round_up(tmp, -g_i, d);
          max_assign(dbm_i[0], tmp);
        }
        for (dimension_type j = space_dim; j > 0; --j) {
          const Coefficient& g_j = g.expression().get(Variable(j - 1));
          div_round_up(tmp, g_j, d);
          max_assign(dbm_0[j], tmp);
        }
      }
      break;
    default:
      // Lines and rays temporarily ignored.
      break;
    }
  }

  if (!point_seen) {
    // The generator system is not empty, but contains no points.
    throw_invalid_argument("BD_Shape(gs)",
                           "the non-empty generator system gs "
                           "contains no points.");
  }

  // Going through all the lines and rays.
  for (Generator_System::const_iterator gs_i = gs_begin;
       gs_i != gs_end; ++gs_i) {
    const Generator& g = *gs_i;
    switch (g.type()) {
    case Generator::LINE:
      // TODO: Check if the following loop can be optimized used
      // Generator::expr_type::const_iterator.
      for (dimension_type i = space_dim; i > 0; --i) {
        const Coefficient& g_i = g.expression().get(Variable(i - 1));
        DB_Row<N>& dbm_i = dbm[i];
        // The loop correctly handles the case when i == j.
        for (dimension_type j = space_dim; j > 0; --j) {
          if (g_i != g.expression().get(Variable(j - 1))) {
            assign_r(dbm_i[j], PLUS_INFINITY, ROUND_NOT_NEEDED);
          }
        }
        if (g_i != 0) {
          assign_r(dbm_i[0], PLUS_INFINITY, ROUND_NOT_NEEDED);
        }
      }
      for (Generator::expr_type::const_iterator i = g.expression().begin(),
            i_end = g.expression().end(); i != i_end; ++i) {
        assign_r(dbm_0[i.variable().space_dimension()],
                 PLUS_INFINITY, ROUND_NOT_NEEDED);
      }
      break;
    case Generator::RAY:
      // TODO: Check if the following loop can be optimized used
      // Generator::expr_type::const_iterator.
      for (dimension_type i = space_dim; i > 0; --i) {
        const Coefficient& g_i = g.expression().get(Variable(i - 1));
        DB_Row<N>& dbm_i = dbm[i];
        // The loop correctly handles the case when i == j.
        for (dimension_type j = space_dim; j > 0; --j) {
          if (g_i < g.expression().get(Variable(j - 1))) {
            assign_r(dbm_i[j], PLUS_INFINITY, ROUND_NOT_NEEDED);
          }
        }
        if (g_i < 0) {
          assign_r(dbm_i[0], PLUS_INFINITY, ROUND_NOT_NEEDED);
        }
      }
      for (Generator::expr_type::const_iterator i = g.expression().begin(),
            i_end = g.expression().end(); i != i_end; ++i) {
        if (*i > 0) {
          assign_r(dbm_0[i.variable().space_dimension()],
                   PLUS_INFINITY, ROUND_NOT_NEEDED);
        }
      }
      break;
    default:
      // Points and closure points already dealt with.
      break;
    }
  }
  set_shortest_path_closed();
  PPL_ASSERT(OK());
}

template <typename T>
BD_Shape<T>::BD_Shape(const Polyhedron& ph, const Complexity_Class complexity)
  : dbm(), status(), redundancy_dbm() {
  const dimension_type num_dimensions = ph.space_dimension();

  if (ph.marked_empty()) {
    *this = BD_Shape<T>(num_dimensions, EMPTY);
    return;
  }

  if (num_dimensions == 0) {
    *this = BD_Shape<T>(num_dimensions, UNIVERSE);
    return;
  }

  // Build from generators when we do not care about complexity
  // or when the process has polynomial complexity.
  if (complexity == ANY_COMPLEXITY
      || (!ph.has_pending_constraints() && ph.generators_are_up_to_date())) {
    *this = BD_Shape<T>(ph.generators());
    return;
  }

  // We cannot afford exponential complexity, we do not have a complete set
  // of generators for the polyhedron, and the polyhedron is not trivially
  // empty or zero-dimensional.  Constraints, however, are up to date.
  PPL_ASSERT(ph.constraints_are_up_to_date());

  if (!ph.has_something_pending() && ph.constraints_are_minimized()) {
    // If the constraint system of the polyhedron is minimized,
    // the test `is_universe()' has polynomial complexity.
    if (ph.is_universe()) {
      *this = BD_Shape<T>(num_dimensions, UNIVERSE);
      return;
    }
  }

  // See if there is at least one inconsistent constraint in `ph.con_sys'.
  for (Constraint_System::const_iterator i = ph.con_sys.begin(),
         cs_end = ph.con_sys.end(); i != cs_end; ++i) {
    if (i->is_inconsistent()) {
      *this = BD_Shape<T>(num_dimensions, EMPTY);
      return;
    }
  }

  // If `complexity' allows it, use simplex to derive the exact (modulo
  // the fact that our BDSs are topologically closed) variable bounds.
  if (complexity == SIMPLEX_COMPLEXITY) {
    MIP_Problem lp(num_dimensions);
    lp.set_optimization_mode(MAXIMIZATION);

    const Constraint_System& ph_cs = ph.constraints();
    if (!ph_cs.has_strict_inequalities()) {
      lp.add_constraints(ph_cs);
    }
    else {
      // Adding to `lp' a topologically closed version of `ph_cs'.
      for (Constraint_System::const_iterator i = ph_cs.begin(),
             ph_cs_end = ph_cs.end(); i != ph_cs_end; ++i) {
        const Constraint& c = *i;
        if (c.is_strict_inequality()) {
          Linear_Expression expr(c.expression());
          lp.add_constraint(expr >= 0);
        }
        else {
          lp.add_constraint(c);
        }
      }
    }

    // Check for unsatisfiability.
    if (!lp.is_satisfiable()) {
      *this = BD_Shape<T>(num_dimensions, EMPTY);
      return;
    }

    // Start with a universe BDS that will be refined by the simplex.
    *this = BD_Shape<T>(num_dimensions, UNIVERSE);
    // Get all the upper bounds.
    Generator g(point());
    PPL_DIRTY_TEMP_COEFFICIENT(numer);
    PPL_DIRTY_TEMP_COEFFICIENT(denom);
    for (dimension_type i = 1; i <= num_dimensions; ++i) {
      Variable x(i-1);
      // Evaluate optimal upper bound for `x <= ub'.
      lp.set_objective_function(x);
      if (lp.solve() == OPTIMIZED_MIP_PROBLEM) {
        g = lp.optimizing_point();
        lp.evaluate_objective_function(g, numer, denom);
        div_round_up(dbm[0][i], numer, denom);
      }
      // Evaluate optimal upper bound for `x - y <= ub'.
      for (dimension_type j = 1; j <= num_dimensions; ++j) {
        if (i == j) {
          continue;
        }
        Variable y(j-1);
        lp.set_objective_function(x - y);
        if (lp.solve() == OPTIMIZED_MIP_PROBLEM) {
          g = lp.optimizing_point();
          lp.evaluate_objective_function(g, numer, denom);
          div_round_up(dbm[j][i], numer, denom);
        }
      }
      // Evaluate optimal upper bound for `-x <= ub'.
      lp.set_objective_function(-x);
      if (lp.solve() == OPTIMIZED_MIP_PROBLEM) {
        g = lp.optimizing_point();
        lp.evaluate_objective_function(g, numer, denom);
        div_round_up(dbm[i][0], numer, denom);
      }
    }
    set_shortest_path_closed();
    PPL_ASSERT(OK());
    return;
  }

  // Extract easy-to-find bounds from constraints.
  PPL_ASSERT(complexity == POLYNOMIAL_COMPLEXITY);
  *this = BD_Shape<T>(num_dimensions, UNIVERSE);
  refine_with_constraints(ph.constraints());
}

template <typename T>
dimension_type
BD_Shape<T>::affine_dimension() const {
  const dimension_type space_dim = space_dimension();
  // A zero-space-dim shape always has affine dimension zero.
  if (space_dim == 0) {
    return 0;
  }
  // Shortest-path closure is necessary to detect emptiness
  // and all (possibly implicit) equalities.
  shortest_path_closure_assign();
  if (marked_empty()) {
    return 0;
  }
  // The vector `predecessor' is used to represent equivalence classes:
  // `predecessor[i] == i' if and only if `i' is the leader of its
  // equivalence class (i.e., the minimum index in the class).
  std::vector<dimension_type> predecessor;
  compute_predecessors(predecessor);

  // Due to the fictitious variable `0', the affine dimension is one
  // less the number of equivalence classes.
  dimension_type affine_dim = 0;
  // Note: disregard the first equivalence class.
  for (dimension_type i = 1; i <= space_dim; ++i) {
    if (predecessor[i] == i) {
      ++affine_dim;
    }
  }
  return affine_dim;
}

template <typename T>
Congruence_System
BD_Shape<T>::minimized_congruences() const {
  // Shortest-path closure is necessary to detect emptiness
  // and all (possibly implicit) equalities.
  shortest_path_closure_assign();

  const dimension_type space_dim = space_dimension();
  Congruence_System cgs(space_dim);

  if (space_dim == 0) {
    if (marked_empty()) {
      cgs = Congruence_System::zero_dim_empty();
    }
    return cgs;
  }

  if (marked_empty()) {
    cgs.insert(Congruence::zero_dim_false());
    return cgs;
  }

  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);

  // Compute leader information.
  std::vector<dimension_type> leaders;
  compute_leaders(leaders);

  // Go through the non-leaders to generate equality constraints.
  const DB_Row<N>& dbm_0 = dbm[0];
  for (dimension_type i = 1; i <= space_dim; ++i) {
    const dimension_type leader = leaders[i];
    if (i != leader) {
      // Generate the constraint relating `i' and its leader.
      if (leader == 0) {
        // A unary equality has to be generated.
        PPL_ASSERT(!is_plus_infinity(dbm_0[i]));
        numer_denom(dbm_0[i], numer, denom);
        cgs.insert(denom*Variable(i-1) == numer);
      }
      else {
        // A binary equality has to be generated.
        PPL_ASSERT(!is_plus_infinity(dbm[i][leader]));
        numer_denom(dbm[i][leader], numer, denom);
        cgs.insert(denom*Variable(leader-1) - denom*Variable(i-1) == numer);
      }
    }
  }
  return cgs;
}

template <typename T>
void
BD_Shape<T>::add_constraint(const Constraint& c) {
  // Dimension-compatibility check.
  if (c.space_dimension() > space_dimension()) {
    throw_dimension_incompatible("add_constraint(c)", c);
  }
  // Get rid of strict inequalities.
  if (c.is_strict_inequality()) {
    if (c.is_inconsistent()) {
      set_empty();
      return;
    }
    if (c.is_tautological()) {
      return;
    }
    // Nontrivial strict inequalities are not allowed.
    throw_invalid_argument("add_constraint(c)",
                           "strict inequalities are not allowed");
  }

  dimension_type num_vars = 0;
  dimension_type i = 0;
  dimension_type j = 0;
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  // Constraints that are not bounded differences are not allowed.
  if (!BD_Shape_Helpers::extract_bounded_difference(c, num_vars, i, j, coeff)) {
    throw_invalid_argument("add_constraint(c)",
                           "c is not a bounded difference constraint");
  }
  const Coefficient& inhomo = c.inhomogeneous_term();
  if (num_vars == 0) {
    // Dealing with a trivial constraint (not a strict inequality).
    if (inhomo < 0
        || (inhomo != 0 && c.is_equality())) {
      set_empty();
    }
    return;
  }

  // Select the cell to be modified for the "<=" part of the constraint,
  // and set `coeff' to the absolute value of itself.
  const bool negative = (coeff < 0);
  if (negative) {
    neg_assign(coeff);
  }
  bool changed = false;
  N& x = negative ? dbm[i][j] : dbm[j][i];
  // Compute the bound for `x', rounding towards plus infinity.
  PPL_DIRTY_TEMP(N, d);
  div_round_up(d, inhomo, coeff);
  if (x > d) {
    x = d;
    changed = true;
  }

  if (c.is_equality()) {
    N& y = negative ? dbm[j][i] : dbm[i][j];
    // Also compute the bound for `y', rounding towards plus infinity.
    PPL_DIRTY_TEMP_COEFFICIENT(minus_c_term);
    neg_assign(minus_c_term, inhomo);
    div_round_up(d, minus_c_term, coeff);
    if (y > d) {
      y = d;
      changed = true;
    }
  }

  // In general, adding a constraint does not preserve the shortest-path
  // closure or reduction of the bounded difference shape.
  if (changed && marked_shortest_path_closed()) {
    reset_shortest_path_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::add_congruence(const Congruence& cg) {
  const dimension_type cg_space_dim = cg.space_dimension();
  // Dimension-compatibility check:
  // the dimension of `cg' can not be greater than space_dim.
  if (space_dimension() < cg_space_dim) {
    throw_dimension_incompatible("add_congruence(cg)", cg);
  }
  // Handle the case of proper congruences first.
  if (cg.is_proper_congruence()) {
    if (cg.is_tautological()) {
      return;
    }
    if (cg.is_inconsistent()) {
      set_empty();
      return;
    }
    // Non-trivial and proper congruences are not allowed.
    throw_invalid_argument("add_congruence(cg)",
                           "cg is a non-trivial, proper congruence");
  }

  PPL_ASSERT(cg.is_equality());
  Constraint c(cg);
  add_constraint(c);
}

template <typename T>
void
BD_Shape<T>::refine_no_check(const Constraint& c) {
  PPL_ASSERT(!marked_empty());
  PPL_ASSERT(c.space_dimension() <= space_dimension());

  dimension_type num_vars = 0;
  dimension_type i = 0;
  dimension_type j = 0;
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  // Constraints that are not bounded differences are ignored.
  if (!BD_Shape_Helpers::extract_bounded_difference(c, num_vars, i, j, coeff)) {
    return;
  }
  const Coefficient& inhomo = c.inhomogeneous_term();
  if (num_vars == 0) {
    // Dealing with a trivial constraint (might be a strict inequality).
    if (inhomo < 0
        || (c.is_equality() && inhomo != 0)
        || (c.is_strict_inequality() && inhomo == 0)) {
      set_empty();
    }
    return;
  }

  // Select the cell to be modified for the "<=" part of the constraint,
  // and set `coeff' to the absolute value of itself.
  const bool negative = (coeff < 0);
  N& x = negative ? dbm[i][j] : dbm[j][i];
  N& y = negative ? dbm[j][i] : dbm[i][j];
  if (negative) {
    neg_assign(coeff);
  }
  bool changed = false;
  // Compute the bound for `x', rounding towards plus infinity.
  PPL_DIRTY_TEMP(N, d);
  div_round_up(d, inhomo, coeff);
  if (x > d) {
    x = d;
    changed = true;
  }

  if (c.is_equality()) {
    // Also compute the bound for `y', rounding towards plus infinity.
    PPL_DIRTY_TEMP_COEFFICIENT(minus_c_term);
    neg_assign(minus_c_term, inhomo);
    div_round_up(d, minus_c_term, coeff);
    if (y > d) {
      y = d;
      changed = true;
    }
  }

  // In general, adding a constraint does not preserve the shortest-path
  // closure or reduction of the bounded difference shape.
  if (changed && marked_shortest_path_closed()) {
    reset_shortest_path_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::concatenate_assign(const BD_Shape& y) {
  BD_Shape& x = *this;

  const dimension_type x_space_dim = x.space_dimension();
  const dimension_type y_space_dim = y.space_dimension();

  // If `y' is an empty 0-dim space bounded difference shape,
  // let `*this' become empty.
  if (y_space_dim == 0 && y.marked_empty()) {
    set_empty();
    return;
  }

  // If `x' is an empty 0-dim space BDS, then it is sufficient to adjust
  // the dimension of the vector space.
  if (x_space_dim == 0 && marked_empty()) {
    dbm.grow(y_space_dim + 1);
    PPL_ASSERT(OK());
    return;
  }
  // First we increase the space dimension of `x' by adding
  // `y.space_dimension()' new dimensions.
  // The matrix for the new system of constraints is obtained
  // by leaving the old system of constraints in the upper left-hand side
  // and placing the constraints of `y' in the lower right-hand side,
  // except the constraints as `y(i) >= cost' or `y(i) <= cost', that are
  // placed in the right position on the new matrix.
  add_space_dimensions_and_embed(y_space_dim);
  const dimension_type new_space_dim = x_space_dim + y_space_dim;
  for (dimension_type i = x_space_dim + 1; i <= new_space_dim; ++i) {
    DB_Row<N>& dbm_i = dbm[i];
    dbm_i[0] = y.dbm[i - x_space_dim][0];
    dbm[0][i] = y.dbm[0][i - x_space_dim];
    for (dimension_type j = x_space_dim + 1; j <= new_space_dim; ++j) {
      dbm_i[j] = y.dbm[i - x_space_dim][j - x_space_dim];
    }
  }

  if (marked_shortest_path_closed()) {
    reset_shortest_path_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
bool
BD_Shape<T>::contains(const BD_Shape& y) const {
  const BD_Shape<T>& x = *this;
  const dimension_type x_space_dim = x.space_dimension();

  // Dimension-compatibility check.
  if (x_space_dim != y.space_dimension()) {
    throw_dimension_incompatible("contains(y)", y);
  }
  if (x_space_dim == 0) {
    // The zero-dimensional empty shape only contains another
    // zero-dimensional empty shape.
    // The zero-dimensional universe shape contains any other
    // zero-dimensional shape.
    return marked_empty() ? y.marked_empty() : true;
  }

  /*
    The `y' bounded difference shape must be closed.  As an example,
    consider the case where in `*this' we have the constraints

    x1 - x2 <= 1,
    x1      <= 3,
    x2      <= 2,

    and in `y' the constraints are

    x1 - x2 <= 0,
    x2      <= 1.

    Without closure the (erroneous) analysis of the inhomogeneous terms
    would conclude containment does not hold.  Closing `y' results into
    the "discovery" of the implicit constraint

    x1      <= 1,

    at which point the inhomogeneous terms can be examined to determine
    that containment does hold.
  */
  y.shortest_path_closure_assign();
  // An empty shape is contained in any other dimension-compatible shapes.
  if (y.marked_empty()) {
    return true;
  }
  // If `x' is empty it can not contain `y' (which is not empty).
  if (x.is_empty()) {
    return false;
  }
  // `*this' contains `y' if and only if every cell of `dbm'
  // is greater than or equal to the correspondent one of `y.dbm'.
  for (dimension_type i = x_space_dim + 1; i-- > 0; ) {
    const DB_Row<N>& x_dbm_i = x.dbm[i];
    const DB_Row<N>& y_dbm_i = y.dbm[i];
    for (dimension_type j = x_space_dim + 1; j-- > 0; ) {
      if (x_dbm_i[j] < y_dbm_i[j]) {
        return false;
      }
    }
  }
  return true;
}

template <typename T>
bool
BD_Shape<T>::is_disjoint_from(const BD_Shape& y) const {
  const dimension_type space_dim = space_dimension();
  // Dimension-compatibility check.
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("is_disjoint_from(y)", y);
  }
  // If one of the two bounded difference shape is empty,
  // then the two bounded difference shape are disjoint.
  shortest_path_closure_assign();
  if (marked_empty()) {
    return true;
  }
  y.shortest_path_closure_assign();
  if (y.marked_empty()) {
    return true;
  }
  // Two BDSs are disjoint when their intersection is empty.
  // That is if and only if there exists at least a bounded difference
  // such that the upper bound of the bounded difference in the first
  // BD_Shape is strictly less than the lower bound of
  // the corresponding bounded difference in the second BD_Shape
  // or vice versa.
  // For example: let be
  // in `*this':    -a_j_i <= v_j - v_i <= a_i_j;
  // and in `y':    -b_j_i <= v_j - v_i <= b_i_j;
  // `*this' and `y' are disjoint if
  // 1.) a_i_j < -b_j_i or
  // 2.) b_i_j < -a_j_i.
  PPL_DIRTY_TEMP(N, tmp);
  for (dimension_type i = space_dim+1; i-- > 0; ) {
    const DB_Row<N>& x_i = dbm[i];
    for (dimension_type j = space_dim+1; j-- > 0; ) {
      neg_assign_r(tmp, y.dbm[j][i], ROUND_UP);
      if (x_i[j] < tmp) {
        return true;
      }
    }
  }

  return false;
}

template <typename T>
bool
BD_Shape<T>::is_universe() const {
  if (marked_empty()) {
    return false;
  }
  const dimension_type space_dim = space_dimension();
  // If the BDS is non-empty and zero-dimensional,
  // then it is necessarily the universe BDS.
  if (space_dim == 0) {
    return true;
  }
  // A bounded difference shape defining the universe BDS can only
  // contain trivial constraints.
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    const DB_Row<N>& dbm_i = dbm[i];
    for (dimension_type j = space_dim + 1; j-- > 0; ) {
      if (!is_plus_infinity(dbm_i[j])) {
        return false;
      }
    }
  }
  return true;
}

template <typename T>
bool
BD_Shape<T>::is_bounded() const {
  shortest_path_closure_assign();
  const dimension_type space_dim = space_dimension();
  // A zero-dimensional or empty BDS is bounded.
  if (marked_empty() || space_dim == 0) {
    return true;
  }
  // A bounded difference shape defining the bounded BDS never can
  // contain trivial constraints.
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    const DB_Row<N>& dbm_i = dbm[i];
    for (dimension_type j = space_dim + 1; j-- > 0; ) {
      if (i != j) {
        if (is_plus_infinity(dbm_i[j])) {
          return false;
        }
      }
    }
  }

  return true;
}

template <typename T>
bool
BD_Shape<T>::contains_integer_point() const {
  // Force shortest-path closure.
  if (is_empty()) {
    return false;
  }
  const dimension_type space_dim = space_dimension();
  if (space_dim == 0) {
    return true;
  }
  // A non-empty BD_Shape defined by integer constraints
  // necessarily contains an integer point.
  if (std::numeric_limits<T>::is_integer) {
    return true;
  }
  // Build an integer BD_Shape z with bounds at least as tight as
  // those in *this and then recheck for emptiness.
  BD_Shape<mpz_class> bds_z(space_dim);
  typedef BD_Shape<mpz_class>::N Z;
  bds_z.reset_shortest_path_closed();
  PPL_DIRTY_TEMP(N, tmp);
  bool all_integers = true;
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    DB_Row<Z>& z_i = bds_z.dbm[i];
    const DB_Row<N>& dbm_i = dbm[i];
    for (dimension_type j = space_dim + 1; j-- > 0; ) {
      const N& dbm_i_j = dbm_i[j];
      if (is_plus_infinity(dbm_i_j)) {
        continue;
      }
      if (is_integer(dbm_i_j)) {
        assign_r(z_i[j], dbm_i_j, ROUND_NOT_NEEDED);
      }
      else {
        all_integers = false;
        Z& z_i_j = z_i[j];
        // Copy dbm_i_j into z_i_j, but rounding downwards.
        neg_assign_r(tmp, dbm_i_j, ROUND_NOT_NEEDED);
        assign_r(z_i_j, tmp, ROUND_UP);
        neg_assign_r(z_i_j, z_i_j, ROUND_NOT_NEEDED);
      }
    }
  }
  return all_integers || !bds_z.is_empty();
}

template <typename T>
bool
BD_Shape<T>::frequency(const Linear_Expression& expr,
                       Coefficient& freq_n, Coefficient& freq_d,
                       Coefficient& val_n, Coefficient& val_d) const {
  dimension_type space_dim = space_dimension();
  // The dimension of `expr' must be at most the dimension of *this.
  if (space_dim < expr.space_dimension()) {
    throw_dimension_incompatible("frequency(e, ...)", "e", expr);
  }
  // Check if `expr' has a constant value.
  // If it is constant, set the frequency `freq_n' to 0
  // and return true. Otherwise the values for \p expr
  // are not discrete so return false.

  // Space dimension is 0: if empty, then return false;
  // otherwise the frequency is 0 and the value is the inhomogeneous term.
  if (space_dim == 0) {
    if (is_empty()) {
      return false;
    }
    freq_n = 0;
    freq_d = 1;
    val_n = expr.inhomogeneous_term();
    val_d = 1;
    return true;
  }

  shortest_path_closure_assign();
  // For an empty BD shape, we simply return false.
  if (marked_empty()) {
    return false;
  }
  // The BD shape has at least 1 dimension and is not empty.
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);
  PPL_DIRTY_TEMP(N, tmp);
  Linear_Expression le = expr;
  // Boolean to keep track of a variable `v' in expression `le'.
  // If we can replace `v' by an expression using variables other
  // than `v' and are already in `le', then this is set to true.

  PPL_DIRTY_TEMP_COEFFICIENT(val_denom);
  val_denom = 1;

  // TODO: This loop can be optimized more, if needed, exploiting the
  // (possible) sparseness of le.
  for (dimension_type i = dbm.num_rows(); i-- > 1; ) {
    const Variable v(i-1);
    coeff = le.coefficient(v);
    if (coeff == 0) {
      continue;
    }
    const DB_Row<N>& dbm_i = dbm[i];
    // Check if `v' is constant in the BD shape.
    assign_r(tmp, dbm_i[0], ROUND_NOT_NEEDED);
    if (is_additive_inverse(dbm[0][i], tmp)) {
      // If `v' is constant, replace it in `le' by the value.
      numer_denom(tmp, numer, denom);
      sub_mul_assign(le, coeff, v);
      le *= denom;
      le -= numer*coeff;
      val_denom *= denom;
      continue;
    }
    // Check the bounded differences with the other dimensions that
    // have non-zero coefficient in `le'.
    else {
      bool constant_v = false;
      for (Linear_Expression::const_iterator j = le.begin(),
            j_end = le.lower_bound(Variable(i - 1)); j != j_end; ++j) {
        const Variable vj = j.variable();
        const dimension_type j_dim = vj.space_dimension();
        assign_r(tmp, dbm_i[j_dim], ROUND_NOT_NEEDED);
        if (is_additive_inverse(dbm[j_dim][i], tmp)) {
          // The coefficient for `vj' in `le' is not 0
          // and the difference with `v' in the BD shape is constant.
          // So apply this equality to eliminate `v' in `le'.
          numer_denom(tmp, numer, denom);
          // Modifying le invalidates the iterators, but it's not a problem
          // since we are going to exit the loop.
          sub_mul_assign(le, coeff, v);
          add_mul_assign(le, coeff, vj);
          le *= denom;
          le -= numer*coeff;
          val_denom *= denom;
          constant_v = true;
          break;
        }
      }
      if (!constant_v) {
        // The expression `expr' is not constant.
        return false;
      }
    }
  }

  // The expression `expr' is constant.
  freq_n = 0;
  freq_d = 1;

  // Reduce `val_n' and `val_d'.
  normalize2(le.inhomogeneous_term(), val_denom, val_n, val_d);
  return true;
}

template <typename T>
bool
BD_Shape<T>::constrains(const Variable var) const {
  // `var' should be one of the dimensions of the BD shape.
  const dimension_type var_space_dim = var.space_dimension();
  if (space_dimension() < var_space_dim) {
    throw_dimension_incompatible("constrains(v)", "v", var);
  }
  shortest_path_closure_assign();
  // A BD shape known to be empty constrains all variables.
  // (Note: do not force emptiness check _yet_)
  if (marked_empty()) {
    return true;
  }
  // Check whether `var' is syntactically constrained.
  const DB_Row<N>& dbm_v = dbm[var_space_dim];
  for (dimension_type i = dbm.num_rows(); i-- > 0; ) {
    if (!is_plus_infinity(dbm_v[i])
        || !is_plus_infinity(dbm[i][var_space_dim])) {
      return true;
    }
  }

  // `var' is not syntactically constrained:
  // now force an emptiness check.
  return is_empty();
}

template <typename T>
void
BD_Shape<T>
::compute_predecessors(std::vector<dimension_type>& predecessor) const {
  PPL_ASSERT(!marked_empty() && marked_shortest_path_closed());
  PPL_ASSERT(predecessor.size() == 0);
  // Variables are ordered according to their index.
  // The vector `predecessor' is used to indicate which variable
  // immediately precedes a given one in the corresponding equivalence class.
  // The `leader' of an equivalence class is the element having minimum
  // index: leaders are their own predecessors.
  const dimension_type predecessor_size = dbm.num_rows();
  // Initially, each variable is leader of its own zero-equivalence class.
  predecessor.reserve(predecessor_size);
  for (dimension_type i = 0; i < predecessor_size; ++i) {
    predecessor.push_back(i);
  }
  // Now compute actual predecessors.
  for (dimension_type i = predecessor_size; i-- > 1; ) {
    if (i == predecessor[i]) {
      const DB_Row<N>& dbm_i = dbm[i];
      for (dimension_type j = i; j-- > 0; ) {
        if (j == predecessor[j]
            && is_additive_inverse(dbm[j][i], dbm_i[j])) {
          // Choose as predecessor the variable having the smaller index.
          predecessor[i] = j;
          break;
        }
      }
    }
  }
}

template <typename T>
void
BD_Shape<T>::compute_leaders(std::vector<dimension_type>& leaders) const {
  PPL_ASSERT(!marked_empty() && marked_shortest_path_closed());
  PPL_ASSERT(leaders.size() == 0);
  // Compute predecessor information.
  compute_predecessors(leaders);
  // Flatten the predecessor chains so as to obtain leaders.
  PPL_ASSERT(leaders[0] == 0);
  for (dimension_type i = 1, l_size = leaders.size(); i != l_size; ++i) {
    const dimension_type leaders_i = leaders[i];
    PPL_ASSERT(leaders_i <= i);
    if (leaders_i != i) {
      const dimension_type leaders_leaders_i = leaders[leaders_i];
      PPL_ASSERT(leaders_leaders_i == leaders[leaders_leaders_i]);
      leaders[i] = leaders_leaders_i;
    }
  }
}

template <typename T>
bool
BD_Shape<T>::is_shortest_path_reduced() const {
  // If the BDS is empty, it is also reduced.
  if (marked_empty()) {
    return true;
  }
  const dimension_type space_dim = space_dimension();
  // Zero-dimensional BDSs are necessarily reduced.
  if (space_dim == 0) {
    return true;
  }
  // A shortest-path reduced dbm is just a dbm with an indication of
  // those constraints that are redundant. If there is no indication
  // of the redundant constraints, then it cannot be reduced.
  if (!marked_shortest_path_reduced()) {
    return false;
  }

  const BD_Shape x_copy = *this;
  x_copy.shortest_path_closure_assign();
  // If we just discovered emptiness, it cannot be reduced.
  if (x_copy.marked_empty()) {
    return false;
  }
  // The vector `leader' is used to indicate which variables are equivalent.
  std::vector<dimension_type> leader(space_dim + 1);

  // We store the leader.
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    leader[i] = i;
  }
  // Step 1: we store really the leader with the corrected value.
  // We search for the equivalent or zero-equivalent variables.
  // The variable(i-1) and variable(j-1) are equivalent if and only if
  // m_i_j == -(m_j_i).
  for (dimension_type i = 0; i < space_dim; ++i) {
    const DB_Row<N>& x_copy_dbm_i = x_copy.dbm[i];
    for (dimension_type j = i + 1; j <= space_dim; ++j) {
      if (is_additive_inverse(x_copy.dbm[j][i], x_copy_dbm_i[j])) {
        // Two equivalent variables have got the same leader
        // (the smaller variable).
        leader[j] = leader[i];
      }
    }
  }

  // Step 2: we check if there are redundant constraints in the zero_cycle
  // free bounded difference shape, considering only the leaders.
  // A constraint `c' is redundant, when there are two constraints such that
  // their sum is the same constraint with the inhomogeneous term
  // less than or equal to the `c' one.
  PPL_DIRTY_TEMP(N, c);
  for (dimension_type k = 0; k <= space_dim; ++k) {
    if (leader[k] == k) {
      const DB_Row<N>& x_k = x_copy.dbm[k];
      for (dimension_type i = 0; i <= space_dim; ++i) {
        if (leader[i] == i) {
          const DB_Row<N>& x_i = x_copy.dbm[i];
          const Bit_Row& redundancy_i = redundancy_dbm[i];
          const N& x_i_k = x_i[k];
          for (dimension_type j = 0; j <= space_dim; ++j) {
            if (leader[j] == j) {
              const N& x_i_j = x_i[j];
              if (!is_plus_infinity(x_i_j)) {
                add_assign_r(c, x_i_k, x_k[j], ROUND_UP);
                if (x_i_j >= c && !redundancy_i[j]) {
                  return false;
                }
              }
            }
          }
        }
      }
    }
  }

  // The vector `var_conn' is used to check if there is a single cycle
  // that connected all zero-equivalent variables between them.
  // The value `space_dim + 1' is used to indicate that the equivalence
  // class contains a single variable.
  std::vector<dimension_type> var_conn(space_dim + 1);
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    var_conn[i] = space_dim + 1;
  }
  // Step 3: we store really the `var_conn' with the right value, putting
  // the variable with the selected variable is connected:
  // we check the row of each variable:
  // a- each leader could be connected with only zero-equivalent one,
  // b- each non-leader with only another zero-equivalent one.
  for (dimension_type i = 0; i <= space_dim; ++i) {
    // It count with how many variables the selected variable is
    // connected.
    dimension_type t = 0;
    dimension_type leader_i = leader[i];
    // Case a: leader.
    if (leader_i == i) {
      for (dimension_type j = 0; j <= space_dim; ++j) {
        dimension_type leader_j = leader[j];
        // Only the connectedness with equivalent variables
        // is considered.
        if (j != leader_j) {
          if (!redundancy_dbm[i][j]) {
            if (t == 1) {
              // Two non-leaders cannot be connected with the same leader.
              return false;
            }
            else {
              if (leader_j != i) {
                // The variables are not in the same equivalence class.
                return false;
              }
              else {
                ++t;
                var_conn[i] = j;
              }
            }
          }
        }
      }
    }
    // Case b: non-leader.
    else {
      for (dimension_type j = 0; j <= space_dim; ++j) {
        if (!redundancy_dbm[i][j]) {
          dimension_type leader_j = leader[j];
          if (leader_i != leader_j) {
            // The variables are not in the same equivalence class.
            return false;
          }
          else {
            if (t == 1) {
              // The variables cannot be connected with the same leader.
              return false;
            }
            else {
              ++t;
              var_conn[i] = j;
            }
          }
          // A non-leader must be connected with
          // another variable.
          if (t == 0) {
            return false;
          }
        }
      }
    }
  }

  // The vector `just_checked' is used to check if
  // a variable is already checked.
  std::vector<bool> just_checked(space_dim + 1);
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    just_checked[i] = false;
  }
  // Step 4: we check if there are single cycles that
  // connected all the zero-equivalent variables between them.
  for (dimension_type i = 0; i <= space_dim; ++i) {
    // We do not re-check the already considered single cycles.
    if (!just_checked[i]) {
      dimension_type v_con = var_conn[i];
      // We consider only the equivalence classes with
      // 2 or plus variables.
      if (v_con != space_dim + 1) {
        // There is a single cycle if taken a variable,
        // we return to this same variable.
        while (v_con != i) {
          just_checked[v_con] = true;
          v_con = var_conn[v_con];
          // If we re-pass to an already considered variable,
          // then we haven't a single cycle.
          if (just_checked[v_con]) {
            return false;
          }
        }
      }
    }
    just_checked[i] = true;
  }

  // The system bounded differences is just reduced.
  return true;
}

template <typename T>
bool
BD_Shape<T>::bounds(const Linear_Expression& expr,
                    const bool from_above) const {
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type expr_space_dim = expr.space_dimension();
  const dimension_type space_dim = space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible((from_above
                                  ? "bounds_from_above(e)"
                                  : "bounds_from_below(e)"), "e", expr);
  }
  shortest_path_closure_assign();
  // A zero-dimensional or empty BDS bounds everything.
  if (space_dim == 0 || marked_empty()) {
    return true;
  }
  // The constraint `c' is used to check if `expr' is a difference
  // bounded and, in this case, to select the cell.
  const Constraint& c = from_above ? expr <= 0 : expr >= 0;
  dimension_type num_vars = 0;
  dimension_type i = 0;
  dimension_type j = 0;
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  // Check if `c' is a BD constraint.
  if (BD_Shape_Helpers::extract_bounded_difference(c, num_vars, i, j, coeff)) {
    if (num_vars == 0) {
      // Dealing with a trivial constraint.
      return true;
    }
    // Select the cell to be checked.
    const N& x = (coeff < 0) ? dbm[i][j] : dbm[j][i];
    return !is_plus_infinity(x);
  }
  else {
    // Not a DB constraint: use the MIP solver.
    Optimization_Mode mode_bounds
      = from_above ? MAXIMIZATION : MINIMIZATION;
    MIP_Problem mip(space_dim, constraints(), expr, mode_bounds);
    // Problem is known to be feasible.
    return mip.solve() == OPTIMIZED_MIP_PROBLEM;
  }
}

template <typename T>
bool
BD_Shape<T>::max_min(const Linear_Expression& expr,
                     const bool maximize,
                     Coefficient& ext_n, Coefficient& ext_d,
                     bool& included) const {
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type space_dim = space_dimension();
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible((maximize
                                  ? "maximize(e, ...)"
                                  : "minimize(e, ...)"), "e", expr);
  }
  // Deal with zero-dim BDS first.
  if (space_dim == 0) {
    if (marked_empty()) {
      return false;
    }
    else {
      ext_n = expr.inhomogeneous_term();
      ext_d = 1;
      included = true;
      return true;
    }
  }

  shortest_path_closure_assign();
  // For an empty BDS we simply return false.
  if (marked_empty()) {
    return false;
  }
  // The constraint `c' is used to check if `expr' is a difference
  // bounded and, in this case, to select the cell.
  const Constraint& c = maximize ? expr <= 0 : expr >= 0;
  dimension_type num_vars = 0;
  dimension_type i = 0;
  dimension_type j = 0;
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  // Check if `c' is a BD constraint.
  if (!BD_Shape_Helpers::extract_bounded_difference(c, num_vars, i, j, coeff)) {
    Optimization_Mode mode_max_min
      = maximize ? MAXIMIZATION : MINIMIZATION;
    MIP_Problem mip(space_dim, constraints(), expr, mode_max_min);
    if (mip.solve() == OPTIMIZED_MIP_PROBLEM) {
      mip.optimal_value(ext_n, ext_d);
      included = true;
      return true;
    }
    else {
      // Here`expr' is unbounded in `*this'.
      return false;
    }
  }
  else {
    // Here `expr' is a bounded difference.
    if (num_vars == 0) {
      // Dealing with a trivial expression.
      ext_n = expr.inhomogeneous_term();
      ext_d = 1;
      included = true;
      return true;
    }

    // Select the cell to be checked.
    const N& x = (coeff < 0) ? dbm[i][j] : dbm[j][i];
    if (!is_plus_infinity(x)) {
      // Compute the maximize/minimize of `expr'.
      PPL_DIRTY_TEMP(N, d);
      const Coefficient& b = expr.inhomogeneous_term();
      PPL_DIRTY_TEMP_COEFFICIENT(minus_b);
      neg_assign(minus_b, b);
      const Coefficient& sc_b = maximize ? b : minus_b;
      assign_r(d, sc_b, ROUND_UP);
      // Set `coeff_expr' to the absolute value of coefficient of
      // a variable in `expr'.
      PPL_DIRTY_TEMP(N, coeff_expr);
      PPL_ASSERT(i != 0);
      const Coefficient& coeff_i = expr.get(Variable(i - 1));
      const int sign_i = sgn(coeff_i);
      if (sign_i > 0) {
        assign_r(coeff_expr, coeff_i, ROUND_UP);
      }
      else {
        PPL_DIRTY_TEMP_COEFFICIENT(minus_coeff_i);
        neg_assign(minus_coeff_i, coeff_i);
        assign_r(coeff_expr, minus_coeff_i, ROUND_UP);
      }
      // Approximating the maximum/minimum of `expr'.
      add_mul_assign_r(d, coeff_expr, x, ROUND_UP);
      numer_denom(d, ext_n, ext_d);
      if (!maximize) {
        neg_assign(ext_n);
      }
      included = true;
      return true;
    }

    // `expr' is unbounded.
    return false;
  }
}

template <typename T>
bool
BD_Shape<T>::max_min(const Linear_Expression& expr,
                     const bool maximize,
                     Coefficient& ext_n, Coefficient& ext_d,
                     bool& included,
                     Generator& g) const {
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type space_dim = space_dimension();
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible((maximize
                                  ? "maximize(e, ...)"
                                  : "minimize(e, ...)"), "e", expr);
  }
  // Deal with zero-dim BDS first.
  if (space_dim == 0) {
    if (marked_empty()) {
      return false;
    }
    else {
      ext_n = expr.inhomogeneous_term();
      ext_d = 1;
      included = true;
      g = point();
      return true;
    }
  }

  shortest_path_closure_assign();
  // For an empty BDS we simply return false.
  if (marked_empty()) {
    return false;
  }
  Optimization_Mode mode_max_min
    = maximize ? MAXIMIZATION : MINIMIZATION;
  MIP_Problem mip(space_dim, constraints(), expr, mode_max_min);
  if (mip.solve() == OPTIMIZED_MIP_PROBLEM) {
    g = mip.optimizing_point();
    mip.evaluate_objective_function(g, ext_n, ext_d);
    included = true;
    return true;
  }
  // Here `expr' is unbounded in `*this'.
  return false;
}

template <typename T>
Poly_Con_Relation
BD_Shape<T>::relation_with(const Congruence& cg) const {
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (cg.space_dimension() > space_dim) {
    throw_dimension_incompatible("relation_with(cg)", cg);
  }
  // If the congruence is an equality, find the relation with
  // the equivalent equality constraint.
  if (cg.is_equality()) {
    Constraint c(cg);
    return relation_with(c);
  }

  shortest_path_closure_assign();

  if (marked_empty()) {
    return Poly_Con_Relation::saturates()
      && Poly_Con_Relation::is_included()
      && Poly_Con_Relation::is_disjoint();
  }
  if (space_dim == 0) {
    if (cg.is_inconsistent()) {
      return Poly_Con_Relation::is_disjoint();
    }
    else {
      return Poly_Con_Relation::saturates()
        && Poly_Con_Relation::is_included();
    }
  }

  // Find the lower bound for a hyperplane with direction
  // defined by the congruence.
  Linear_Expression le = Linear_Expression(cg.expression());
  PPL_DIRTY_TEMP_COEFFICIENT(min_numer);
  PPL_DIRTY_TEMP_COEFFICIENT(min_denom);
  bool min_included;
  bool bounded_below = minimize(le, min_numer, min_denom, min_included);

  // If there is no lower bound, then some of the hyperplanes defined by
  // the congruence will strictly intersect the shape.
  if (!bounded_below) {
    return Poly_Con_Relation::strictly_intersects();
  }
  // TODO: Consider adding a max_and_min() method, performing both
  // maximization and minimization so as to possibly exploit
  // incrementality of the MIP solver.

  // Find the upper bound for a hyperplane with direction
  // defined by the congruence.
  PPL_DIRTY_TEMP_COEFFICIENT(max_numer);
  PPL_DIRTY_TEMP_COEFFICIENT(max_denom);
  bool max_included;
  bool bounded_above = maximize(le, max_numer, max_denom, max_included);

  // If there is no upper bound, then some of the hyperplanes defined by
  // the congruence will strictly intersect the shape.
  if (!bounded_above) {
    return Poly_Con_Relation::strictly_intersects();
  }
  PPL_DIRTY_TEMP_COEFFICIENT(signed_distance);

  // Find the position value for the hyperplane that satisfies the congruence
  // and is above the lower bound for the shape.
  PPL_DIRTY_TEMP_COEFFICIENT(min_value);
  min_value = min_numer / min_denom;
  const Coefficient& modulus = cg.modulus();
  signed_distance = min_value % modulus;
  min_value -= signed_distance;
  if (min_value * min_denom < min_numer) {
    min_value += modulus;
  }
  // Find the position value for the hyperplane that satisfies the congruence
  // and is below the upper bound for the shape.
  PPL_DIRTY_TEMP_COEFFICIENT(max_value);
  max_value = max_numer / max_denom;
  signed_distance = max_value % modulus;
  max_value += signed_distance;
  if (max_value * max_denom > max_numer) {
    max_value -= modulus;
  }
  // If the upper bound value is less than the lower bound value,
  // then there is an empty intersection with the congruence;
  // otherwise it will strictly intersect.
  if (max_value < min_value) {
    return Poly_Con_Relation::is_disjoint();
  }
  else {
    return Poly_Con_Relation::strictly_intersects();
  }
}


template <typename T>
Poly_Con_Relation
BD_Shape<T>::relation_with(const Constraint& c) const {
  const dimension_type c_space_dim = c.space_dimension();
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (c_space_dim > space_dim) {
    throw_dimension_incompatible("relation_with(c)", c);
  }
  shortest_path_closure_assign();

  if (marked_empty()) {
    return Poly_Con_Relation::saturates()
      && Poly_Con_Relation::is_included()
      && Poly_Con_Relation::is_disjoint();
  }
  if (space_dim == 0) {
    if ((c.is_equality() && c.inhomogeneous_term() != 0)
        || (c.is_inequality() && c.inhomogeneous_term() < 0)) {
      return Poly_Con_Relation::is_disjoint();
    }
    else if (c.is_strict_inequality() && c.inhomogeneous_term() == 0) {
      // The constraint 0 > 0 implicitly defines the hyperplane 0 = 0;
      // thus, the zero-dimensional point also saturates it.
      return Poly_Con_Relation::saturates()
        && Poly_Con_Relation::is_disjoint();
    }
    else if (c.is_equality() || c.inhomogeneous_term() == 0) {
      return Poly_Con_Relation::saturates()
        && Poly_Con_Relation::is_included();
    }
    else {
      // The zero-dimensional point saturates
      // neither the positivity constraint 1 >= 0,
      // nor the strict positivity constraint 1 > 0.
      return Poly_Con_Relation::is_included();
    }
  }

  dimension_type num_vars = 0;
  dimension_type i = 0;
  dimension_type j = 0;
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  if (!BD_Shape_Helpers::extract_bounded_difference(c, num_vars, i, j, coeff)) {
    // Constraints that are not bounded differences.
    // Use maximize() and minimize() to do much of the work.

    // Find the linear expression for the constraint and use that to
    // find if the expression is bounded from above or below and if it
    // is, find the maximum and minimum values.
    Linear_Expression le(c.expression());
    le.set_inhomogeneous_term(Coefficient_zero());

    PPL_DIRTY_TEMP_COEFFICIENT(max_numer);
    PPL_DIRTY_TEMP_COEFFICIENT(max_denom);
    bool max_included;
    PPL_DIRTY_TEMP_COEFFICIENT(min_numer);
    PPL_DIRTY_TEMP_COEFFICIENT(min_denom);
    bool min_included;
    bool bounded_above = maximize(le, max_numer, max_denom, max_included);
    bool bounded_below = minimize(le, min_numer, min_denom, min_included);
    if (!bounded_above) {
      if (!bounded_below) {
        return Poly_Con_Relation::strictly_intersects();
      }
      min_numer += c.inhomogeneous_term() * min_denom;
      switch (sgn(min_numer)) {
      case 1:
        if (c.is_equality()) {
          return Poly_Con_Relation::is_disjoint();
        }
        return Poly_Con_Relation::is_included();
      case 0:
        if (c.is_strict_inequality() || c.is_equality()) {
          return Poly_Con_Relation::strictly_intersects();
        }
        return Poly_Con_Relation::is_included();
      case -1:
        return Poly_Con_Relation::strictly_intersects();
      }
    }
    if (!bounded_below) {
      max_numer += c.inhomogeneous_term() * max_denom;
      switch (sgn(max_numer)) {
      case 1:
        return Poly_Con_Relation::strictly_intersects();
      case 0:
        if (c.is_strict_inequality()) {
          return Poly_Con_Relation::is_disjoint();
        }
        return Poly_Con_Relation::strictly_intersects();
      case -1:
        return Poly_Con_Relation::is_disjoint();
      }
    }
    else {
      max_numer += c.inhomogeneous_term() * max_denom;
      min_numer += c.inhomogeneous_term() * min_denom;
      switch (sgn(max_numer)) {
      case 1:
        switch (sgn(min_numer)) {
        case 1:
          if (c.is_equality()) {
            return Poly_Con_Relation::is_disjoint();
          }
          return Poly_Con_Relation::is_included();
        case 0:
          if (c.is_equality()) {
            return Poly_Con_Relation::strictly_intersects();
          }
          if (c.is_strict_inequality()) {
            return Poly_Con_Relation::strictly_intersects();
          }
          return Poly_Con_Relation::is_included();
        case -1:
          return Poly_Con_Relation::strictly_intersects();
        }
        PPL_UNREACHABLE;
        break;
      case 0:
        if (min_numer == 0) {
          if (c.is_strict_inequality()) {
            return Poly_Con_Relation::is_disjoint()
              && Poly_Con_Relation::saturates();
          }
          return Poly_Con_Relation::is_included()
            && Poly_Con_Relation::saturates();
        }
        if (c.is_strict_inequality()) {
          return Poly_Con_Relation::is_disjoint();
        }
        return Poly_Con_Relation::strictly_intersects();
      case -1:
        return Poly_Con_Relation::is_disjoint();
      }
    }
  }

  // Constraints that are bounded differences.
  if (num_vars == 0) {
    // Dealing with a trivial constraint.
    switch (sgn(c.inhomogeneous_term())) {
    case -1:
      return Poly_Con_Relation::is_disjoint();
    case 0:
      if (c.is_strict_inequality()) {
        return Poly_Con_Relation::saturates()
          && Poly_Con_Relation::is_disjoint();
      }
      else {
        return Poly_Con_Relation::saturates()
          && Poly_Con_Relation::is_included();
      }
    case 1:
      if (c.is_equality()) {
        return Poly_Con_Relation::is_disjoint();
      }
      else {
        return Poly_Con_Relation::is_included();
      }
    }
  }

  // Select the cell to be checked for the "<=" part of the constraint,
  // and set `coeff' to the absolute value of itself.
  const bool negative = (coeff < 0);
  const N& x = negative ? dbm[i][j] : dbm[j][i];
  const N& y = negative ? dbm[j][i] : dbm[i][j];
  if (negative) {
    neg_assign(coeff);
  }
  // Deduce the relation/s of the constraint `c' of the form
  // `coeff*v - coeff*u </<=/== c.inhomogeneous_term()'
  // with the respectively constraints in `*this'
  // `-y <= v - u <= x'.
  // Let `d == c.inhomogeneous_term()/coeff'
  // and `d1 == -c.inhomogeneous_term()/coeff'.
  // The following variables of mpq_class type are used to be precise
  // when the bds is defined by integer constraints.
  PPL_DIRTY_TEMP(mpq_class, q_x);
  PPL_DIRTY_TEMP(mpq_class, q_y);
  PPL_DIRTY_TEMP(mpq_class, d);
  PPL_DIRTY_TEMP(mpq_class, d1);
  PPL_DIRTY_TEMP(mpq_class, c_denom);
  PPL_DIRTY_TEMP(mpq_class, q_denom);
  assign_r(c_denom, coeff, ROUND_NOT_NEEDED);
  assign_r(d, c.inhomogeneous_term(), ROUND_NOT_NEEDED);
  neg_assign_r(d1, d, ROUND_NOT_NEEDED);
  div_assign_r(d, d, c_denom, ROUND_NOT_NEEDED);
  div_assign_r(d1, d1, c_denom, ROUND_NOT_NEEDED);

  if (is_plus_infinity(x)) {
    if (!is_plus_infinity(y)) {
      // `*this' is in the following form:
      // `-y <= v - u'.
      // In this case `*this' is disjoint from `c' if
      // `-y > d' (`-y >= d' if c is a strict equality), i.e. if
      // `y < d1' (`y <= d1' if c is a strict equality).
      PPL_DIRTY_TEMP_COEFFICIENT(numer);
      PPL_DIRTY_TEMP_COEFFICIENT(denom);
      numer_denom(y, numer, denom);
      assign_r(q_denom, denom, ROUND_NOT_NEEDED);
      assign_r(q_y, numer, ROUND_NOT_NEEDED);
      div_assign_r(q_y, q_y, q_denom, ROUND_NOT_NEEDED);
      if (q_y < d1) {
        return Poly_Con_Relation::is_disjoint();
      }
      if (q_y == d1 && c.is_strict_inequality()) {
        return Poly_Con_Relation::is_disjoint();
      }
    }

    // In all other cases `*this' intersects `c'.
    return Poly_Con_Relation::strictly_intersects();
  }

  // Here `x' is not plus-infinity.
  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);
  numer_denom(x, numer, denom);
  assign_r(q_denom, denom, ROUND_NOT_NEEDED);
  assign_r(q_x, numer, ROUND_NOT_NEEDED);
  div_assign_r(q_x, q_x, q_denom, ROUND_NOT_NEEDED);

  if (!is_plus_infinity(y)) {
    numer_denom(y, numer, denom);
    assign_r(q_denom, denom, ROUND_NOT_NEEDED);
    assign_r(q_y, numer, ROUND_NOT_NEEDED);
    div_assign_r(q_y, q_y, q_denom, ROUND_NOT_NEEDED);
    if (q_x == d && q_y == d1) {
      if (c.is_strict_inequality()) {
        return Poly_Con_Relation::saturates()
          && Poly_Con_Relation::is_disjoint();
      }
      else {
        return Poly_Con_Relation::saturates()
          && Poly_Con_Relation::is_included();
      }
    }
    // `*this' is disjoint from `c' when
    // `-y > d' (`-y >= d' if c is a strict equality), i.e. if
    // `y < d1' (`y <= d1' if c is a strict equality).
    if (q_y < d1) {
      return Poly_Con_Relation::is_disjoint();
    }
    if (q_y == d1 && c.is_strict_inequality()) {
      return Poly_Con_Relation::is_disjoint();
    }
  }

  // Here `y' can be also plus-infinity.
  // If `c' is an equality, `*this' is disjoint from `c' if
  // `x < d'.
  if (d > q_x) {
    if (c.is_equality()) {
      return Poly_Con_Relation::is_disjoint();
    }
    else {
      return Poly_Con_Relation::is_included();
    }
  }

  if (d == q_x && c.is_nonstrict_inequality()) {
    return Poly_Con_Relation::is_included();
  }
  // In all other cases `*this' intersects `c'.
  return Poly_Con_Relation::strictly_intersects();
}

template <typename T>
Poly_Gen_Relation
BD_Shape<T>::relation_with(const Generator& g) const {
  const dimension_type space_dim = space_dimension();
  const dimension_type g_space_dim = g.space_dimension();

  // Dimension-compatibility check.
  if (space_dim < g_space_dim) {
    throw_dimension_incompatible("relation_with(g)", g);
  }
  shortest_path_closure_assign();
  // The empty BDS cannot subsume a generator.
  if (marked_empty()) {
    return Poly_Gen_Relation::nothing();
  }
  // A universe BDS in a zero-dimensional space subsumes
  // all the generators of a zero-dimensional space.
  if (space_dim == 0) {
    return Poly_Gen_Relation::subsumes();
  }
  const bool is_line = g.is_line();
  const bool is_line_or_ray = g.is_line_or_ray();

  // The relation between the BDS and the given generator is obtained
  // checking if the generator satisfies all the constraints in the BDS.
  // To check if the generator satisfies all the constraints it's enough
  // studying the sign of the scalar product between the generator and
  // all the constraints in the BDS.

  // Allocation of temporaries done once and for all.
  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);
  PPL_DIRTY_TEMP_COEFFICIENT(product);
  // We find in `*this' all the constraints.
  // TODO: This loop can be optimized more, if needed.
  for (dimension_type i = 0; i <= space_dim; ++i) {
    const Coefficient& g_coeff_y = (i > g_space_dim || i == 0)
      ? Coefficient_zero() : g.coefficient(Variable(i-1));
    const DB_Row<N>& dbm_i = dbm[i];
    for (dimension_type j = i + 1; j <= space_dim; ++j) {
      const Coefficient& g_coeff_x = (j > g_space_dim)
        ? Coefficient_zero() : g.coefficient(Variable(j-1));
      const N& dbm_ij = dbm_i[j];
      const N& dbm_ji = dbm[j][i];
      if (is_additive_inverse(dbm_ji, dbm_ij)) {
        // We have one equality constraint: denom*x - denom*y = numer.
        // Compute the scalar product.
        numer_denom(dbm_ij, numer, denom);
        product = g_coeff_y;
        product -= g_coeff_x;
        product *= denom;
        if (!is_line_or_ray) {
          add_mul_assign(product, numer, g.divisor());
        }
        if (product != 0) {
          return Poly_Gen_Relation::nothing();
        }
      }
      else {
        // We have 0, 1 or 2 binary inequality constraint/s.
        if (!is_plus_infinity(dbm_ij)) {
          // We have the binary inequality constraint:
          // denom*x - denom*y <= numer.
          // Compute the scalar product.
          numer_denom(dbm_ij, numer, denom);
          product = g_coeff_y;
          product -= g_coeff_x;
          product *= denom;
          if (!is_line_or_ray) {
            add_mul_assign(product, numer, g.divisor());
          }
          if (is_line) {
            if (product != 0) {
              // Lines must saturate all constraints.
              return Poly_Gen_Relation::nothing();
            }
          }
          else {
            // `g' is either a ray, a point or a closure point.
            if (product < 0) {
              return Poly_Gen_Relation::nothing();
            }
          }
        }

        if (!is_plus_infinity(dbm_ji)) {
          // We have the binary inequality constraint: denom*y - denom*x <= b.
          // Compute the scalar product.
          numer_denom(dbm_ji, numer, denom);
          product = 0;
          add_mul_assign(product, denom, g_coeff_x);
          add_mul_assign(product, -denom, g_coeff_y);
          if (!is_line_or_ray) {
            add_mul_assign(product, numer, g.divisor());
          }
          if (is_line) {
            if (product != 0) {
              // Lines must saturate all constraints.
              return Poly_Gen_Relation::nothing();
            }
          }
          else {
            // `g' is either a ray, a point or a closure point.
            if (product < 0) {
              return Poly_Gen_Relation::nothing();
            }
          }
        }
      }
    }
  }

  // The generator satisfies all the constraints.
  return Poly_Gen_Relation::subsumes();
}

template <typename T>
void
BD_Shape<T>::shortest_path_closure_assign() const {
  // Do something only if necessary.
  if (marked_empty() || marked_shortest_path_closed()) {
    return;
  }
  const dimension_type num_dimensions = space_dimension();
  // Zero-dimensional BDSs are necessarily shortest-path closed.
  if (num_dimensions == 0) {
    return;
  }
  // Even though the BDS will not change, its internal representation
  // is going to be modified by the Floyd-Warshall algorithm.
  BD_Shape& x = const_cast<BD_Shape<T>&>(*this);

  // Fill the main diagonal with zeros.
  for (dimension_type h = num_dimensions + 1; h-- > 0; ) {
    PPL_ASSERT(is_plus_infinity(x.dbm[h][h]));
    assign_r(x.dbm[h][h], 0, ROUND_NOT_NEEDED);
  }

  PPL_DIRTY_TEMP(N, sum);
  for (dimension_type k = num_dimensions + 1; k-- > 0; ) {
    const DB_Row<N>& x_dbm_k = x.dbm[k];
    for (dimension_type i = num_dimensions + 1; i-- > 0; ) {
      DB_Row<N>& x_dbm_i = x.dbm[i];
      const N& x_dbm_i_k = x_dbm_i[k];
      if (!is_plus_infinity(x_dbm_i_k)) {
        for (dimension_type j = num_dimensions + 1; j-- > 0; ) {
          const N& x_dbm_k_j = x_dbm_k[j];
          if (!is_plus_infinity(x_dbm_k_j)) {
            // Rounding upward for correctness.
            add_assign_r(sum, x_dbm_i_k, x_dbm_k_j, ROUND_UP);
            min_assign(x_dbm_i[j], sum);
          }
        }
      }
    }
  }

  // Check for emptiness: the BDS is empty if and only if there is a
  // negative value on the main diagonal of `dbm'.
  for (dimension_type h = num_dimensions + 1; h-- > 0; ) {
    N& x_dbm_hh = x.dbm[h][h];
    if (sgn(x_dbm_hh) < 0) {
      x.set_empty();
      return;
    }
    else {
      PPL_ASSERT(sgn(x_dbm_hh) == 0);
      // Restore PLUS_INFINITY on the main diagonal.
      assign_r(x_dbm_hh, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
  }

  // The BDS is not empty and it is now shortest-path closed.
  x.set_shortest_path_closed();
}

template <typename T>
void
BD_Shape<T>::incremental_shortest_path_closure_assign(Variable var) const {
  // Do something only if necessary.
  if (marked_empty() || marked_shortest_path_closed()) {
    return;
  }
  const dimension_type num_dimensions = space_dimension();
  PPL_ASSERT(var.id() < num_dimensions);

  // Even though the BDS will not change, its internal representation
  // is going to be modified by the incremental Floyd-Warshall algorithm.
  BD_Shape& x = const_cast<BD_Shape&>(*this);

  // Fill the main diagonal with zeros.
  for (dimension_type h = num_dimensions + 1; h-- > 0; ) {
    PPL_ASSERT(is_plus_infinity(x.dbm[h][h]));
    assign_r(x.dbm[h][h], 0, ROUND_NOT_NEEDED);
  }

  // Using the incremental Floyd-Warshall algorithm.
  PPL_DIRTY_TEMP(N, sum);
  const dimension_type v = var.id() + 1;
  DB_Row<N>& x_v = x.dbm[v];
  // Step 1: Improve all constraints on variable `var'.
  for (dimension_type k = num_dimensions + 1; k-- > 0; ) {
    DB_Row<N>& x_k = x.dbm[k];
    const N& x_v_k = x_v[k];
    const N& x_k_v = x_k[v];
    const bool x_v_k_finite = !is_plus_infinity(x_v_k);
    const bool x_k_v_finite = !is_plus_infinity(x_k_v);
    // Specialize inner loop based on finiteness info.
    if (x_v_k_finite) {
      if (x_k_v_finite) {
        // Here both x_v_k and x_k_v are finite.
        for (dimension_type i = num_dimensions + 1; i-- > 0; ) {
          DB_Row<N>& x_i = x.dbm[i];
          const N& x_i_k = x_i[k];
          if (!is_plus_infinity(x_i_k)) {
            add_assign_r(sum, x_i_k, x_k_v, ROUND_UP);
            min_assign(x_i[v], sum);
          }
          const N& x_k_i = x_k[i];
          if (!is_plus_infinity(x_k_i)) {
            add_assign_r(sum, x_v_k, x_k_i, ROUND_UP);
            min_assign(x_v[i], sum);
          }
        }
      }
      else {
        // Here x_v_k is finite, but x_k_v is not.
        for (dimension_type i = num_dimensions + 1; i-- > 0; ) {
          const N& x_k_i = x_k[i];
          if (!is_plus_infinity(x_k_i)) {
            add_assign_r(sum, x_v_k, x_k_i, ROUND_UP);
            min_assign(x_v[i], sum);
          }
        }
      }
    }
    else if (x_k_v_finite) {
      // Here x_v_k is infinite, but x_k_v is finite.
      for (dimension_type i = num_dimensions + 1; i-- > 0; ) {
        DB_Row<N>& x_i = x.dbm[i];
        const N& x_i_k = x_i[k];
        if (!is_plus_infinity(x_i_k)) {
          add_assign_r(sum, x_i_k, x_k_v, ROUND_UP);
          min_assign(x_i[v], sum);
        }
      }
    }
    else {
      // Here both x_v_k and x_k_v are infinite.
      continue;
    }
  }

  // Step 2: improve the other bounds by using the precise bounds
  // for the constraints on `var'.
  for (dimension_type i = num_dimensions + 1; i-- > 0; ) {
    DB_Row<N>& x_i = x.dbm[i];
    const N& x_i_v = x_i[v];
    if (!is_plus_infinity(x_i_v)) {
      for (dimension_type j = num_dimensions + 1; j-- > 0; ) {
        const N& x_v_j = x_v[j];
        if (!is_plus_infinity(x_v_j)) {
          add_assign_r(sum, x_i_v, x_v_j, ROUND_UP);
          min_assign(x_i[j], sum);
        }
      }
    }
  }

  // Check for emptiness: the BDS is empty if and only if there is a
  // negative value on the main diagonal of `dbm'.
  for (dimension_type h = num_dimensions + 1; h-- > 0; ) {
    N& x_dbm_hh = x.dbm[h][h];
    if (sgn(x_dbm_hh) < 0) {
      x.set_empty();
      return;
    }
    else {
      PPL_ASSERT(sgn(x_dbm_hh) == 0);
      // Restore PLUS_INFINITY on the main diagonal.
      assign_r(x_dbm_hh, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
  }

  // The BDS is not empty and it is now shortest-path closed.
  x.set_shortest_path_closed();
}

template <typename T>
void
BD_Shape<T>::shortest_path_reduction_assign() const {
  // Do something only if necessary.
  if (marked_shortest_path_reduced()) {
    return;
  }
  const dimension_type space_dim = space_dimension();
  // Zero-dimensional BDSs are necessarily reduced.
  if (space_dim == 0) {
    return;
  }
  // First find the tightest constraints for this BDS.
  shortest_path_closure_assign();

  // If `*this' is empty, then there is nothing to reduce.
  if (marked_empty()) {
    return;
  }
  // Step 1: compute zero-equivalence classes.
  // Variables corresponding to indices `i' and `j' are zero-equivalent
  // if they lie on a zero-weight loop; since the matrix is shortest-path
  // closed, this happens if and only if dbm[i][j] == -dbm[j][i].
  std::vector<dimension_type> predecessor;
  compute_predecessors(predecessor);
  std::vector<dimension_type> leaders;
  compute_leader_indices(predecessor, leaders);
  const dimension_type num_leaders = leaders.size();

  Bit_Matrix redundancy(space_dim + 1, space_dim + 1);
  // Init all constraints to be redundant.
  // TODO: provide an appropriate method to set multiple bits.
  Bit_Row& red_0 = redundancy[0];
  for (dimension_type j = space_dim + 1; j-- > 0; ) {
    red_0.set(j);
  }
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    redundancy[i] = red_0;
  }
  // Step 2: flag non-redundant constraints in the (zero-cycle-free)
  // subsystem of bounded differences having only leaders as variables.
  PPL_DIRTY_TEMP(N, c);
  for (dimension_type l_i = 0; l_i < num_leaders; ++l_i) {
    const dimension_type i = leaders[l_i];
    const DB_Row<N>& dbm_i = dbm[i];
    Bit_Row& redundancy_i = redundancy[i];
    for (dimension_type l_j = 0; l_j < num_leaders; ++l_j) {
      const dimension_type j = leaders[l_j];
      if (redundancy_i[j]) {
        const N& dbm_i_j = dbm_i[j];
        redundancy_i.clear(j);
        for (dimension_type l_k = 0; l_k < num_leaders; ++l_k) {
          const dimension_type k = leaders[l_k];
          add_assign_r(c, dbm_i[k], dbm[k][j], ROUND_UP);
          if (dbm_i_j >= c) {
            redundancy_i.set(j);
            break;
          }
        }
      }
    }
  }

  // Step 3: flag non-redundant constraints in zero-equivalence classes.
  // Each equivalence class must have a single 0-cycle connecting
  // all the equivalent variables in increasing order.
  std::deque<bool> dealt_with(space_dim + 1, false);
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    // We only need to deal with non-singleton zero-equivalence classes
    // that haven't already been dealt with.
    if (i != predecessor[i] && !dealt_with[i]) {
      dimension_type j = i;
      while (true) {
        const dimension_type predecessor_j = predecessor[j];
        if (j == predecessor_j) {
          // We finally found the leader of `i'.
          PPL_ASSERT(redundancy[i][j]);
          redundancy[i].clear(j);
          // Here we dealt with `j' (i.e., `predecessor_j'), but it is useless
          // to update `dealt_with' because `j' is a leader.
          break;
        }
        // We haven't found the leader of `i' yet.
        PPL_ASSERT(redundancy[predecessor_j][j]);
        redundancy[predecessor_j].clear(j);
        dealt_with[predecessor_j] = true;
        j = predecessor_j;
      }
    }
  }
  // Even though shortest-path reduction is not going to change the BDS,
  // it might change its internal representation.
  BD_Shape<T>& x = const_cast<BD_Shape<T>&>(*this);
  using std::swap;
  swap(x.redundancy_dbm, redundancy);
  x.set_shortest_path_reduced();

  PPL_ASSERT(is_shortest_path_reduced());
}

template <typename T>
void
BD_Shape<T>::upper_bound_assign(const BD_Shape& y) {
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("upper_bound_assign(y)", y);
  }
  // The upper bound of a BD shape `bd' with an empty shape is `bd'.
  y.shortest_path_closure_assign();
  if (y.marked_empty()) {
    return;
  }
  shortest_path_closure_assign();
  if (marked_empty()) {
    *this = y;
    return;
  }

  // The bds-hull consists in constructing `*this' with the maximum
  // elements selected from `*this' and `y'.
  PPL_ASSERT(space_dim == 0 || marked_shortest_path_closed());
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    DB_Row<N>& dbm_i = dbm[i];
    const DB_Row<N>& y_dbm_i = y.dbm[i];
    for (dimension_type j = space_dim + 1; j-- > 0; ) {
      N& dbm_ij = dbm_i[j];
      const N& y_dbm_ij = y_dbm_i[j];
      if (dbm_ij < y_dbm_ij) {
        dbm_ij = y_dbm_ij;
      }
    }
  }
  // Shortest-path closure is maintained (if it was holding).
  // TODO: see whether reduction can be (efficiently!) maintained too.
  if (marked_shortest_path_reduced()) {
    reset_shortest_path_reduced();
  }
  PPL_ASSERT(OK());
}

template <typename T>
bool
BD_Shape<T>::BFT00_upper_bound_assign_if_exact(const BD_Shape& y) {
  // Declare a const reference to *this (to avoid accidental modifications).
  const BD_Shape& x = *this;
  const dimension_type x_space_dim = x.space_dimension();

  // Private method: the caller must ensure the following.
  PPL_ASSERT(x_space_dim == y.space_dimension());

  // The zero-dim case is trivial.
  if (x_space_dim == 0) {
    upper_bound_assign(y);
    return true;
  }
  // If `x' or `y' is (known to be) empty, the upper bound is exact.
  if (x.marked_empty()) {
    *this = y;
    return true;
  }
  else if (y.is_empty()) {
    return true;
  }
  else if (x.is_empty()) {
    *this = y;
    return true;
  }

  // Here both `x' and `y' are known to be non-empty.
  // Implementation based on Algorithm 4.1 (page 6) in [BemporadFT00TR],
  // tailored to the special case of BD shapes.

  Variable epsilon(x_space_dim);
  Linear_Expression zero_expr;
  zero_expr.set_space_dimension(x_space_dim + 1);
  Linear_Expression db_expr;
  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);

  // Step 1: compute the constraint system for the envelope env(x,y)
  // and put into x_cs_removed and y_cs_removed those non-redundant
  // constraints that are not in the constraint system for env(x,y).
  // While at it, also add the additional space dimension (epsilon).
  Constraint_System env_cs;
  Constraint_System x_cs_removed;
  Constraint_System y_cs_removed;
  x.shortest_path_reduction_assign();
  y.shortest_path_reduction_assign();
  for (dimension_type i = x_space_dim + 1; i-- > 0; ) {
    const Bit_Row& x_red_i = x.redundancy_dbm[i];
    const Bit_Row& y_red_i = y.redundancy_dbm[i];
    const DB_Row<N>& x_dbm_i = x.dbm[i];
    const DB_Row<N>& y_dbm_i = y.dbm[i];
    for (dimension_type j = x_space_dim + 1; j-- > 0; ) {
      if (x_red_i[j] && y_red_i[j]) {
        continue;
      }
      if (!x_red_i[j]) {
        const N& x_dbm_ij = x_dbm_i[j];
        PPL_ASSERT(!is_plus_infinity(x_dbm_ij));
        numer_denom(x_dbm_ij, numer, denom);
        // Build skeleton DB constraint (having the right space dimension).
        db_expr = zero_expr;
        if (i > 0) {
          db_expr += Variable(i-1);
        }
        if (j > 0) {
          db_expr -= Variable(j-1);
        }
        if (denom != 1) {
          db_expr *= denom;
        }
        db_expr += numer;
        if (x_dbm_ij >= y_dbm_i[j]) {
          env_cs.insert(db_expr >= 0);
        }
        else {
          db_expr += epsilon;
          x_cs_removed.insert(db_expr == 0);
        }
      }
      if (!y_red_i[j]) {
        const N& y_dbm_ij = y_dbm_i[j];
        const N& x_dbm_ij = x_dbm_i[j];
        PPL_ASSERT(!is_plus_infinity(y_dbm_ij));
        numer_denom(y_dbm_ij, numer, denom);
        // Build skeleton DB constraint (having the right space dimension).
        db_expr = zero_expr;
        if (i > 0) {
          db_expr += Variable(i-1);
        }
        if (j > 0) {
          db_expr -= Variable(j-1);
        }
        if (denom != 1) {
          db_expr *= denom;
        }
        db_expr += numer;
        if (y_dbm_ij >= x_dbm_ij) {
          // Check if same constraint was added when considering x_dbm_ij.
          if (!x_red_i[j] && x_dbm_ij == y_dbm_ij) {
            continue;
          }
          env_cs.insert(db_expr >= 0);
        }
        else {
          db_expr += epsilon;
          y_cs_removed.insert(db_expr == 0);
        }
      }
    }
  }

  if (x_cs_removed.empty()) {
    // No constraint of x was removed: y is included in x.
    return true;
  }
  if (y_cs_removed.empty()) {
    // No constraint of y was removed: x is included in y.
    *this = y;
    return true;
  }

  // In preparation to Step 4: build the common part of LP problems,
  // i.e., the constraints corresponding to env(x,y),
  // where the additional space dimension (epsilon) has to be maximized.
  MIP_Problem env_lp(x_space_dim + 1, env_cs, epsilon, MAXIMIZATION);
  // Pre-solve `env_lp' to later exploit incrementality.
  env_lp.solve();
  PPL_ASSERT(env_lp.solve() != UNFEASIBLE_MIP_PROBLEM);

  // Implementing loop in Steps 3-6.
  for (Constraint_System::const_iterator i = x_cs_removed.begin(),
         i_end = x_cs_removed.end(); i != i_end; ++i) {
    MIP_Problem lp_i(env_lp);
    lp_i.add_constraint(*i);
    // Pre-solve to exploit incrementality.
    if (lp_i.solve() == UNFEASIBLE_MIP_PROBLEM) {
      continue;
    }
    for (Constraint_System::const_iterator j = y_cs_removed.begin(),
           j_end = y_cs_removed.end(); j != j_end; ++j) {
      MIP_Problem lp_ij(lp_i);
      lp_ij.add_constraint(*j);
      // Solve and check for a positive optimal value.
      switch (lp_ij.solve()) {
      case UNFEASIBLE_MIP_PROBLEM:
        // CHECKME: is the following actually impossible?
        PPL_UNREACHABLE;
        return false;
      case UNBOUNDED_MIP_PROBLEM:
        return false;
      case OPTIMIZED_MIP_PROBLEM:
        lp_ij.optimal_value(numer, denom);
        if (numer > 0) {
          return false;
        }
        break;
      }
    }
  }

  // The upper bound of x and y is indeed exact.
  upper_bound_assign(y);
  PPL_ASSERT(OK());
  return true;
}

template <typename T>
template <bool integer_upper_bound>
bool
BD_Shape<T>::BHZ09_upper_bound_assign_if_exact(const BD_Shape& y) {
  PPL_COMPILE_TIME_CHECK(!integer_upper_bound
                         || std::numeric_limits<T>::is_integer,
                         "BD_Shape<T>::BHZ09_upper_bound_assign_if_exact(y):"
                         " instantiating for integer upper bound,"
                         " but T in not an integer datatype.");

  // FIXME, CHECKME: what about inexact computations?
  // Declare a const reference to *this (to avoid accidental modifications).
  const BD_Shape& x = *this;
  const dimension_type x_space_dim = x.space_dimension();

  // Private method: the caller must ensure the following.
  PPL_ASSERT(x_space_dim == y.space_dimension());

  // The zero-dim case is trivial.
  if (x_space_dim == 0) {
    upper_bound_assign(y);
    return true;
  }
  // If `x' or `y' is (known to be) empty, the upper bound is exact.
  if (x.marked_empty()) {
    *this = y;
    return true;
  }
  else if (y.is_empty()) {
    return true;
  }
  else if (x.is_empty()) {
    *this = y;
    return true;
  }

  // Here both `x' and `y' are known to be non-empty.
  x.shortest_path_reduction_assign();
  y.shortest_path_reduction_assign();
  PPL_ASSERT(x.marked_shortest_path_closed());
  PPL_ASSERT(y.marked_shortest_path_closed());
  // Pre-compute the upper bound of `x' and `y'.
  BD_Shape<T> ub(x);
  ub.upper_bound_assign(y);

  PPL_DIRTY_TEMP(N, lhs);
  PPL_DIRTY_TEMP(N, rhs);
  PPL_DIRTY_TEMP(N, temp_zero);
  assign_r(temp_zero, 0, ROUND_NOT_NEEDED);
  PPL_DIRTY_TEMP(N, temp_one);
  if (integer_upper_bound) {
    assign_r(temp_one, 1, ROUND_NOT_NEEDED);
  }
  for (dimension_type i = x_space_dim + 1; i-- > 0; ) {
    const DB_Row<N>& x_i = x.dbm[i];
    const Bit_Row& x_red_i = x.redundancy_dbm[i];
    const DB_Row<N>& y_i = y.dbm[i];
    const DB_Row<N>& ub_i = ub.dbm[i];
    for (dimension_type j = x_space_dim + 1; j-- > 0; ) {
      // Check redundancy of x_i_j.
      if (x_red_i[j]) {
        continue;
      }
      // By non-redundancy, we know that i != j.
      PPL_ASSERT(i != j);
      const N& x_i_j = x_i[j];
      if (x_i_j < y_i[j]) {
        for (dimension_type k = x_space_dim + 1; k-- > 0; ) {
          const DB_Row<N>& x_k = x.dbm[k];
          const DB_Row<N>& y_k = y.dbm[k];
          const Bit_Row& y_red_k = y.redundancy_dbm[k];
          const DB_Row<N>& ub_k = ub.dbm[k];
          const N& ub_k_j = (k == j) ? temp_zero : ub_k[j];
          for (dimension_type ell = x_space_dim + 1; ell-- > 0; ) {
            // Check redundancy of y_k_ell.
            if (y_red_k[ell]) {
              continue;
            }
            // By non-redundancy, we know that k != ell.
            PPL_ASSERT(k != ell);
            const N& y_k_ell = y_k[ell];
            if (y_k_ell < x_k[ell]) {
              // The first condition in BHZ09 theorem holds;
              // now check for the second condition.
              add_assign_r(lhs, x_i_j, y_k_ell, ROUND_UP);
              const N& ub_i_ell = (i == ell) ? temp_zero : ub_i[ell];
              add_assign_r(rhs, ub_i_ell, ub_k_j, ROUND_UP);
              if (integer_upper_bound) {
                // Note: adding 1 rather than 2 (as in Theorem 5.3)
                // so as to later test for < rather than <=.
                add_assign_r(lhs, lhs, temp_one, ROUND_NOT_NEEDED);
              }
              // Testing for < in both the rational and integer case.
              if (lhs < rhs) {
                return false;
              }
            }
          }
        }
      }
    }
  }
  // The upper bound of x and y is indeed exact.
  m_swap(ub);
  PPL_ASSERT(OK());
  return true;
}

template <typename T>
void
BD_Shape<T>::difference_assign(const BD_Shape& y) {
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("difference_assign(y)", y);
  }
  BD_Shape new_bd_shape(space_dim, EMPTY);

  BD_Shape& x = *this;

  x.shortest_path_closure_assign();
  // The difference of an empty bounded difference shape
  // and of a bounded difference shape `p' is empty.
  if (x.marked_empty()) {
    return;
  }
  y.shortest_path_closure_assign();
  // The difference of a bounded difference shape `p'
  // and an empty bounded difference shape is `p'.
  if (y.marked_empty()) {
    return;
  }
  // If both bounded difference shapes are zero-dimensional,
  // then at this point they are necessarily universe system of
  // bounded differences, so that their difference is empty.
  if (space_dim == 0) {
    x.set_empty();
    return;
  }

  // TODO: This is just an executable specification.
  //       Have to find a more efficient method.
  if (y.contains(x)) {
    x.set_empty();
    return;
  }

  // We take a constraint of the system y at the time and we
  // consider its complementary. Then we intersect the union
  // of these complementary constraints with the system x.
  const Constraint_System& y_cs = y.constraints();
  for (Constraint_System::const_iterator i = y_cs.begin(),
         y_cs_end = y_cs.end(); i != y_cs_end; ++i) {
    const Constraint& c = *i;
    // If the bounded difference shape `x' is included
    // in the bounded difference shape defined by `c',
    // then `c' _must_ be skipped, as adding its complement to `x'
    // would result in the empty bounded difference shape,
    // and as we would obtain a result that is less precise
    // than the bds-difference.
    if (x.relation_with(c).implies(Poly_Con_Relation::is_included())) {
      continue;
    }
    BD_Shape z = x;
    const Linear_Expression e(c.expression());
    z.add_constraint(e <= 0);
    if (!z.is_empty()) {
      new_bd_shape.upper_bound_assign(z);
    }
    if (c.is_equality()) {
      z = x;
      z.add_constraint(e >= 0);
      if (!z.is_empty()) {
        new_bd_shape.upper_bound_assign(z);
      }
    }
  }
  *this = new_bd_shape;
  PPL_ASSERT(OK());
}

template <typename T>
bool
BD_Shape<T>::simplify_using_context_assign(const BD_Shape& y) {
  BD_Shape& x = *this;
  const dimension_type dim = x.space_dimension();
  // Dimension-compatibility check.
  if (dim != y.space_dimension()) {
    throw_dimension_incompatible("simplify_using_context_assign(y)", y);
  }
  // Filter away the zero-dimensional case.
  if (dim == 0) {
    if (y.marked_empty()) {
      x.set_zero_dim_univ();
      return false;
    }
    else {
      return !x.marked_empty();
    }
  }

  // Filter away the case where `x' contains `y'
  // (this subsumes the case when `y' is empty).
  y.shortest_path_closure_assign();
  if (x.contains(y)) {
    BD_Shape<T> res(dim, UNIVERSE);
    x.m_swap(res);
    return false;
  }

  // Filter away the case where `x' is empty.
  x.shortest_path_closure_assign();
  if (x.marked_empty()) {
    // Search for a constraint of `y' that is not a tautology.
    dimension_type i;
    dimension_type j;
    // Prefer unary constraints.
    i = 0;
    const DB_Row<N>& y_dbm_0 = y.dbm[0];
    for (j = 1; j <= dim; ++j) {
      if (!is_plus_infinity(y_dbm_0[j])) {
        // FIXME: if N is a float or bounded integer type, then
        // we also need to check that we are actually able to construct
        // a constraint inconsistent with respect to this one.
        goto found;
      }
    }
    j = 0;
    for (i = 1; i <= dim; ++i) {
      if (!is_plus_infinity(y.dbm[i][0])) {
        // FIXME: if N is a float or bounded integer type, then
        // we also need to check that we are actually able to construct
        // a constraint inconsistent with respect to this one.
        goto found;
      }
    }
    // Then search binary constraints.
    for (i = 1; i <= dim; ++i) {
      const DB_Row<N>& y_dbm_i = y.dbm[i];
      for (j = 1; j <= dim; ++j) {
        if (!is_plus_infinity(y_dbm_i[j])) {
          // FIXME: if N is a float or bounded integer type, then
          // we also need to check that we are actually able to construct
          // a constraint inconsistent with respect to this one.
          goto found;
        }
      }
    }
    // Not found: we were not able to build a constraint contradicting
    // one of the constraints in `y': `x' cannot be enlarged.
    return false;

  found:
    // Found: build a new BDS contradicting the constraint found.
    PPL_ASSERT(i <= dim && j <= dim && (i > 0 || j > 0));
    BD_Shape<T> res(dim, UNIVERSE);
    PPL_DIRTY_TEMP(N, tmp);
    assign_r(tmp, 1, ROUND_UP);
    add_assign_r(tmp, tmp, y.dbm[i][j], ROUND_UP);
    PPL_ASSERT(!is_plus_infinity(tmp));
    // CHECKME: round down is really meant.
    neg_assign_r(res.dbm[j][i], tmp, ROUND_DOWN);
    x.m_swap(res);
    return false;
  }

  // Here `x' and `y' are not empty and shortest-path closed;
  // also, `x' does not contain `y'.
  // Let `target' be the intersection of `x' and `y'.
  BD_Shape<T> target = x;
  target.intersection_assign(y);
  const bool bool_result = !target.is_empty();

  // Compute a reduced dbm for `x' and ...
  x.shortest_path_reduction_assign();
  // ... count the non-redundant constraints.
  dimension_type x_num_non_redundant = (dim+1)*(dim+1);
  for (dimension_type i = dim + 1; i-- > 0; ) {
    x_num_non_redundant -= x.redundancy_dbm[i].count_ones();
  }
  PPL_ASSERT(x_num_non_redundant > 0);

  // Let `yy' be a copy of `y': we will keep adding to `yy'
  // the non-redundant constraints of `x',
  // stopping as soon as `yy' becomes equal to `target'.
  BD_Shape<T> yy = y;

  // The constraints added to `yy' will be recorded in `res' ...
  BD_Shape<T> res(dim, UNIVERSE);
  // ... and we will count them too.
  dimension_type res_num_non_redundant = 0;

  // Compute leader information for `x'.
  std::vector<dimension_type> x_leaders;
  x.compute_leaders(x_leaders);

  // First go through the unary equality constraints.
  const DB_Row<N>& x_dbm_0 = x.dbm[0];
  DB_Row<N>& yy_dbm_0 = yy.dbm[0];
  DB_Row<N>& res_dbm_0 = res.dbm[0];
  for (dimension_type j = 1; j <= dim; ++j) {
    // Unary equality constraints are encoded in entries dbm_0j and dbm_j0
    // provided index j has special variable index 0 as its leader.
    if (x_leaders[j] != 0) {
      continue;
    }
    PPL_ASSERT(!is_plus_infinity(x_dbm_0[j]));
    if (x_dbm_0[j] < yy_dbm_0[j]) {
      res_dbm_0[j] = x_dbm_0[j];
      ++res_num_non_redundant;
      // Tighten context `yy' using the newly added constraint.
      yy_dbm_0[j] = x_dbm_0[j];
      yy.reset_shortest_path_closed();
    }
    PPL_ASSERT(!is_plus_infinity(x.dbm[j][0]));
    if (x.dbm[j][0] < yy.dbm[j][0]) {
      res.dbm[j][0] = x.dbm[j][0];
      ++res_num_non_redundant;
      // Tighten context `yy' using the newly added constraint.
      yy.dbm[j][0] = x.dbm[j][0];
      yy.reset_shortest_path_closed();
    }
    // Restore shortest-path closure, if it was lost.
    if (!yy.marked_shortest_path_closed()) {
      Variable var_j(j-1);
      yy.incremental_shortest_path_closure_assign(var_j);
      if (target.contains(yy)) {
        // Target reached: swap `x' and `res' if needed.
        if (res_num_non_redundant < x_num_non_redundant) {
          res.reset_shortest_path_closed();
          x.m_swap(res);
        }
        return bool_result;
      }
    }
  }

  // Go through the binary equality constraints.
  // Note: no need to consider the case i == 1.
  for (dimension_type i = 2; i <= dim; ++i) {
    const dimension_type j = x_leaders[i];
    if (j == i || j == 0) {
      continue;
    }
    PPL_ASSERT(!is_plus_infinity(x.dbm[i][j]));
    if (x.dbm[i][j] < yy.dbm[i][j]) {
      res.dbm[i][j] = x.dbm[i][j];
      ++res_num_non_redundant;
      // Tighten context `yy' using the newly added constraint.
      yy.dbm[i][j] = x.dbm[i][j];
      yy.reset_shortest_path_closed();
    }
    PPL_ASSERT(!is_plus_infinity(x.dbm[j][i]));
    if (x.dbm[j][i] < yy.dbm[j][i]) {
      res.dbm[j][i] = x.dbm[j][i];
      ++res_num_non_redundant;
      // Tighten context `yy' using the newly added constraint.
      yy.dbm[j][i] = x.dbm[j][i];
      yy.reset_shortest_path_closed();
    }
    // Restore shortest-path closure, if it was lost.
    if (!yy.marked_shortest_path_closed()) {
      Variable var_j(j-1);
      yy.incremental_shortest_path_closure_assign(var_j);
      if (target.contains(yy)) {
        // Target reached: swap `x' and `res' if needed.
        if (res_num_non_redundant < x_num_non_redundant) {
          res.reset_shortest_path_closed();
          x.m_swap(res);
        }
        return bool_result;
      }
    }
  }

  // Finally go through the (proper) inequality constraints:
  // both indices i and j should be leaders.
  for (dimension_type i = 0; i <= dim; ++i) {
    if (i != x_leaders[i]) {
      continue;
    }
    const DB_Row<N>& x_dbm_i = x.dbm[i];
    const Bit_Row& x_redundancy_dbm_i = x.redundancy_dbm[i];
    DB_Row<N>& yy_dbm_i = yy.dbm[i];
    DB_Row<N>& res_dbm_i = res.dbm[i];
    for (dimension_type j = 0; j <= dim; ++j) {
      if (j != x_leaders[j] || x_redundancy_dbm_i[j]) {
        continue;
      }
      N& yy_dbm_ij = yy_dbm_i[j];
      const N& x_dbm_ij = x_dbm_i[j];
      if (x_dbm_ij < yy_dbm_ij) {
        res_dbm_i[j] = x_dbm_ij;
        ++res_num_non_redundant;
        // Tighten context `yy' using the newly added constraint.
        yy_dbm_ij = x_dbm_ij;
        yy.reset_shortest_path_closed();
        PPL_ASSERT(i > 0 || j > 0);
        Variable var(((i > 0) ? i : j) - 1);
        yy.incremental_shortest_path_closure_assign(var);
        if (target.contains(yy)) {
          // Target reached: swap `x' and `res' if needed.
          if (res_num_non_redundant < x_num_non_redundant) {
            res.reset_shortest_path_closed();
            x.m_swap(res);
          }
          return bool_result;
        }
      }
    }
  }
  // This point should be unreachable.
  PPL_UNREACHABLE;
  return false;
}

template <typename T>
void
BD_Shape<T>::add_space_dimensions_and_embed(const dimension_type m) {
  // Adding no dimensions is a no-op.
  if (m == 0) {
    return;
  }
  const dimension_type space_dim = space_dimension();
  const dimension_type new_space_dim = space_dim + m;
  const bool was_zero_dim_univ = (!marked_empty() && space_dim == 0);

  // To embed an n-dimension space BDS in a (n+m)-dimension space,
  // we just add `m' rows and columns in the bounded difference shape,
  // initialized to PLUS_INFINITY.
  dbm.grow(new_space_dim + 1);

  // Shortest-path closure is maintained (if it was holding).
  // TODO: see whether reduction can be (efficiently!) maintained too.
  if (marked_shortest_path_reduced()) {
    reset_shortest_path_reduced();
  }
  // If `*this' was the zero-dim space universe BDS,
  // the we can set the shortest-path closure flag.
  if (was_zero_dim_univ) {
    set_shortest_path_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::add_space_dimensions_and_project(const dimension_type m) {
  // Adding no dimensions is a no-op.
  if (m == 0) {
    return;
  }
  const dimension_type space_dim = space_dimension();

  // If `*this' was zero-dimensional, then we add `m' rows and columns.
  // If it also was non-empty, then we zero all the added elements
  // and set the flag for shortest-path closure.
  if (space_dim == 0) {
    dbm.grow(m + 1);
    if (!marked_empty()) {
      for (dimension_type i = m + 1; i-- > 0; ) {
        DB_Row<N>& dbm_i = dbm[i];
        for (dimension_type j = m + 1; j-- > 0; ) {
          if (i != j) {
            assign_r(dbm_i[j], 0, ROUND_NOT_NEEDED);
          }
        }
      }
      set_shortest_path_closed();
    }
    PPL_ASSERT(OK());
    return;
  }

  // To project an n-dimension space bounded difference shape
  // in a (n+m)-dimension space, we add `m' rows and columns.
  // In the first row and column of the matrix we add `zero' from
  // the (n+1)-th position to the end.
  const dimension_type new_space_dim = space_dim + m;
  dbm.grow(new_space_dim + 1);

  // Bottom of the matrix and first row.
  DB_Row<N>& dbm_0 = dbm[0];
  for (dimension_type i = space_dim + 1; i <= new_space_dim; ++i) {
    assign_r(dbm[i][0], 0, ROUND_NOT_NEEDED);
    assign_r(dbm_0[i], 0, ROUND_NOT_NEEDED);
  }

  if (marked_shortest_path_closed()) {
    reset_shortest_path_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::remove_space_dimensions(const Variables_Set& vars) {
  // The removal of no dimensions from any BDS is a no-op.
  // Note that this case also captures the only legal removal of
  // space dimensions from a BDS in a 0-dim space.
  if (vars.empty()) {
    PPL_ASSERT(OK());
    return;
  }

  const dimension_type old_space_dim = space_dimension();

  // Dimension-compatibility check.
  const dimension_type min_space_dim = vars.space_dimension();
  if (old_space_dim < min_space_dim) {
    throw_dimension_incompatible("remove_space_dimensions(vs)", min_space_dim);
  }
  // Shortest-path closure is necessary to keep precision.
  shortest_path_closure_assign();

  // When removing _all_ dimensions from a BDS, we obtain the
  // zero-dimensional BDS.
  const dimension_type new_space_dim = old_space_dim - vars.size();
  if (new_space_dim == 0) {
    dbm.resize_no_copy(1);
    if (!marked_empty()) {
      // We set the zero_dim_univ flag.
      set_zero_dim_univ();
    }
    PPL_ASSERT(OK());
    return;
  }

  // Handle the case of an empty BD_Shape.
  if (marked_empty()) {
    dbm.resize_no_copy(new_space_dim + 1);
    PPL_ASSERT(OK());
    return;
  }

  // Shortest-path closure is maintained.
  // TODO: see whether reduction can be (efficiently!) maintained too.
  if (marked_shortest_path_reduced()) {
    reset_shortest_path_reduced();
  }
  // For each variable to remove, we fill the corresponding column and
  // row by shifting respectively left and above those
  // columns and rows, that will not be removed.
  Variables_Set::const_iterator vsi = vars.begin();
  Variables_Set::const_iterator vsi_end = vars.end();
  dimension_type dst = *vsi + 1;
  dimension_type src = dst + 1;
  for (++vsi; vsi != vsi_end; ++vsi) {
    const dimension_type vsi_next = *vsi + 1;
    // All other columns and rows are moved respectively to the left
    // and above.
    while (src < vsi_next) {
      using std::swap;
      swap(dbm[dst], dbm[src]);
      for (dimension_type i = old_space_dim + 1; i-- > 0; ) {
        DB_Row<N>& dbm_i = dbm[i];
        assign_or_swap(dbm_i[dst], dbm_i[src]);
      }
      ++dst;
      ++src;
    }
    ++src;
  }

  // Moving the remaining rows and columns.
  while (src <= old_space_dim) {
    using std::swap;
    swap(dbm[dst], dbm[src]);
    for (dimension_type i = old_space_dim + 1; i-- > 0; ) {
      DB_Row<N>& dbm_i = dbm[i];
      assign_or_swap(dbm_i[dst], dbm_i[src]);
    }
    ++src;
    ++dst;
  }

  // Update the space dimension.
  dbm.resize_no_copy(new_space_dim + 1);
  PPL_ASSERT(OK());
}

template <typename T>
template <typename Partial_Function>
void
BD_Shape<T>::map_space_dimensions(const Partial_Function& pfunc) {
  const dimension_type space_dim = space_dimension();
  // TODO: this implementation is just an executable specification.
  if (space_dim == 0) {
    return;
  }
  if (pfunc.has_empty_codomain()) {
    // All dimensions vanish: the BDS becomes zero_dimensional.
    remove_higher_space_dimensions(0);
    return;
  }

  const dimension_type new_space_dim = pfunc.max_in_codomain() + 1;
  // If we are going to actually reduce the space dimension,
  // then shortest-path closure is required to keep precision.
  if (new_space_dim < space_dim) {
    shortest_path_closure_assign();
  }
  // If the BDS is empty, then it is sufficient to adjust the
  // space dimension of the bounded difference shape.
  if (marked_empty()) {
    remove_higher_space_dimensions(new_space_dim);
    return;
  }

  // Shortest-path closure is maintained (if it was holding).
  // TODO: see whether reduction can be (efficiently!) maintained too.
  if (marked_shortest_path_reduced()) {
    reset_shortest_path_reduced();
  }
  // We create a new matrix with the new space dimension.
  DB_Matrix<N> x(new_space_dim+1);
  // First of all we must map the unary constraints, because
  // there is the fictitious variable `zero', that can't be mapped
  // at all.
  DB_Row<N>& dbm_0 = dbm[0];
  DB_Row<N>& x_0 = x[0];
  for (dimension_type j = 1; j <= space_dim; ++j) {
    dimension_type new_j;
    if (pfunc.maps(j - 1, new_j)) {
      assign_or_swap(x_0[new_j + 1], dbm_0[j]);
      assign_or_swap(x[new_j + 1][0], dbm[j][0]);
    }
  }
  // Now we map the binary constraints, exchanging the indexes.
  for (dimension_type i = 1; i <= space_dim; ++i) {
    dimension_type new_i;
    if (pfunc.maps(i - 1, new_i)) {
      DB_Row<N>& dbm_i = dbm[i];
      ++new_i;
      DB_Row<N>& x_new_i = x[new_i];
      for (dimension_type j = i+1; j <= space_dim; ++j) {
        dimension_type new_j;
        if (pfunc.maps(j - 1, new_j)) {
          ++new_j;
          assign_or_swap(x_new_i[new_j], dbm_i[j]);
          assign_or_swap(x[new_j][new_i], dbm[j][i]);
        }
      }
    }
  }

  using std::swap;
  swap(dbm, x);
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::intersection_assign(const BD_Shape& y) {
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("intersection_assign(y)", y);
  }
  // If one of the two bounded difference shapes is empty,
  // the intersection is empty.
  if (marked_empty()) {
    return;
  }
  if (y.marked_empty()) {
    set_empty();
    return;
  }

  // If both bounded difference shapes are zero-dimensional,
  // then at this point they are necessarily non-empty,
  // so that their intersection is non-empty too.
  if (space_dim == 0) {
    return;
  }
  // To intersect two bounded difference shapes we compare
  // the constraints and we choose the less values.
  bool changed = false;
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    DB_Row<N>& dbm_i = dbm[i];
    const DB_Row<N>& y_dbm_i = y.dbm[i];
    for (dimension_type j = space_dim + 1; j-- > 0; ) {
      N& dbm_ij = dbm_i[j];
      const N& y_dbm_ij = y_dbm_i[j];
      if (dbm_ij > y_dbm_ij) {
        dbm_ij = y_dbm_ij;
        changed = true;
      }
    }
  }

  if (changed && marked_shortest_path_closed()) {
    reset_shortest_path_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
template <typename Iterator>
void
BD_Shape<T>::CC76_extrapolation_assign(const BD_Shape& y,
                                       Iterator first, Iterator last,
                                       unsigned* tp) {
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("CC76_extrapolation_assign(y)", y);
  }
  // We assume that `y' is contained in or equal to `*this'.
  PPL_EXPECT_HEAVY(copy_contains(*this, y));

  // If both bounded difference shapes are zero-dimensional,
  // since `*this' contains `y', we simply return `*this'.
  if (space_dim == 0) {
    return;
  }
  shortest_path_closure_assign();
  // If `*this' is empty, since `*this' contains `y', `y' is empty too.
  if (marked_empty()) {
    return;
  }
  y.shortest_path_closure_assign();
  // If `y' is empty, we return.
  if (y.marked_empty()) {
    return;
  }
  // If there are tokens available, work on a temporary copy.
  if (tp != 0 && *tp > 0) {
    BD_Shape<T> x_tmp(*this);
    x_tmp.CC76_extrapolation_assign(y, first, last, 0);
    // If the widening was not precise, use one of the available tokens.
    if (!contains(x_tmp)) {
      --(*tp);
    }
    return;
  }

  // Compare each constraint in `y' to the corresponding one in `*this'.
  // The constraint in `*this' is kept as is if it is stronger than or
  // equal to the constraint in `y'; otherwise, the inhomogeneous term
  // of the constraint in `*this' is further compared with elements taken
  // from a sorted container (the stop-points, provided by the user), and
  // is replaced by the first entry, if any, which is greater than or equal
  // to the inhomogeneous term. If no such entry exists, the constraint
  // is removed altogether.
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    DB_Row<N>& dbm_i = dbm[i];
    const DB_Row<N>& y_dbm_i = y.dbm[i];
    for (dimension_type j = space_dim + 1; j-- > 0; ) {
      N& dbm_ij = dbm_i[j];
      const N& y_dbm_ij = y_dbm_i[j];
      if (y_dbm_ij < dbm_ij) {
        Iterator k = std::lower_bound(first, last, dbm_ij);
        if (k != last) {
          if (dbm_ij < *k) {
            assign_r(dbm_ij, *k, ROUND_UP);
          }
        }
        else {
          assign_r(dbm_ij, PLUS_INFINITY, ROUND_NOT_NEEDED);
        }
      }
    }
  }
  reset_shortest_path_closed();
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::get_limiting_shape(const Constraint_System& cs,
                                BD_Shape& limiting_shape) const {
  // Private method: the caller has to ensure the following.
  PPL_ASSERT(cs.space_dimension() <= space_dimension());

  shortest_path_closure_assign();
  bool changed = false;
  PPL_DIRTY_TEMP_COEFFICIENT(coeff);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_c_term);
  PPL_DIRTY_TEMP(N, d);
  PPL_DIRTY_TEMP(N, d1);
  for (Constraint_System::const_iterator cs_i = cs.begin(),
         cs_end = cs.end(); cs_i != cs_end; ++cs_i) {
    const Constraint& c = *cs_i;
    dimension_type num_vars = 0;
    dimension_type i = 0;
    dimension_type j = 0;
    // Constraints that are not bounded differences are ignored.
    if (BD_Shape_Helpers::extract_bounded_difference(c, num_vars, i, j, coeff)) {
      // Select the cell to be modified for the "<=" part of the constraint,
      // and set `coeff' to the absolute value of itself.
      const bool negative = (coeff < 0);
      const N& x = negative ? dbm[i][j] : dbm[j][i];
      const N& y = negative ? dbm[j][i] : dbm[i][j];
      DB_Matrix<N>& ls_dbm = limiting_shape.dbm;
      if (negative) {
        neg_assign(coeff);
      }
      // Compute the bound for `x', rounding towards plus infinity.
      div_round_up(d, c.inhomogeneous_term(), coeff);
      if (x <= d) {
        if (c.is_inequality()) {
          N& ls_x = negative ? ls_dbm[i][j] : ls_dbm[j][i];
          if (ls_x > d) {
            ls_x = d;
            changed = true;
          }
        }
        else {
          // Compute the bound for `y', rounding towards plus infinity.
          neg_assign(minus_c_term, c.inhomogeneous_term());
          div_round_up(d1, minus_c_term, coeff);
          if (y <= d1) {
            N& ls_x = negative ? ls_dbm[i][j] : ls_dbm[j][i];
            N& ls_y = negative ? ls_dbm[j][i] : ls_dbm[i][j];
            if ((ls_x >= d && ls_y > d1) || (ls_x > d && ls_y >= d1)) {
              ls_x = d;
              ls_y = d1;
              changed = true;
            }
          }
        }
      }
    }
  }

  // In general, adding a constraint does not preserve the shortest-path
  // closure of the bounded difference shape.
  if (changed && limiting_shape.marked_shortest_path_closed()) {
    limiting_shape.reset_shortest_path_closed();
  }
}

template <typename T>
void
BD_Shape<T>::limited_CC76_extrapolation_assign(const BD_Shape& y,
                                               const Constraint_System& cs,
                                               unsigned* tp) {
  // Dimension-compatibility check.
  const dimension_type space_dim = space_dimension();
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("limited_CC76_extrapolation_assign(y, cs)",
                                 y);
  }
  // `cs' must be dimension-compatible with the two systems
  // of bounded differences.
  const dimension_type cs_space_dim = cs.space_dimension();
  if (space_dim < cs_space_dim) {
    throw_invalid_argument("limited_CC76_extrapolation_assign(y, cs)",
                           "cs is space_dimension incompatible");
  }

  // Strict inequalities not allowed.
  if (cs.has_strict_inequalities()) {
    throw_invalid_argument("limited_CC76_extrapolation_assign(y, cs)",
                           "cs has strict inequalities");
  }
  // The limited CC76-extrapolation between two systems of bounded
  // differences in a zero-dimensional space is a system of bounded
  // differences in a zero-dimensional space, too.
  if (space_dim == 0) {
    return;
  }
  // We assume that `y' is contained in or equal to `*this'.
  PPL_EXPECT_HEAVY(copy_contains(*this, y));

  // If `*this' is empty, since `*this' contains `y', `y' is empty too.
  if (marked_empty()) {
    return;
  }
  // If `y' is empty, we return.
  if (y.marked_empty()) {
    return;
  }
  BD_Shape<T> limiting_shape(space_dim, UNIVERSE);
  get_limiting_shape(cs, limiting_shape);
  CC76_extrapolation_assign(y, tp);
  intersection_assign(limiting_shape);
}

template <typename T>
void
BD_Shape<T>::BHMZ05_widening_assign(const BD_Shape& y, unsigned* tp) {
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("BHMZ05_widening_assign(y)", y);
  }
  // We assume that `y' is contained in or equal to `*this'.
  PPL_EXPECT_HEAVY(copy_contains(*this, y));

  // Compute the affine dimension of `y'.
  const dimension_type y_affine_dim = y.affine_dimension();
  // If the affine dimension of `y' is zero, then either `y' is
  // zero-dimensional, or it is empty, or it is a singleton.
  // In all cases, due to the inclusion hypothesis, the result is `*this'.
  if (y_affine_dim == 0) {
    return;
  }
  // If the affine dimension has changed, due to the inclusion hypothesis,
  // the result is `*this'.
  const dimension_type x_affine_dim = affine_dimension();
  PPL_ASSERT(x_affine_dim >= y_affine_dim);
  if (x_affine_dim != y_affine_dim) {
    return;
  }
  // If there are tokens available, work on a temporary copy.
  if (tp != 0 && *tp > 0) {
    BD_Shape<T> x_tmp(*this);
    x_tmp.BHMZ05_widening_assign(y, 0);
    // If the widening was not precise, use one of the available tokens.
    if (!contains(x_tmp)) {
      --(*tp);
    }
    return;
  }

  // Here no token is available.
  PPL_ASSERT(marked_shortest_path_closed() && y.marked_shortest_path_closed());
  // Minimize `y'.
  y.shortest_path_reduction_assign();

  // Extrapolate unstable bounds, taking into account redundancy in `y'.
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    DB_Row<N>& dbm_i = dbm[i];
    const DB_Row<N>& y_dbm_i = y.dbm[i];
    const Bit_Row& y_redundancy_i = y.redundancy_dbm[i];
    for (dimension_type j = space_dim + 1; j-- > 0; ) {
      N& dbm_ij = dbm_i[j];
      // Note: in the following line the use of `!=' (as opposed to
      // the use of `<' that would seem -but is not- equivalent) is
      // intentional.
      if (y_redundancy_i[j] || y_dbm_i[j] != dbm_ij) {
        assign_r(dbm_ij, PLUS_INFINITY, ROUND_NOT_NEEDED);
      }
    }
  }
  // NOTE: this will also reset the shortest-path reduction flag,
  // even though the dbm is still in reduced form. However, the
  // current implementation invariant requires that any reduced dbm
  // is closed too.
  reset_shortest_path_closed();
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::limited_BHMZ05_extrapolation_assign(const BD_Shape& y,
                                                 const Constraint_System& cs,
                                                 unsigned* tp) {
  // Dimension-compatibility check.
  const dimension_type space_dim = space_dimension();
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("limited_BHMZ05_extrapolation_assign(y, cs)",
                                 y);
  }
  // `cs' must be dimension-compatible with the two systems
  // of bounded differences.
  const dimension_type cs_space_dim = cs.space_dimension();
  if (space_dim < cs_space_dim) {
    throw_invalid_argument("limited_BHMZ05_extrapolation_assign(y, cs)",
                           "cs is space-dimension incompatible");
  }
  // Strict inequalities are not allowed.
  if (cs.has_strict_inequalities()) {
    throw_invalid_argument("limited_BHMZ05_extrapolation_assign(y, cs)",
                           "cs has strict inequalities");
  }
  // The limited BHMZ05-extrapolation between two systems of bounded
  // differences in a zero-dimensional space is a system of bounded
  // differences in a zero-dimensional space, too.
  if (space_dim == 0) {
    return;
  }
  // We assume that `y' is contained in or equal to `*this'.
  PPL_EXPECT_HEAVY(copy_contains(*this, y));

  // If `*this' is empty, since `*this' contains `y', `y' is empty too.
  if (marked_empty()) {
    return;
  }
  // If `y' is empty, we return.
  if (y.marked_empty()) {
    return;
  }
  BD_Shape<T> limiting_shape(space_dim, UNIVERSE);
  get_limiting_shape(cs, limiting_shape);
  BHMZ05_widening_assign(y, tp);
  intersection_assign(limiting_shape);
}

template <typename T>
void
BD_Shape<T>::CC76_narrowing_assign(const BD_Shape& y) {
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("CC76_narrowing_assign(y)", y);
  }
  // We assume that `*this' is contained in or equal to `y'.
  PPL_EXPECT_HEAVY(copy_contains(y, *this));

  // If both bounded difference shapes are zero-dimensional,
  // since `y' contains `*this', we simply return `*this'.
  if (space_dim == 0) {
    return;
  }
  y.shortest_path_closure_assign();
  // If `y' is empty, since `y' contains `this', `*this' is empty too.
  if (y.marked_empty()) {
    return;
  }
  shortest_path_closure_assign();
  // If `*this' is empty, we return.
  if (marked_empty()) {
    return;
  }
  // Replace each constraint in `*this' by the corresponding constraint
  // in `y' if the corresponding inhomogeneous terms are both finite.
  bool changed = false;
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    DB_Row<N>& dbm_i = dbm[i];
    const DB_Row<N>& y_dbm_i = y.dbm[i];
    for (dimension_type j = space_dim + 1; j-- > 0; ) {
      N& dbm_ij = dbm_i[j];
      const N& y_dbm_ij = y_dbm_i[j];
      if (!is_plus_infinity(dbm_ij)
          && !is_plus_infinity(y_dbm_ij)
          && dbm_ij != y_dbm_ij) {
        dbm_ij = y_dbm_ij;
        changed = true;
      }
    }
  }
  if (changed && marked_shortest_path_closed()) {
    reset_shortest_path_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>
::deduce_v_minus_u_bounds(const dimension_type v,
                          const dimension_type last_v,
                          const Linear_Expression& sc_expr,
                          Coefficient_traits::const_reference sc_denom,
                          const N& ub_v) {
  PPL_ASSERT(sc_denom > 0);
  PPL_ASSERT(!is_plus_infinity(ub_v));
  // Deduce constraints of the form `v - u', where `u != v'.
  // Note: the shortest-path closure is able to deduce the constraint
  // `v - u <= ub_v - lb_u'. We can be more precise if variable `u'
  // played an active role in the computation of the upper bound for `v',
  // i.e., if the corresponding coefficient `q == expr_u/denom' is
  // greater than zero. In particular:
  // if `q >= 1',    then `v - u <= ub_v - ub_u';
  // if `0 < q < 1', then `v - u <= ub_v - (q*ub_u + (1-q)*lb_u)'.
  PPL_DIRTY_TEMP(mpq_class, mpq_sc_denom);
  assign_r(mpq_sc_denom, sc_denom, ROUND_NOT_NEEDED);
  const DB_Row<N>& dbm_0 = dbm[0];
  // Speculative allocation of temporaries to be used in the following loop.
  PPL_DIRTY_TEMP(mpq_class, minus_lb_u);
  PPL_DIRTY_TEMP(mpq_class, q);
  PPL_DIRTY_TEMP(mpq_class, ub_u);
  PPL_DIRTY_TEMP(N, up_approx);
  for (Linear_Expression::const_iterator u = sc_expr.begin(),
        u_end = sc_expr.lower_bound(Variable(last_v)); u != u_end; ++u) {
    const dimension_type u_dim = u.variable().space_dimension();
    if (u_dim == v) {
      continue;
    }
    const Coefficient& expr_u = *u;
    if (expr_u < 0) {
      continue;
    }
    PPL_ASSERT(expr_u > 0);
    if (expr_u >= sc_denom) {
      // Deducing `v - u <= ub_v - ub_u'.
      sub_assign_r(dbm[u_dim][v], ub_v, dbm_0[u_dim], ROUND_UP);
    }
    else {
      DB_Row<N>& dbm_u = dbm[u_dim];
      const N& dbm_u0 = dbm_u[0];
      if (!is_plus_infinity(dbm_u0)) {
        // Let `ub_u' and `lb_u' be the known upper and lower bound
        // for `u', respectively. Letting `q = expr_u/sc_denom' be the
        // rational coefficient of `u' in `sc_expr/sc_denom',
        // the upper bound for `v - u' is computed as
        // `ub_v - (q * ub_u + (1-q) * lb_u)', i.e.,
        // `ub_v + (-lb_u) - q * (ub_u + (-lb_u))'.
        assign_r(minus_lb_u, dbm_u0, ROUND_NOT_NEEDED);
        assign_r(q, expr_u, ROUND_NOT_NEEDED);
        div_assign_r(q, q, mpq_sc_denom, ROUND_NOT_NEEDED);
        assign_r(ub_u, dbm_0[u_dim], ROUND_NOT_NEEDED);
        // Compute `ub_u - lb_u'.
        add_assign_r(ub_u, ub_u, minus_lb_u, ROUND_NOT_NEEDED);
        // Compute `(-lb_u) - q * (ub_u - lb_u)'.
        sub_mul_assign_r(minus_lb_u, q, ub_u, ROUND_NOT_NEEDED);
        assign_r(up_approx, minus_lb_u, ROUND_UP);
        // Deducing `v - u <= ub_v - (q * ub_u + (1-q) * lb_u)'.
        add_assign_r(dbm_u[v], ub_v, up_approx, ROUND_UP);
      }
    }
  }
}

template <typename T>
void
BD_Shape<T>
::deduce_u_minus_v_bounds(const dimension_type v,
                          const dimension_type last_v,
                          const Linear_Expression& sc_expr,
                          Coefficient_traits::const_reference sc_denom,
                          const N& minus_lb_v) {
  PPL_ASSERT(sc_denom > 0);
  PPL_ASSERT(!is_plus_infinity(minus_lb_v));
  // Deduce constraints of the form `u - v', where `u != v'.
  // Note: the shortest-path closure is able to deduce the constraint
  // `u - v <= ub_u - lb_v'. We can be more precise if variable `u'
  // played an active role in the computation of the lower bound for `v',
  // i.e., if the corresponding coefficient `q == expr_u/denom' is
  // greater than zero. In particular:
  // if `q >= 1',    then `u - v <= lb_u - lb_v';
  // if `0 < q < 1', then `u - v <= (q*lb_u + (1-q)*ub_u) - lb_v'.
  PPL_DIRTY_TEMP(mpq_class, mpq_sc_denom);
  assign_r(mpq_sc_denom, sc_denom, ROUND_NOT_NEEDED);
  DB_Row<N>& dbm_0 = dbm[0];
  DB_Row<N>& dbm_v = dbm[v];
  // Speculative allocation of temporaries to be used in the following loop.
  PPL_DIRTY_TEMP(mpq_class, ub_u);
  PPL_DIRTY_TEMP(mpq_class, q);
  PPL_DIRTY_TEMP(mpq_class, minus_lb_u);
  PPL_DIRTY_TEMP(N, up_approx);
  // No need to consider indices greater than `last_v'.
  for (Linear_Expression::const_iterator u = sc_expr.begin(),
        u_end = sc_expr.lower_bound(Variable(last_v)); u != u_end; ++u) {
    const Variable u_var = u.variable();
    const dimension_type u_dim = u_var.space_dimension();
    if (u_var.space_dimension() == v) {
      continue;
    }
    const Coefficient& expr_u = *u;
    if (expr_u < 0) {
      continue;
    }
    PPL_ASSERT(expr_u > 0);
    if (expr_u >= sc_denom) {
      // Deducing `u - v <= lb_u - lb_v',
      // i.e., `u - v <= (-lb_v) - (-lb_u)'.
      sub_assign_r(dbm_v[u_dim], minus_lb_v, dbm[u_dim][0], ROUND_UP);
    }
    else {
      const N& dbm_0u = dbm_0[u_dim];
      if (!is_plus_infinity(dbm_0u)) {
        // Let `ub_u' and `lb_u' be the known upper and lower bound
        // for `u', respectively. Letting `q = expr_u/sc_denom' be the
        // rational coefficient of `u' in `sc_expr/sc_denom',
        // the upper bound for `u - v' is computed as
        // `(q * lb_u + (1-q) * ub_u) - lb_v', i.e.,
        // `ub_u - q * (ub_u + (-lb_u)) + minus_lb_v'.
        assign_r(ub_u, dbm_0u, ROUND_NOT_NEEDED);
        assign_r(q, expr_u, ROUND_NOT_NEEDED);
        div_assign_r(q, q, mpq_sc_denom, ROUND_NOT_NEEDED);
        assign_r(minus_lb_u, dbm[u_dim][0], ROUND_NOT_NEEDED);
        // Compute `ub_u - lb_u'.
        add_assign_r(minus_lb_u, minus_lb_u, ub_u, ROUND_NOT_NEEDED);
        // Compute `ub_u - q * (ub_u - lb_u)'.
        sub_mul_assign_r(ub_u, q, minus_lb_u, ROUND_NOT_NEEDED);
        assign_r(up_approx, ub_u, ROUND_UP);
        // Deducing `u - v <= (q*lb_u + (1-q)*ub_u) - lb_v'.
        add_assign_r(dbm_v[u_dim], up_approx, minus_lb_v, ROUND_UP);
      }
    }
  }
}

template <typename T>
void
BD_Shape<T>::forget_all_dbm_constraints(const dimension_type v) {
  PPL_ASSERT(0 < v && v <= dbm.num_rows());
  DB_Row<N>& dbm_v = dbm[v];
  for (dimension_type i = dbm.num_rows(); i-- > 0; ) {
    assign_r(dbm_v[i], PLUS_INFINITY, ROUND_NOT_NEEDED);
    assign_r(dbm[i][v], PLUS_INFINITY, ROUND_NOT_NEEDED);
  }
}

template <typename T>
void
BD_Shape<T>::forget_binary_dbm_constraints(const dimension_type v) {
  PPL_ASSERT(0 < v && v <= dbm.num_rows());
  DB_Row<N>& dbm_v = dbm[v];
  for (dimension_type i = dbm.num_rows()-1; i > 0; --i) {
    assign_r(dbm_v[i], PLUS_INFINITY, ROUND_NOT_NEEDED);
    assign_r(dbm[i][v], PLUS_INFINITY, ROUND_NOT_NEEDED);
  }
}

template <typename T>
void
BD_Shape<T>::unconstrain(const Variable var) {
  // Dimension-compatibility check.
  const dimension_type var_space_dim = var.space_dimension();
  if (space_dimension() < var_space_dim) {
    throw_dimension_incompatible("unconstrain(var)", var_space_dim);
  }
  // Shortest-path closure is necessary to detect emptiness
  // and all (possibly implicit) constraints.
  shortest_path_closure_assign();

  // If the shape is empty, this is a no-op.
  if (marked_empty()) {
    return;
  }
  forget_all_dbm_constraints(var_space_dim);
  // Shortest-path closure is preserved, but not reduction.
  reset_shortest_path_reduced();
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::unconstrain(const Variables_Set& vars) {
  // The cylindrification with respect to no dimensions is a no-op.
  // This case captures the only legal cylindrification in a 0-dim space.
  if (vars.empty()) {
    return;
  }
  // Dimension-compatibility check.
  const dimension_type min_space_dim = vars.space_dimension();
  if (space_dimension() < min_space_dim) {
    throw_dimension_incompatible("unconstrain(vs)", min_space_dim);
  }
  // Shortest-path closure is necessary to detect emptiness
  // and all (possibly implicit) constraints.
  shortest_path_closure_assign();

  // If the shape is empty, this is a no-op.
  if (marked_empty()) {
    return;
  }
  for (Variables_Set::const_iterator vsi = vars.begin(),
         vsi_end = vars.end(); vsi != vsi_end; ++vsi) {
    forget_all_dbm_constraints(*vsi + 1);
  }
  // Shortest-path closure is preserved, but not reduction.
  reset_shortest_path_reduced();
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::refine(const Variable var,
                    const Relation_Symbol relsym,
                    const Linear_Expression& expr,
                    Coefficient_traits::const_reference denominator) {
  PPL_ASSERT(denominator != 0);
  PPL_ASSERT(space_dimension() >= expr.space_dimension());
  const dimension_type v = var.id() + 1;
  PPL_ASSERT(v <= space_dimension());
  PPL_ASSERT(expr.coefficient(var) == 0);
  PPL_ASSERT(relsym != LESS_THAN && relsym != GREATER_THAN);

  const Coefficient& b = expr.inhomogeneous_term();
  // Number of non-zero coefficients in `expr': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;
  // Index of the last non-zero coefficient in `expr', if any.
  dimension_type w = expr.last_nonzero();

  if (w != 0) {
    ++t;
    if (!expr.all_zeroes(1, w)) {
      ++t;
    }
  }

  // Since we are only able to record bounded differences, we can
  // precisely deal with the case of a single variable only if its
  // coefficient (taking into account the denominator) is 1.
  // If this is not the case, we fall back to the general case
  // so as to over-approximate the constraint.
  if (t == 1 && expr.get(Variable(w - 1)) != denominator) {
    t = 2;
  }
  // Now we know the form of `expr':
  // - If t == 0, then expr == b, with `b' a constant;
  // - If t == 1, then expr == a*w + b, where `w != v' and `a == denominator';
  // - If t == 2, the `expr' is of the general form.
  const DB_Row<N>& dbm_0 = dbm[0];
  PPL_DIRTY_TEMP_COEFFICIENT(minus_denom);
  neg_assign(minus_denom, denominator);

  if (t == 0) {
    // Case 1: expr == b.
    switch (relsym) {
    case EQUAL:
      // Add the constraint `var == b/denominator'.
      add_dbm_constraint(0, v, b, denominator);
      add_dbm_constraint(v, 0, b, minus_denom);
      break;
    case LESS_OR_EQUAL:
      // Add the constraint `var <= b/denominator'.
      add_dbm_constraint(0, v, b, denominator);
      break;
    case GREATER_OR_EQUAL:
      // Add the constraint `var >= b/denominator',
      // i.e., `-var <= -b/denominator',
      add_dbm_constraint(v, 0, b, minus_denom);
      break;
    default:
      // We already dealt with the other cases.
      PPL_UNREACHABLE;
      break;
    }
    return;
  }

  if (t == 1) {
    // Case 2: expr == a*w + b, w != v, a == denominator.
    PPL_ASSERT(expr.get(Variable(w - 1)) == denominator);
    PPL_DIRTY_TEMP(N, d);
    switch (relsym) {
    case EQUAL:
      // Add the new constraint `v - w <= b/denominator'.
      div_round_up(d, b, denominator);
      add_dbm_constraint(w, v, d);
      // Add the new constraint `v - w >= b/denominator',
      // i.e., `w - v <= -b/denominator'.
      div_round_up(d, b, minus_denom);
      add_dbm_constraint(v, w, d);
      break;
    case LESS_OR_EQUAL:
      // Add the new constraint `v - w <= b/denominator'.
      div_round_up(d, b, denominator);
      add_dbm_constraint(w, v, d);
      break;
    case GREATER_OR_EQUAL:
      // Add the new constraint `v - w >= b/denominator',
      // i.e., `w - v <= -b/denominator'.
      div_round_up(d, b, minus_denom);
      add_dbm_constraint(v, w, d);
      break;
    default:
      // We already dealt with the other cases.
      PPL_UNREACHABLE;
      break;
    }
    return;
  }

  // Here t == 2, so that either
  // expr == a_1*x_1 + a_2*x_2 + ... + a_n*x_n + b, where n >= 2, or
  // expr == a*w + b, w != v and a != denominator.
  const bool is_sc = (denominator > 0);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_b);
  neg_assign(minus_b, b);
  const Coefficient& sc_b = is_sc ? b : minus_b;
  const Coefficient& minus_sc_b = is_sc ? minus_b : b;
  const Coefficient& sc_denom = is_sc ? denominator : minus_denom;
  const Coefficient& minus_sc_denom = is_sc ? minus_denom : denominator;
  // NOTE: here, for optimization purposes, `minus_expr' is only assigned
  // when `denominator' is negative. Do not use it unless you are sure
  // it has been correctly assigned.
  Linear_Expression minus_expr;
  if (!is_sc) {
    minus_expr = -expr;
  }
  const Linear_Expression& sc_expr = is_sc ? expr : minus_expr;

  PPL_DIRTY_TEMP(N, sum);
  // Indices of the variables that are unbounded in `this->dbm'.
  PPL_UNINITIALIZED(dimension_type, pinf_index);
  // Number of unbounded variables found.
  dimension_type pinf_count = 0;

  // Speculative allocation of temporaries that are used in most
  // of the computational traces starting from this point (also loops).
  PPL_DIRTY_TEMP_COEFFICIENT(minus_sc_i);
  PPL_DIRTY_TEMP(N, coeff_i);

  switch (relsym) {
  case EQUAL:
    {
      PPL_DIRTY_TEMP(N, neg_sum);
      // Indices of the variables that are unbounded in `this->dbm'.
      PPL_UNINITIALIZED(dimension_type, neg_pinf_index);
      // Number of unbounded variables found.
      dimension_type neg_pinf_count = 0;

      // Compute an upper approximation for `expr' into `sum',
      // taking into account the sign of `denominator'.

      // Approximate the inhomogeneous term.
      assign_r(sum, sc_b, ROUND_UP);
      assign_r(neg_sum, minus_sc_b, ROUND_UP);

      // Approximate the homogeneous part of `sc_expr'.
      // Note: indices above `w' can be disregarded, as they all have
      // a zero coefficient in `expr'.
      for (Linear_Expression::const_iterator i = sc_expr.begin(),
            i_end = sc_expr.lower_bound(Variable(w)); i != i_end; ++i) {
        const dimension_type i_dim = i.variable().space_dimension();
        const Coefficient& sc_i = *i;
        const int sign_i = sgn(sc_i);
        PPL_ASSERT(sign_i != 0);
        if (sign_i > 0) {
          assign_r(coeff_i, sc_i, ROUND_UP);
          // Approximating `sc_expr'.
          if (pinf_count <= 1) {
            const N& approx_i = dbm_0[i_dim];
            if (!is_plus_infinity(approx_i)) {
              add_mul_assign_r(sum, coeff_i, approx_i, ROUND_UP);
            }
            else {
              ++pinf_count;
              pinf_index = i_dim;
            }
          }
          // Approximating `-sc_expr'.
          if (neg_pinf_count <= 1) {
            const N& approx_minus_i = dbm[i_dim][0];
            if (!is_plus_infinity(approx_minus_i)) {
              add_mul_assign_r(neg_sum, coeff_i, approx_minus_i, ROUND_UP);
            }
            else {
              ++neg_pinf_count;
              neg_pinf_index = i_dim;
            }
          }
        }
        else {
          PPL_ASSERT(sign_i < 0);
          neg_assign(minus_sc_i, sc_i);
          // Note: using temporary named `coeff_i' to store -coeff_i.
          assign_r(coeff_i, minus_sc_i, ROUND_UP);
          // Approximating `sc_expr'.
          if (pinf_count <= 1) {
            const N& approx_minus_i = dbm[i_dim][0];
            if (!is_plus_infinity(approx_minus_i)) {
              add_mul_assign_r(sum, coeff_i, approx_minus_i, ROUND_UP);
            }
            else {
              ++pinf_count;
              pinf_index = i_dim;
            }
          }
          // Approximating `-sc_expr'.
          if (neg_pinf_count <= 1) {
            const N& approx_i = dbm_0[i_dim];
            if (!is_plus_infinity(approx_i)) {
              add_mul_assign_r(neg_sum, coeff_i, approx_i, ROUND_UP);
            }
            else {
              ++neg_pinf_count;
              neg_pinf_index = i_dim;
            }
          }
        }
      }
      // Return immediately if no approximation could be computed.
      if (pinf_count > 1 && neg_pinf_count > 1) {
        PPL_ASSERT(OK());
        return;
      }

      // In the following, shortest-path closure will be definitely lost.
      reset_shortest_path_closed();

      // Before computing quotients, the denominator should be approximated
      // towards zero. Since `sc_denom' is known to be positive, this amounts to
      // rounding downwards, which is achieved as usual by rounding upwards
      // `minus_sc_denom' and negating again the result.
      PPL_DIRTY_TEMP(N, down_sc_denom);
      assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
      neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);

      // Exploit the upper approximation, if possible.
      if (pinf_count <= 1) {
        // Compute quotient (if needed).
        if (down_sc_denom != 1) {
          div_assign_r(sum, sum, down_sc_denom, ROUND_UP);
        }
        // Add the upper bound constraint, if meaningful.
        if (pinf_count == 0) {
          // Add the constraint `v <= sum'.
          dbm[0][v] = sum;
          // Deduce constraints of the form `v - u', where `u != v'.
          deduce_v_minus_u_bounds(v, w, sc_expr, sc_denom, sum);
        }
        else {
          // Here `pinf_count == 1'.
          if (pinf_index != v
              && sc_expr.get(Variable(pinf_index - 1)) == sc_denom) {
            // Add the constraint `v - pinf_index <= sum'.
            dbm[pinf_index][v] = sum;
          }
        }
      }

      // Exploit the lower approximation, if possible.
      if (neg_pinf_count <= 1) {
        // Compute quotient (if needed).
        if (down_sc_denom != 1) {
          div_assign_r(neg_sum, neg_sum, down_sc_denom, ROUND_UP);
        }
        // Add the lower bound constraint, if meaningful.
        if (neg_pinf_count == 0) {
          // Add the constraint `v >= -neg_sum', i.e., `-v <= neg_sum'.
          DB_Row<N>& dbm_v = dbm[v];
          dbm_v[0] = neg_sum;
          // Deduce constraints of the form `u - v', where `u != v'.
          deduce_u_minus_v_bounds(v, w, sc_expr, sc_denom, neg_sum);
        }
        // Here `neg_pinf_count == 1'.
        else if (neg_pinf_index != v
              && sc_expr.get(Variable(neg_pinf_index - 1)) == sc_denom) {
            // Add the constraint `v - neg_pinf_index >= -neg_sum',
            // i.e., `neg_pinf_index - v <= neg_sum'.
            dbm[v][neg_pinf_index] = neg_sum;
        }
      }
    }
    break;

  case LESS_OR_EQUAL:
    // Compute an upper approximation for `expr' into `sum',
    // taking into account the sign of `denominator'.

    // Approximate the inhomogeneous term.
    assign_r(sum, sc_b, ROUND_UP);

    // Approximate the homogeneous part of `sc_expr'.
    // Note: indices above `w' can be disregarded, as they all have
    // a zero coefficient in `expr'.
    for (Linear_Expression::const_iterator i = sc_expr.begin(),
          i_end = sc_expr.lower_bound(Variable(w)); i != i_end; ++i) {
      const Coefficient& sc_i = *i;
      const dimension_type i_dim = i.variable().space_dimension();
      const int sign_i = sgn(sc_i);
      PPL_ASSERT(sign_i != 0);
      // Choose carefully: we are approximating `sc_expr'.
      const N& approx_i = (sign_i > 0) ? dbm_0[i_dim] : dbm[i_dim][0];
      if (is_plus_infinity(approx_i)) {
        if (++pinf_count > 1) {
          break;
        }
        pinf_index = i_dim;
        continue;
      }
      if (sign_i > 0) {
        assign_r(coeff_i, sc_i, ROUND_UP);
      }
      else {
        neg_assign(minus_sc_i, sc_i);
        assign_r(coeff_i, minus_sc_i, ROUND_UP);
      }
      add_mul_assign_r(sum, coeff_i, approx_i, ROUND_UP);
    }

    // Divide by the (sign corrected) denominator (if needed).
    if (sc_denom != 1) {
      // Before computing the quotient, the denominator should be
      // approximated towards zero. Since `sc_denom' is known to be
      // positive, this amounts to rounding downwards, which is achieved
      // by rounding upwards `minus_sc - denom' and negating again the result.
      PPL_DIRTY_TEMP(N, down_sc_denom);
      assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
      neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
      div_assign_r(sum, sum, down_sc_denom, ROUND_UP);
    }

    if (pinf_count == 0) {
      // Add the constraint `v <= sum'.
      add_dbm_constraint(0, v, sum);
      // Deduce constraints of the form `v - u', where `u != v'.
      deduce_v_minus_u_bounds(v, w, sc_expr, sc_denom, sum);
    }
    else if (pinf_count == 1) {
      if (expr.get(Variable(pinf_index - 1)) == denominator) {
        // Add the constraint `v - pinf_index <= sum'.
        add_dbm_constraint(pinf_index, v, sum);
      }
    }
    break;

  case GREATER_OR_EQUAL:
    // Compute an upper approximation for `-sc_expr' into `sum'.
    // Note: approximating `-sc_expr' from above and then negating the
    // result is the same as approximating `sc_expr' from below.

    // Approximate the inhomogeneous term.
    assign_r(sum, minus_sc_b, ROUND_UP);

    // Approximate the homogeneous part of `-sc_expr'.
    for (Linear_Expression::const_iterator i = sc_expr.begin(),
          i_end = sc_expr.lower_bound(Variable(w)); i != i_end; ++i) {
      const Coefficient& sc_i = *i;
      const dimension_type i_dim = i.variable().space_dimension();
      const int sign_i = sgn(sc_i);
      PPL_ASSERT(sign_i != 0);
      // Choose carefully: we are approximating `-sc_expr'.
      const N& approx_i = (sign_i > 0) ? dbm[i_dim][0] : dbm_0[i_dim];
      if (is_plus_infinity(approx_i)) {
        if (++pinf_count > 1) {
          break;
        }
        pinf_index = i_dim;
        continue;
      }
      if (sign_i > 0) {
        assign_r(coeff_i, sc_i, ROUND_UP);
      }
      else {
        neg_assign(minus_sc_i, sc_i);
        assign_r(coeff_i, minus_sc_i, ROUND_UP);
      }
      add_mul_assign_r(sum, coeff_i, approx_i, ROUND_UP);
    }

    // Divide by the (sign corrected) denominator (if needed).
    if (sc_denom != 1) {
      // Before computing the quotient, the denominator should be
      // approximated towards zero. Since `sc_denom' is known to be positive,
      // this amounts to rounding downwards, which is achieved by rounding
      // upwards `minus_sc_denom' and negating again the result.
      PPL_DIRTY_TEMP(N, down_sc_denom);
      assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
      neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
      div_assign_r(sum, sum, down_sc_denom, ROUND_UP);
    }

    if (pinf_count == 0) {
      // Add the constraint `v >= -sum', i.e., `-v <= sum'.
      add_dbm_constraint(v, 0, sum);
      // Deduce constraints of the form `u - v', where `u != v'.
      deduce_u_minus_v_bounds(v, w, sc_expr, sc_denom, sum);
    }
    else if (pinf_count == 1) {
      if (pinf_index != v
          && expr.get(Variable(pinf_index - 1)) == denominator) {
        // Add the constraint `v - pinf_index >= -sum',
        // i.e., `pinf_index - v <= sum'.
        add_dbm_constraint(v, pinf_index, sum);
      }
    }
    break;

  default:
    // We already dealt with the other cases.
    PPL_UNREACHABLE;
    break;
  }

  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::affine_image(const Variable var,
                          const Linear_Expression& expr,
                          Coefficient_traits::const_reference denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("affine_image(v, e, d)", "d == 0");
  }
  // Dimension-compatibility checks.
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type space_dim = space_dimension();
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible("affine_image(v, e, d)", "e", expr);
  }
  // `var' should be one of the dimensions of the shape.
  const dimension_type v = var.id() + 1;
  if (v > space_dim) {
    throw_dimension_incompatible("affine_image(v, e, d)", var.id());
  }
  // The image of an empty BDS is empty too.
  shortest_path_closure_assign();
  if (marked_empty()) {
    return;
  }
  const Coefficient& b = expr.inhomogeneous_term();
  // Number of non-zero coefficients in `expr': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;
  // Index of the last non-zero coefficient in `expr', if any.
  dimension_type w = expr.last_nonzero();

  if (w != 0) {
    ++t;
    if (!expr.all_zeroes(1, w)) {
      ++t;
    }
  }

  // Now we know the form of `expr':
  // - If t == 0, then expr == b, with `b' a constant;
  // - If t == 1, then expr == a*w + b, where `w' can be `v' or another
  //   variable; in this second case we have to check whether `a' is
  //   equal to `denominator' or `-denominator', since otherwise we have
  //   to fall back on the general form;
  // - If t == 2, the `expr' is of the general form.
  PPL_DIRTY_TEMP_COEFFICIENT(minus_denom);
  neg_assign(minus_denom, denominator);

  if (t == 0) {
    // Case 1: expr == b.
    // Remove all constraints on `var'.
    forget_all_dbm_constraints(v);
    // Shortest-path closure is preserved, but not reduction.
    if (marked_shortest_path_reduced()) {
      reset_shortest_path_reduced();
    }
    // Add the constraint `var == b/denominator'.
    add_dbm_constraint(0, v, b, denominator);
    add_dbm_constraint(v, 0, b, minus_denom);
    PPL_ASSERT(OK());
    return;
  }

  if (t == 1) {
    // Value of the one and only non-zero coefficient in `expr'.
    const Coefficient& a = expr.get(Variable(w - 1));
    if (a == denominator || a == minus_denom) {
      // Case 2: expr == a*w + b, with a == +/- denominator.
      if (w == v) {
        // `expr' is of the form: a*v + b.
        if (a == denominator) {
          if (b == 0) {
            // The transformation is the identity function.
            return;
          }
          else {
            // Translate all the constraints on `var',
            // adding or subtracting the value `b/denominator'.
            PPL_DIRTY_TEMP(N, d);
            div_round_up(d, b, denominator);
            PPL_DIRTY_TEMP(N, c);
            div_round_up(c, b, minus_denom);
            DB_Row<N>& dbm_v = dbm[v];
            for (dimension_type i = space_dim + 1; i-- > 0; ) {
              N& dbm_vi = dbm_v[i];
              add_assign_r(dbm_vi, dbm_vi, c, ROUND_UP);
              N& dbm_iv = dbm[i][v];
              add_assign_r(dbm_iv, dbm_iv, d, ROUND_UP);
            }
            // Both shortest-path closure and reduction are preserved.
          }
        }
        else {
          // Here `a == -denominator'.
          // Remove the binary constraints on `var'.
          forget_binary_dbm_constraints(v);
          // Swap the unary constraints on `var'.
          using std::swap;
          swap(dbm[v][0], dbm[0][v]);
          // Shortest-path closure is not preserved.
          reset_shortest_path_closed();
          if (b != 0) {
            // Translate the unary constraints on `var',
            // adding or subtracting the value `b/denominator'.
            PPL_DIRTY_TEMP(N, c);
            div_round_up(c, b, minus_denom);
            N& dbm_v0 = dbm[v][0];
            add_assign_r(dbm_v0, dbm_v0, c, ROUND_UP);
            PPL_DIRTY_TEMP(N, d);
            div_round_up(d, b, denominator);
            N& dbm_0v = dbm[0][v];
            add_assign_r(dbm_0v, dbm_0v, d, ROUND_UP);
          }
        }
      }
      else {
        // Here `w != v', so that `expr' is of the form
        // +/-denominator * w + b.
        // Remove all constraints on `var'.
        forget_all_dbm_constraints(v);
        // Shortest-path closure is preserved, but not reduction.
        if (marked_shortest_path_reduced()) {
          reset_shortest_path_reduced();
        }
        if (a == denominator) {
          // Add the new constraint `v - w == b/denominator'.
          add_dbm_constraint(w, v, b, denominator);
          add_dbm_constraint(v, w, b, minus_denom);
        }
        else {
          // Here a == -denominator, so that we should be adding
          // the constraint `v + w == b/denominator'.
          // Approximate it by computing lower and upper bounds for `w'.
          const N& dbm_w0 = dbm[w][0];
          if (!is_plus_infinity(dbm_w0)) {
            // Add the constraint `v <= b/denominator - lower_w'.
            PPL_DIRTY_TEMP(N, d);
            div_round_up(d, b, denominator);
            add_assign_r(dbm[0][v], d, dbm_w0, ROUND_UP);
            reset_shortest_path_closed();
          }
          const N& dbm_0w = dbm[0][w];
          if (!is_plus_infinity(dbm_0w)) {
            // Add the constraint `v >= b/denominator - upper_w'.
            PPL_DIRTY_TEMP(N, c);
            div_round_up(c, b, minus_denom);
            add_assign_r(dbm[v][0], dbm_0w, c, ROUND_UP);
            reset_shortest_path_closed();
          }
        }
      }
      PPL_ASSERT(OK());
      return;
    }
  }

  // General case.
  // Either t == 2, so that
  // expr == a_1*x_1 + a_2*x_2 + ... + a_n*x_n + b, where n >= 2,
  // or t == 1, expr == a*w + b, but a <> +/- denominator.
  // We will remove all the constraints on `var' and add back
  // constraints providing upper and lower bounds for `var'.

  // Compute upper approximations for `expr' and `-expr'
  // into `pos_sum' and `neg_sum', respectively, taking into account
  // the sign of `denominator'.
  // Note: approximating `-expr' from above and then negating the
  // result is the same as approximating `expr' from below.
  const bool is_sc = (denominator > 0);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_b);
  neg_assign(minus_b, b);
  const Coefficient& sc_b = is_sc ? b : minus_b;
  const Coefficient& minus_sc_b = is_sc ? minus_b : b;
  const Coefficient& sc_denom = is_sc ? denominator : minus_denom;
  const Coefficient& minus_sc_denom = is_sc ? minus_denom : denominator;
  // NOTE: here, for optimization purposes, `minus_expr' is only assigned
  // when `denominator' is negative. Do not use it unless you are sure
  // it has been correctly assigned.
  Linear_Expression minus_expr;
  if (!is_sc) {
    minus_expr = -expr;
  }
  const Linear_Expression& sc_expr = is_sc ? expr : minus_expr;

  PPL_DIRTY_TEMP(N, pos_sum);
  PPL_DIRTY_TEMP(N, neg_sum);
  // Indices of the variables that are unbounded in `this->dbm'.
  PPL_UNINITIALIZED(dimension_type, pos_pinf_index);
  PPL_UNINITIALIZED(dimension_type, neg_pinf_index);
  // Number of unbounded variables found.
  dimension_type pos_pinf_count = 0;
  dimension_type neg_pinf_count = 0;

  // Approximate the inhomogeneous term.
  assign_r(pos_sum, sc_b, ROUND_UP);
  assign_r(neg_sum, minus_sc_b, ROUND_UP);

  // Approximate the homogeneous part of `sc_expr'.
  const DB_Row<N>& dbm_0 = dbm[0];
  // Speculative allocation of temporaries to be used in the following loop.
  PPL_DIRTY_TEMP(N, coeff_i);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_sc_i);

  // Note: indices above `w' can be disregarded, as they all have
  // a zero coefficient in `sc_expr'.
  for (Linear_Expression::const_iterator i = sc_expr.begin(),
        i_end = sc_expr.lower_bound(Variable(w)); i != i_end; ++i) {
    const Coefficient& sc_i = *i;
    const dimension_type i_dim = i.variable().space_dimension();
    const int sign_i = sgn(sc_i);
    if (sign_i > 0) {
      assign_r(coeff_i, sc_i, ROUND_UP);
      // Approximating `sc_expr'.
      if (pos_pinf_count <= 1) {
        const N& up_approx_i = dbm_0[i_dim];
        if (!is_plus_infinity(up_approx_i)) {
          add_mul_assign_r(pos_sum, coeff_i, up_approx_i, ROUND_UP);
        }
        else {
          ++pos_pinf_count;
          pos_pinf_index = i_dim;
        }
      }
      // Approximating `-sc_expr'.
      if (neg_pinf_count <= 1) {
        const N& up_approx_minus_i = dbm[i_dim][0];
        if (!is_plus_infinity(up_approx_minus_i)) {
          add_mul_assign_r(neg_sum, coeff_i, up_approx_minus_i, ROUND_UP);
        }
        else {
          ++neg_pinf_count;
          neg_pinf_index = i_dim;
        }
      }
    }
    else {
      PPL_ASSERT(sign_i < 0);
      neg_assign(minus_sc_i, sc_i);
      // Note: using temporary named `coeff_i' to store -coeff_i.
      assign_r(coeff_i, minus_sc_i, ROUND_UP);
      // Approximating `sc_expr'.
      if (pos_pinf_count <= 1) {
        const N& up_approx_minus_i = dbm[i_dim][0];
        if (!is_plus_infinity(up_approx_minus_i)) {
          add_mul_assign_r(pos_sum, coeff_i, up_approx_minus_i, ROUND_UP);
        }
        else {
          ++pos_pinf_count;
          pos_pinf_index = i_dim;
        }
      }
      // Approximating `-sc_expr'.
      if (neg_pinf_count <= 1) {
        const N& up_approx_i = dbm_0[i_dim];
        if (!is_plus_infinity(up_approx_i)) {
          add_mul_assign_r(neg_sum, coeff_i, up_approx_i, ROUND_UP);
        }
        else {
          ++neg_pinf_count;
          neg_pinf_index = i_dim;
        }
      }
    }
  }

  // Remove all constraints on 'v'.
  forget_all_dbm_constraints(v);
  // Shortest-path closure is maintained, but not reduction.
  if (marked_shortest_path_reduced()) {
    reset_shortest_path_reduced();
  }
  // Return immediately if no approximation could be computed.
  if (pos_pinf_count > 1 && neg_pinf_count > 1) {
    PPL_ASSERT(OK());
    return;
  }

  // In the following, shortest-path closure will be definitely lost.
  reset_shortest_path_closed();

  // Exploit the upper approximation, if possible.
  if (pos_pinf_count <= 1) {
    // Compute quotient (if needed).
    if (sc_denom != 1) {
      // Before computing quotients, the denominator should be approximated
      // towards zero. Since `sc_denom' is known to be positive, this amounts to
      // rounding downwards, which is achieved as usual by rounding upwards
      // `minus_sc_denom' and negating again the result.
      PPL_DIRTY_TEMP(N, down_sc_denom);
      assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
      neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
      div_assign_r(pos_sum, pos_sum, down_sc_denom, ROUND_UP);
    }
    // Add the upper bound constraint, if meaningful.
    if (pos_pinf_count == 0) {
      // Add the constraint `v <= pos_sum'.
      dbm[0][v] = pos_sum;
      // Deduce constraints of the form `v - u', where `u != v'.
      deduce_v_minus_u_bounds(v, w, sc_expr, sc_denom, pos_sum);
    }  // Here `pos_pinf_count == 1'.
    else if (pos_pinf_index != v
          && sc_expr.get(Variable(pos_pinf_index - 1)) == sc_denom) {
        // Add the constraint `v - pos_pinf_index <= pos_sum'.
        dbm[pos_pinf_index][v] = pos_sum;
      }
  }

  // Exploit the lower approximation, if possible.
  if (neg_pinf_count <= 1) {
    // Compute quotient (if needed).
    if (sc_denom != 1) {
      // Before computing quotients, the denominator should be approximated
      // towards zero. Since `sc_denom' is known to be positive, this amounts to
      // rounding downwards, which is achieved as usual by rounding upwards
      // `minus_sc_denom' and negating again the result.
      PPL_DIRTY_TEMP(N, down_sc_denom);
      assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
      neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
      div_assign_r(neg_sum, neg_sum, down_sc_denom, ROUND_UP);
    }
    // Add the lower bound constraint, if meaningful.
    if (neg_pinf_count == 0) {
      // Add the constraint `v >= -neg_sum', i.e., `-v <= neg_sum'.
      DB_Row<N>& dbm_v = dbm[v];
      dbm_v[0] = neg_sum;
      // Deduce constraints of the form `u - v', where `u != v'.
      deduce_u_minus_v_bounds(v, w, sc_expr, sc_denom, neg_sum);
    }
    // Here `neg_pinf_count == 1'.
    else if (neg_pinf_index != v
          && sc_expr.get(Variable(neg_pinf_index - 1)) == sc_denom) {
        // Add the constraint `v - neg_pinf_index >= -neg_sum',
        // i.e., `neg_pinf_index - v <= neg_sum'.
        dbm[v][neg_pinf_index] = neg_sum;
    }
  }

  PPL_ASSERT(OK());
}

template <typename T>
template <typename Interval_Info>
void
BD_Shape<T>::affine_form_image(const Variable var,
                    const Linear_Form< Interval<T, Interval_Info> >& lf) {

  // Check that T is a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<T>::is_exact,
                    "BD_Shape<T>::affine_form_image(Variable, Linear_Form):"
                    " T not a floating point type.");

  // Dimension-compatibility checks.
  // The dimension of `lf' should not be greater than the dimension
  // of `*this'.
  const dimension_type space_dim = space_dimension();
  const dimension_type lf_space_dim = lf.space_dimension();
  if (space_dim < lf_space_dim) {
    throw_dimension_incompatible("affine_form_image(var_id, l)", "l", lf);
  }
  // `var' should be one of the dimensions of the shape.
  const dimension_type var_id = var.id() + 1;
  if (space_dim < var_id) {
    throw_dimension_incompatible("affine_form_image(var_id, l)", var.id());
  }
  // The image of an empty BDS is empty too.
  shortest_path_closure_assign();
  if (marked_empty()) {
    return;
  }
  // Number of non-zero coefficients in `lf': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;
  // Index of the last non-zero coefficient in `lf', if any.
  dimension_type w_id = 0;
  // Get information about the number of non-zero coefficients in `lf'.
  for (dimension_type i = lf_space_dim; i-- > 0; ) {
    if (lf.coefficient(Variable(i)) != 0) {
      if (t++ == 1) {
        break;
      }
      else {
        w_id = i + 1;
      }
    }
  }
  typedef Interval<T, Interval_Info> FP_Interval_Type;

  const FP_Interval_Type& b = lf.inhomogeneous_term();

  // Now we know the form of `lf':
  // - If t == 0, then lf == b, with `b' a constant;
  // - If t == 1, then lf == a*w + b, where `w' can be `v' or another
  //   variable;
  // - If t == 2, the linear form 'lf' is of the general form.

  if (t == 0) {
    inhomogeneous_affine_form_image(var_id, b);
    PPL_ASSERT(OK());
    return;
  }
  else if (t == 1) {
    const FP_Interval_Type& w_coeff = lf.coefficient(Variable(w_id - 1));
    if (w_coeff == 1 || w_coeff == -1) {
      one_variable_affine_form_image(var_id, b, w_coeff, w_id, space_dim);
      PPL_ASSERT(OK());
      return;
    }
  }
  two_variables_affine_form_image(var_id, lf, space_dim);
  PPL_ASSERT(OK());
}

// Case 1: var = b, where b = [-b_mlb, b_ub]
template <typename T>
template <typename Interval_Info>
void
BD_Shape<T>
::inhomogeneous_affine_form_image(const dimension_type& var_id,
                                  const Interval<T, Interval_Info>& b) {
  PPL_DIRTY_TEMP(N, b_ub);
  assign_r(b_ub, b.upper(), ROUND_NOT_NEEDED);
  PPL_DIRTY_TEMP(N, b_mlb);
  neg_assign_r(b_mlb, b.lower(), ROUND_NOT_NEEDED);

  // Remove all constraints on `var'.
  forget_all_dbm_constraints(var_id);
  // Shortest-path closure is preserved, but not reduction.
  if (marked_shortest_path_reduced()) {
    reset_shortest_path_reduced();
  }
  // Add the constraint `var >= lb && var <= ub'.
  add_dbm_constraint(0, var_id, b_ub);
  add_dbm_constraint(var_id, 0, b_mlb);
  return;
}

// case 2: var = (+/-1) * w + [-b_mlb, b_ub], where `w' can be `var'
// or another variable.
template <typename T>
template <typename Interval_Info>
void BD_Shape<T>
::one_variable_affine_form_image(const dimension_type& var_id,
                            const Interval<T, Interval_Info>& b,
                            const Interval<T, Interval_Info>& w_coeff,
                            const dimension_type& w_id,
                            const dimension_type& space_dim) {

  PPL_DIRTY_TEMP(N, b_ub);
  assign_r(b_ub, b.upper(), ROUND_NOT_NEEDED);
  PPL_DIRTY_TEMP(N, b_mlb);
  neg_assign_r(b_mlb, b.lower(), ROUND_NOT_NEEDED);

  // True if `w_coeff' is in [1, 1].
  bool is_w_coeff_one = (w_coeff == 1);

  if (w_id == var_id) {
    // True if `b' is in [b_mlb, b_ub] and that is [0, 0].
    bool is_b_zero = (b_mlb == 0 && b_ub == 0);
    // Here `lf' is of the form: [+/-1, +/-1] * v + b.
    if (is_w_coeff_one) {
      if (is_b_zero) {
        // The transformation is the identity function.
        return;
      }
      else {
        // Translate all the constraints on `var' by adding the value
        // `b_ub' or subtracting the value `b_mlb'.
        DB_Row<N>& dbm_v = dbm[var_id];
        for (dimension_type i = space_dim + 1; i-- > 0; ) {
          N& dbm_vi = dbm_v[i];
          add_assign_r(dbm_vi, dbm_vi, b_mlb, ROUND_UP);
          N& dbm_iv = dbm[i][var_id];
          add_assign_r(dbm_iv, dbm_iv, b_ub, ROUND_UP);
        }
        // Both shortest-path closure and reduction are preserved.
      }
    }
    else {
      // Here `w_coeff = [-1, -1].
      // Remove the binary constraints on `var'.
      forget_binary_dbm_constraints(var_id);
      using std::swap;
      swap(dbm[var_id][0], dbm[0][var_id]);
      // Shortest-path closure is not preserved.
      reset_shortest_path_closed();
      if (!is_b_zero) {
        // Translate the unary constraints on `var' by adding the value
        // `b_ub' or subtracting the value `b_mlb'.
        N& dbm_v0 = dbm[var_id][0];
        add_assign_r(dbm_v0, dbm_v0, b_mlb, ROUND_UP);
        N& dbm_0v = dbm[0][var_id];
        add_assign_r(dbm_0v, dbm_0v, b_ub, ROUND_UP);
      }
    }
  }
  else {
    // Here `w != var', so that `lf' is of the form
    // [+/-1, +/-1] * w + b.
    // Remove all constraints on `var'.
    forget_all_dbm_constraints(var_id);
    // Shortest-path closure is preserved, but not reduction.
    if (marked_shortest_path_reduced()) {
      reset_shortest_path_reduced();
    }
    if (is_w_coeff_one) {
      // Add the new constraints `var - w >= b_mlb'
      // `and var - w <= b_ub'.
      add_dbm_constraint(w_id, var_id, b_ub);
      add_dbm_constraint(var_id, w_id, b_mlb);
    }
    else {
      // We have to add the constraint `v + w == b', over-approximating it
      // by computing lower and upper bounds for `w'.
      const N& mlb_w = dbm[w_id][0];
      if (!is_plus_infinity(mlb_w)) {
        // Add the constraint `v <= ub - lb_w'.
        add_assign_r(dbm[0][var_id], b_ub, mlb_w, ROUND_UP);
        reset_shortest_path_closed();
      }
      const N& ub_w = dbm[0][w_id];
      if (!is_plus_infinity(ub_w)) {
        // Add the constraint `v >= lb - ub_w'.
        add_assign_r(dbm[var_id][0], ub_w, b_mlb, ROUND_UP);
        reset_shortest_path_closed();
      }
    }
  }
  return;
}

// General case.
// Either t == 2, so that
// lf == i_1*x_1 + i_2*x_2 + ... + i_n*x_n + b, where n >= 2,
// or t == 1, lf == i*w + b, but i <> [+/-1, +/-1].
template <typename T>
template <typename Interval_Info>
void BD_Shape<T>
::two_variables_affine_form_image(const dimension_type& var_id,
           const Linear_Form< Interval<T, Interval_Info> >& lf,
                             const dimension_type& space_dim) {
  // Shortest-path closure is maintained, but not reduction.
  if (marked_shortest_path_reduced()) {
    reset_shortest_path_reduced();
  }
  reset_shortest_path_closed();

  Linear_Form< Interval<T, Interval_Info> > minus_lf(lf);
  minus_lf.negate();

  // Declare temporaries outside the loop.
  PPL_DIRTY_TEMP(N, upper_bound);

  // Update binary constraints on var FIRST.
  for (dimension_type curr_var = 1; curr_var < var_id; ++curr_var) {
    Variable current(curr_var - 1);
    linear_form_upper_bound(lf - current, upper_bound);
    assign_r(dbm[curr_var][var_id], upper_bound, ROUND_NOT_NEEDED);
    linear_form_upper_bound(minus_lf + current, upper_bound);
    assign_r(dbm[var_id][curr_var], upper_bound, ROUND_NOT_NEEDED);
  }
  for (dimension_type curr_var = var_id + 1; curr_var <= space_dim;
                                                      ++curr_var) {
    Variable current(curr_var - 1);
    linear_form_upper_bound(lf - current, upper_bound);
    assign_r(dbm[curr_var][var_id], upper_bound, ROUND_NOT_NEEDED);
    linear_form_upper_bound(minus_lf + current, upper_bound);
    assign_r(dbm[var_id][curr_var], upper_bound, ROUND_NOT_NEEDED);
  }
  // Finally, update unary constraints on var.
  PPL_DIRTY_TEMP(N, lf_ub);
  linear_form_upper_bound(lf, lf_ub);
  PPL_DIRTY_TEMP(N, minus_lf_ub);
  linear_form_upper_bound(minus_lf, minus_lf_ub);
  assign_r(dbm[0][var_id], lf_ub, ROUND_NOT_NEEDED);
  assign_r(dbm[var_id][0], minus_lf_ub, ROUND_NOT_NEEDED);
}

template <typename T>
template <typename Interval_Info>
void BD_Shape<T>::refine_with_linear_form_inequality(
                   const Linear_Form< Interval<T, Interval_Info> >& left,
                   const Linear_Form< Interval<T, Interval_Info> >& right) {
    // Check that T is a floating point type.
    PPL_COMPILE_TIME_CHECK(!std::numeric_limits<T>::is_exact,
                    "Octagonal_Shape<T>::refine_with_linear_form_inequality:"
                    " T not a floating point type.");

    //We assume that the analyzer will not try to apply an unreachable filter.
    PPL_ASSERT(!marked_empty());

    // Dimension-compatibility checks.
    // The dimensions of `left' and `right' should not be greater than the
    // dimension of `*this'.
    const dimension_type left_space_dim = left.space_dimension();
    const dimension_type space_dim = space_dimension();
    if (space_dim < left_space_dim) {
      throw_dimension_incompatible(
          "refine_with_linear_form_inequality(left, right)", "left", left);
    }
    const dimension_type right_space_dim = right.space_dimension();
    if (space_dim < right_space_dim) {
      throw_dimension_incompatible(
          "refine_with_linear_form_inequality(left, right)", "right", right);
    }
  // Number of non-zero coefficients in `left': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type left_t = 0;
  // Variable-index of the last non-zero coefficient in `left', if any.
  dimension_type left_w_id = 0;
  // Number of non-zero coefficients in `right': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type right_t = 0;
  // Variable-index of the last non-zero coefficient in `right', if any.
  dimension_type right_w_id = 0;

  typedef Interval<T, Interval_Info> FP_Interval_Type;

  // Get information about the number of non-zero coefficients in `left'.
  for (dimension_type i = left_space_dim; i-- > 0; ) {
    if (left.coefficient(Variable(i)) != 0) {
      if (left_t++ == 1) {
        break;
      }
      else {
        left_w_id = i;
      }
    }
  }

  // Get information about the number of non-zero coefficients in `right'.
  for (dimension_type i = right_space_dim; i-- > 0; ) {
    if (right.coefficient(Variable(i)) != 0) {
      if (right_t++ == 1) {
        break;
      }
      else {
        right_w_id = i;
      }
    }
  }

  const FP_Interval_Type& left_w_coeff =
          left.coefficient(Variable(left_w_id));
  const FP_Interval_Type& right_w_coeff =
          right.coefficient(Variable(right_w_id));

  if (left_t == 0) {
    if (right_t == 0) {
      // The constraint involves constants only. Ignore it: it is up to
      // the analyzer to handle it.
      PPL_ASSERT(OK());
      return;
    }
    else if (right_w_coeff == 1 || right_w_coeff == -1) {
      left_inhomogeneous_refine(right_t, right_w_id, left, right);
      PPL_ASSERT(OK());
      return;
    }
  }
  else if (left_t == 1) {
    if (left_w_coeff == 1 || left_w_coeff == -1) {
      if (right_t == 0 || (right_w_coeff == 1 || right_w_coeff == -1)) {
        left_one_var_refine(left_w_id, right_t, right_w_id, left, right);
        PPL_ASSERT(OK());
        return;
      }
    }
  }

  // General case.
  general_refine(left_w_id, right_w_id, left, right);
  PPL_ASSERT(OK());
} // end of refine_with_linear_form_inequality

template <typename T>
template <typename U>
void
BD_Shape<T>
::export_interval_constraints(U& dest) const {
  const dimension_type space_dim = space_dimension();
  if (space_dim > dest.space_dimension()) {
    throw std::invalid_argument(
               "BD_Shape<T>::export_interval_constraints");
  }

  // Expose all the interval constraints.
  shortest_path_closure_assign();

  if (marked_empty()) {
    dest.set_empty();
    PPL_ASSERT(OK());
    return;
  }

  PPL_DIRTY_TEMP(N, tmp);
  const DB_Row<N>& dbm_0 = dbm[0];
  for (dimension_type i = space_dim; i-- > 0; ) {
    // Set the upper bound.
    const N& u = dbm_0[i+1];
    if (!is_plus_infinity(u)) {
      if (!dest.restrict_upper(i, u.raw_value())) {
        return;
      }
    }
    // Set the lower bound.
    const N& negated_l = dbm[i+1][0];
    if (!is_plus_infinity(negated_l)) {
      neg_assign_r(tmp, negated_l, ROUND_DOWN);
      if (!dest.restrict_lower(i, tmp.raw_value())) {
        return;
      }
    }
  }

  PPL_ASSERT(OK());
}

template <typename T>
template <typename Interval_Info>
void
BD_Shape<T>::left_inhomogeneous_refine(const dimension_type& right_t,
                                       const dimension_type& right_w_id,
                    const Linear_Form< Interval<T, Interval_Info> >& left,
                    const Linear_Form< Interval<T, Interval_Info> >& right) {

  typedef Interval<T, Interval_Info> FP_Interval_Type;

  if (right_t == 1) {
    // The constraint has the form [a-, a+] <= [b-, b+] + [c-, c+] * x.
    // Reduce it to the constraint +/-x <= b+ - a- if [c-, c+] = +/-[1, 1].
      const FP_Interval_Type& right_w_coeff =
                              right.coefficient(Variable(right_w_id));
      if (right_w_coeff == 1) {
        PPL_DIRTY_TEMP(N, b_plus_minus_a_minus);
        const FP_Interval_Type& left_a = left.inhomogeneous_term();
        const FP_Interval_Type& right_b = right.inhomogeneous_term();
        sub_assign_r(b_plus_minus_a_minus, right_b.upper(), left_a.lower(),
                     ROUND_UP);
        add_dbm_constraint(right_w_id+1, 0, b_plus_minus_a_minus);
        return;
      }

      if (right_w_coeff == -1) {
        PPL_DIRTY_TEMP(N, b_plus_minus_a_minus);
        const FP_Interval_Type& left_a = left.inhomogeneous_term();
        const FP_Interval_Type& right_b = right.inhomogeneous_term();
        sub_assign_r(b_plus_minus_a_minus, right_b.upper(), left_a.lower(),
                     ROUND_UP);
        add_dbm_constraint(0, right_w_id+1, b_plus_minus_a_minus);
        return;
      }
    }
} // end of left_inhomogeneous_refine


template <typename T>
template <typename Interval_Info>
void
BD_Shape<T>
::left_one_var_refine(const dimension_type& left_w_id,
                      const dimension_type& right_t,
                      const dimension_type& right_w_id,
                const Linear_Form< Interval<T, Interval_Info> >& left,
                const Linear_Form< Interval<T, Interval_Info> >& right) {

  typedef Interval<T, Interval_Info> FP_Interval_Type;

    if (right_t == 0) {
      // The constraint has the form [b-, b+] + [c-, c+] * x <= [a-, a+]
      // Reduce it to the constraint +/-x <= a+ - b- if [c-, c+] = +/-[1, 1].
      const FP_Interval_Type& left_w_coeff =
        left.coefficient(Variable(left_w_id));

      if (left_w_coeff == 1) {
        PPL_DIRTY_TEMP(N, a_plus_minus_b_minus);
        const FP_Interval_Type& left_b = left.inhomogeneous_term();
        const FP_Interval_Type& right_a = right.inhomogeneous_term();
        sub_assign_r(a_plus_minus_b_minus, right_a.upper(), left_b.lower(),
                     ROUND_UP);
        add_dbm_constraint(0, left_w_id+1, a_plus_minus_b_minus);
        return;
      }

      if (left_w_coeff == -1) {
        PPL_DIRTY_TEMP(N, a_plus_minus_b_minus);
        const FP_Interval_Type& left_b = left.inhomogeneous_term();
        const FP_Interval_Type& right_a = right.inhomogeneous_term();
        sub_assign_r(a_plus_minus_b_minus, right_a.upper(), left_b.lower(),
                     ROUND_UP);
        add_dbm_constraint(left_w_id+1, 0, a_plus_minus_b_minus);
        return;
      }
    }
    else if (right_t == 1) {
      // The constraint has the form
      // [a-, a+] + [b-, b+] * x <= [c-, c+] + [d-, d+] * y.
      // Reduce it to the constraint +/-x +/-y <= c+ - a-
      // if [b-, b+] = +/-[1, 1] and [d-, d+] = +/-[1, 1].
      const FP_Interval_Type& left_w_coeff =
                              left.coefficient(Variable(left_w_id));

      const FP_Interval_Type& right_w_coeff =
                              right.coefficient(Variable(right_w_id));

      bool is_left_coeff_one = (left_w_coeff == 1);
      bool is_left_coeff_minus_one = (left_w_coeff == -1);
      bool is_right_coeff_one = (right_w_coeff == 1);
      bool is_right_coeff_minus_one = (right_w_coeff == -1);
      if (left_w_id == right_w_id) {
        if ((is_left_coeff_one && is_right_coeff_one)
            ||
            (is_left_coeff_minus_one && is_right_coeff_minus_one)) {
          // Here we have an identity or a constants-only constraint.
          return;
        }
        if (is_left_coeff_one && is_right_coeff_minus_one) {
          // We fall back to a previous case.
          PPL_DIRTY_TEMP(N, a_plus_minus_b_minus);
          const FP_Interval_Type& left_b = left.inhomogeneous_term();
          const FP_Interval_Type& right_a = right.inhomogeneous_term();
          sub_assign_r(a_plus_minus_b_minus, right_a.upper(), left_b.lower(),
                       ROUND_UP);
          div_2exp_assign_r(a_plus_minus_b_minus, a_plus_minus_b_minus, 1,
                            ROUND_UP);
          add_dbm_constraint(0, left_w_id + 1, a_plus_minus_b_minus);
          return;
        }
        if (is_left_coeff_minus_one && is_right_coeff_one) {
          // We fall back to a previous case.
          PPL_DIRTY_TEMP(N, a_plus_minus_b_minus);
          const FP_Interval_Type& left_b = left.inhomogeneous_term();
          const FP_Interval_Type& right_a = right.inhomogeneous_term();
          sub_assign_r(a_plus_minus_b_minus, right_a.upper(), left_b.lower(),
                       ROUND_UP);
          div_2exp_assign_r(a_plus_minus_b_minus, a_plus_minus_b_minus, 1,
                            ROUND_UP);
          add_dbm_constraint(right_w_id + 1, 0, a_plus_minus_b_minus);
          return;
        }
      }
      else if (is_left_coeff_minus_one && is_right_coeff_one) {
        // over-approximate (if is it possible) the inequality
        // -B + [b1, b2] <= A + [a1, a2] by adding the constraints
        // -B <= upper_bound(A) + (a2 - b1) and
        // -A <= upper_bound(B) + (a2 - b1)
        PPL_DIRTY_TEMP(N, a_plus_minus_b_minus);
        const FP_Interval_Type& left_b = left.inhomogeneous_term();
        const FP_Interval_Type& right_a = right.inhomogeneous_term();
        sub_assign_r(a_plus_minus_b_minus, right_a.upper(), left_b.lower(),
                       ROUND_UP);
        PPL_DIRTY_TEMP(N, ub);
        ub = dbm[0][right_w_id + 1];
        if (!is_plus_infinity(ub)) {
          add_assign_r(ub, ub, a_plus_minus_b_minus, ROUND_UP);
          add_dbm_constraint(left_w_id + 1, 0, ub);
        }
        ub = dbm[0][left_w_id + 1];
        if (!is_plus_infinity(ub)) {
          add_assign_r(ub, ub, a_plus_minus_b_minus, ROUND_UP);
          add_dbm_constraint(right_w_id + 1, 0, ub);
        }
        return;
      }
      if (is_left_coeff_one && is_right_coeff_minus_one) {
        // over-approximate (if is it possible) the inequality
        // B + [b1, b2] <= -A + [a1, a2] by adding the constraints
        // B <= upper_bound(-A) + (a2 - b1) and
        // A <= upper_bound(-B) + (a2 - b1)
        PPL_DIRTY_TEMP(N, a_plus_minus_b_minus);
        const FP_Interval_Type& left_b = left.inhomogeneous_term();
        const FP_Interval_Type& right_a = right.inhomogeneous_term();
        sub_assign_r(a_plus_minus_b_minus, right_a.upper(), left_b.lower(),
                       ROUND_UP);
        PPL_DIRTY_TEMP(N, ub);
        ub = dbm[right_w_id + 1][0];
        if (!is_plus_infinity(ub)) {
          add_assign_r(ub, ub, a_plus_minus_b_minus, ROUND_UP);
          add_dbm_constraint(0, left_w_id + 1, ub);
        }
        ub = dbm[left_w_id + 1][0];
        if (!is_plus_infinity(ub)) {
          add_assign_r(ub, ub, a_plus_minus_b_minus, ROUND_UP);
          add_dbm_constraint(0, right_w_id + 1, ub);
        }
            return;
      }
      if (is_left_coeff_one && is_right_coeff_one) {
        PPL_DIRTY_TEMP(N, c_plus_minus_a_minus);
        const FP_Interval_Type& left_a = left.inhomogeneous_term();
        const FP_Interval_Type& right_c = right.inhomogeneous_term();
        sub_assign_r(c_plus_minus_a_minus, right_c.upper(), left_a.lower(),
                     ROUND_UP);
        add_dbm_constraint(right_w_id+1, left_w_id+1, c_plus_minus_a_minus);
        return;
      }
      if (is_left_coeff_minus_one && is_right_coeff_minus_one) {
        PPL_DIRTY_TEMP(N, c_plus_minus_a_minus);
        const FP_Interval_Type& left_a = left.inhomogeneous_term();
        const FP_Interval_Type& right_c = right.inhomogeneous_term();
        sub_assign_r(c_plus_minus_a_minus, right_c.upper(), left_a.lower(),
                     ROUND_UP);
        add_dbm_constraint(left_w_id+1, right_w_id+1, c_plus_minus_a_minus);
        return;
      }
    }
}

template <typename T>
template <typename Interval_Info>
void
BD_Shape<T>
::general_refine(const dimension_type& left_w_id,
                 const dimension_type& right_w_id,
                 const Linear_Form< Interval<T, Interval_Info> >& left,
                 const Linear_Form< Interval<T, Interval_Info> >& right) {

  typedef Interval<T, Interval_Info> FP_Interval_Type;
  Linear_Form<FP_Interval_Type> right_minus_left(right);
  right_minus_left -= left;

  // Declare temporaries outside of the loop.
  PPL_DIRTY_TEMP(N, low_coeff);
  PPL_DIRTY_TEMP(N, high_coeff);
  PPL_DIRTY_TEMP(N, upper_bound);

  dimension_type max_w_id = std::max(left_w_id, right_w_id);

  for (dimension_type first_v = 0; first_v < max_w_id; ++first_v) {
    for (dimension_type second_v = first_v+1;
         second_v <= max_w_id; ++second_v) {
      const FP_Interval_Type& lfv_coefficient =
        left.coefficient(Variable(first_v));
      const FP_Interval_Type& lsv_coefficient =
        left.coefficient(Variable(second_v));
      const FP_Interval_Type& rfv_coefficient =
        right.coefficient(Variable(first_v));
      const FP_Interval_Type& rsv_coefficient =
        right.coefficient(Variable(second_v));
      // We update the constraints only when both variables appear in at
      // least one argument.
      bool do_update = false;
      assign_r(low_coeff, lfv_coefficient.lower(), ROUND_NOT_NEEDED);
      assign_r(high_coeff, lfv_coefficient.upper(), ROUND_NOT_NEEDED);
      if (low_coeff != 0 || high_coeff != 0) {
        assign_r(low_coeff, lsv_coefficient.lower(), ROUND_NOT_NEEDED);
        assign_r(high_coeff, lsv_coefficient.upper(), ROUND_NOT_NEEDED);
        if (low_coeff != 0 || high_coeff != 0) {
          do_update = true;
        }
        else {
          assign_r(low_coeff, rsv_coefficient.lower(), ROUND_NOT_NEEDED);
          assign_r(high_coeff, rsv_coefficient.upper(), ROUND_NOT_NEEDED);
          if (low_coeff != 0 || high_coeff != 0) {
            do_update = true;
          }
        }
      }
      else {
        assign_r(low_coeff, rfv_coefficient.lower(), ROUND_NOT_NEEDED);
        assign_r(high_coeff, rfv_coefficient.upper(), ROUND_NOT_NEEDED);
        if (low_coeff != 0 || high_coeff != 0) {
          assign_r(low_coeff, lsv_coefficient.lower(), ROUND_NOT_NEEDED);
          assign_r(high_coeff, lsv_coefficient.upper(), ROUND_NOT_NEEDED);
          if (low_coeff != 0 || high_coeff != 0) {
            do_update = true;
          }
          else {
            assign_r(low_coeff, rsv_coefficient.lower(), ROUND_NOT_NEEDED);
            assign_r(high_coeff, rsv_coefficient.upper(), ROUND_NOT_NEEDED);
            if (low_coeff != 0 || high_coeff != 0) {
              do_update = true;
            }
          }
        }
      }

      if (do_update) {
        Variable first(first_v);
        Variable second(second_v);
        dimension_type n_first_var = first_v +1 ;
        dimension_type n_second_var = second_v + 1;
        linear_form_upper_bound(right_minus_left - first + second,
                                upper_bound);
        add_dbm_constraint(n_first_var, n_second_var, upper_bound);
        linear_form_upper_bound(right_minus_left + first - second,
                                upper_bound);
        add_dbm_constraint(n_second_var, n_first_var, upper_bound);
      }
    }
  }

  // Finally, update the unary constraints.
  for (dimension_type v = 0; v < max_w_id; ++v) {
    const FP_Interval_Type& lv_coefficient =
      left.coefficient(Variable(v));
    const FP_Interval_Type& rv_coefficient =
      right.coefficient(Variable(v));
    // We update the constraints only if v appears in at least one of the
    // two arguments.
    bool do_update = false;
    assign_r(low_coeff, lv_coefficient.lower(), ROUND_NOT_NEEDED);
    assign_r(high_coeff, lv_coefficient.upper(), ROUND_NOT_NEEDED);
    if (low_coeff != 0 || high_coeff != 0) {
      do_update = true;
    }
    else {
      assign_r(low_coeff, rv_coefficient.lower(), ROUND_NOT_NEEDED);
      assign_r(high_coeff, rv_coefficient.upper(), ROUND_NOT_NEEDED);
      if (low_coeff != 0 || high_coeff != 0) {
        do_update = true;
      }
    }

    if (do_update) {
      Variable var(v);
      dimension_type n_var = v + 1;
      linear_form_upper_bound(right_minus_left + var, upper_bound);
      add_dbm_constraint(0, n_var, upper_bound);
      linear_form_upper_bound(right_minus_left - var, upper_bound);
      add_dbm_constraint(n_var, 0, upper_bound);
    }
  }

}

template <typename T>
template <typename Interval_Info>
void
BD_Shape<T>::
linear_form_upper_bound(const Linear_Form< Interval<T, Interval_Info> >& lf,
                        N& result) const {

  // Check that T is a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<T>::is_exact,
                     "BD_Shape<T>::linear_form_upper_bound:"
                     " T not a floating point type.");

  const dimension_type lf_space_dimension = lf.space_dimension();
  PPL_ASSERT(lf_space_dimension <= space_dimension());

  typedef Interval<T, Interval_Info> FP_Interval_Type;

  PPL_DIRTY_TEMP(N, curr_lb);
  PPL_DIRTY_TEMP(N, curr_ub);
  PPL_DIRTY_TEMP(N, curr_var_ub);
  PPL_DIRTY_TEMP(N, curr_minus_var_ub);

  PPL_DIRTY_TEMP(N, first_comparison_term);
  PPL_DIRTY_TEMP(N, second_comparison_term);

  PPL_DIRTY_TEMP(N, negator);

  assign_r(result, lf.inhomogeneous_term().upper(), ROUND_NOT_NEEDED);

  for (dimension_type curr_var = 0, n_var = 0; curr_var < lf_space_dimension;
       ++curr_var) {
    n_var = curr_var + 1;
    const FP_Interval_Type&
      curr_coefficient = lf.coefficient(Variable(curr_var));
    assign_r(curr_lb, curr_coefficient.lower(), ROUND_NOT_NEEDED);
    assign_r(curr_ub, curr_coefficient.upper(), ROUND_NOT_NEEDED);
    if (curr_lb != 0 || curr_ub != 0) {
      assign_r(curr_var_ub, dbm[0][n_var], ROUND_NOT_NEEDED);
      neg_assign_r(curr_minus_var_ub, dbm[n_var][0], ROUND_NOT_NEEDED);
      // Optimize the most commons cases: curr = +/-[1, 1].
      if (curr_lb == 1 && curr_ub == 1) {
        add_assign_r(result, result, std::max(curr_var_ub, curr_minus_var_ub),
                     ROUND_UP);
      }
      else if (curr_lb == -1 && curr_ub == -1) {
        neg_assign_r(negator, std::min(curr_var_ub, curr_minus_var_ub),
                     ROUND_NOT_NEEDED);
        add_assign_r(result, result, negator, ROUND_UP);
      }
      else {
        // Next addend will be the maximum of four quantities.
        assign_r(first_comparison_term, 0, ROUND_NOT_NEEDED);
        assign_r(second_comparison_term, 0, ROUND_NOT_NEEDED);
        add_mul_assign_r(first_comparison_term, curr_var_ub, curr_ub,
                         ROUND_UP);
        add_mul_assign_r(second_comparison_term, curr_var_ub, curr_lb,
                         ROUND_UP);
        assign_r(first_comparison_term, std::max(first_comparison_term,
                                                 second_comparison_term),
                 ROUND_NOT_NEEDED);
        assign_r(second_comparison_term, 0, ROUND_NOT_NEEDED);
        add_mul_assign_r(second_comparison_term, curr_minus_var_ub, curr_ub,
                         ROUND_UP);
        assign_r(first_comparison_term, std::max(first_comparison_term,
                                                 second_comparison_term),
                 ROUND_NOT_NEEDED);
        assign_r(second_comparison_term, 0, ROUND_NOT_NEEDED);
        add_mul_assign_r(second_comparison_term, curr_minus_var_ub, curr_lb,
                         ROUND_UP);
        assign_r(first_comparison_term, std::max(first_comparison_term,
                                                 second_comparison_term),
                 ROUND_NOT_NEEDED);

        add_assign_r(result, result, first_comparison_term, ROUND_UP);
      }
    }
  }
}

template <typename T>
void
BD_Shape<T>::affine_preimage(const Variable var,
                             const Linear_Expression& expr,
                             Coefficient_traits::const_reference denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("affine_preimage(v, e, d)", "d == 0");
  }
  // Dimension-compatibility checks.
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type space_dim = space_dimension();
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible("affine_preimage(v, e, d)", "e", expr);
  }
  // `var' should be one of the dimensions of
  // the bounded difference shapes.
  const dimension_type v = var.id() + 1;
  if (v > space_dim) {
    throw_dimension_incompatible("affine_preimage(v, e, d)", var.id());
  }
  // The image of an empty BDS is empty too.
  shortest_path_closure_assign();
  if (marked_empty()) {
    return;
  }
  const Coefficient& b = expr.inhomogeneous_term();
  // Number of non-zero coefficients in `expr': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;
  // Index of the last non-zero coefficient in `expr', if any.
  dimension_type j = expr.last_nonzero();

  if (j != 0) {
    ++t;
    if (!expr.all_zeroes(1, j)) {
      ++t;
    }
  }

  // Now we know the form of `expr':
  // - If t == 0, then expr = b, with `b' a constant;
  // - If t == 1, then expr = a*w + b, where `w' can be `v' or another
  //   variable; in this second case we have to check whether `a' is
  //   equal to `denominator' or `-denominator', since otherwise we have
  //   to fall back on the general form;
  // - If t > 1, the `expr' is of the general form.
  if (t == 0) {
    // Case 1: expr = n; remove all constraints on `var'.
    forget_all_dbm_constraints(v);
    // Shortest-path closure is preserved, but not reduction.
    if (marked_shortest_path_reduced()) {
      reset_shortest_path_reduced();
    }
    PPL_ASSERT(OK());
    return;
  }

  if (t == 1) {
    // Value of the one and only non-zero coefficient in `expr'.
    const Coefficient& a = expr.get(Variable(j - 1));
    if (a == denominator || a == -denominator) {
      // Case 2: expr = a*w + b, with a = +/- denominator.
      if (j == var.space_dimension()) {
        // Apply affine_image() on the inverse of this transformation.
        affine_image(var, denominator*var - b, a);
      }
      else {
        // `expr == a*w + b', where `w != v'.
        // Remove all constraints on `var'.
        forget_all_dbm_constraints(v);
        // Shortest-path closure is preserved, but not reduction.
        if (marked_shortest_path_reduced()) {
          reset_shortest_path_reduced();
        }
        PPL_ASSERT(OK());
      }
      return;
    }
  }

  // General case.
  // Either t == 2, so that
  // expr = a_1*x_1 + a_2*x_2 + ... + a_n*x_n + b, where n >= 2,
  // or t = 1, expr = a*w + b, but a <> +/- denominator.
  const Coefficient& expr_v = expr.coefficient(var);
  if (expr_v != 0) {
    // The transformation is invertible.
    Linear_Expression inverse((expr_v + denominator)*var);
    inverse -= expr;
    affine_image(var, inverse, expr_v);
  }
  else {
    // Transformation not invertible: all constraints on `var' are lost.
    forget_all_dbm_constraints(v);
    // Shortest-path closure is preserved, but not reduction.
    if (marked_shortest_path_reduced()) {
      reset_shortest_path_reduced();
    }
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>
::bounded_affine_image(const Variable var,
                       const Linear_Expression& lb_expr,
                       const Linear_Expression& ub_expr,
                       Coefficient_traits::const_reference denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("bounded_affine_image(v, lb, ub, d)", "d == 0");
  }
  // Dimension-compatibility checks.
  // `var' should be one of the dimensions of the BD_Shape.
  const dimension_type bds_space_dim = space_dimension();
  const dimension_type v = var.id() + 1;
  if (v > bds_space_dim) {
    throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)",
                                 "v", var);
  }
  // The dimension of `lb_expr' and `ub_expr' should not be
  // greater than the dimension of `*this'.
  const dimension_type lb_space_dim = lb_expr.space_dimension();
  if (bds_space_dim < lb_space_dim) {
    throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)",
                                 "lb", lb_expr);
  }
  const dimension_type ub_space_dim = ub_expr.space_dimension();
  if (bds_space_dim < ub_space_dim) {
    throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)",
                                 "ub", ub_expr);
  }
  // Any image of an empty BDS is empty.
  shortest_path_closure_assign();
  if (marked_empty()) {
    return;
  }
  const Coefficient& b = ub_expr.inhomogeneous_term();
  // Number of non-zero coefficients in `ub_expr': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;
  // Index of the last non-zero coefficient in `ub_expr', if any.
  dimension_type w = ub_expr.last_nonzero();

  if (w != 0) {
    ++t;
    if (!ub_expr.all_zeroes(1, w)) {
      ++t;
    }
  }

  // Now we know the form of `ub_expr':
  // - If t == 0, then ub_expr == b, with `b' a constant;
  // - If t == 1, then ub_expr == a*w + b, where `w' can be `v' or another
  //   variable; in this second case we have to check whether `a' is
  //   equal to `denominator' or `-denominator', since otherwise we have
  //   to fall back on the general form;
  // - If t == 2, the `ub_expr' is of the general form.
  PPL_DIRTY_TEMP_COEFFICIENT(minus_denom);
  neg_assign(minus_denom, denominator);

  if (t == 0) {
    // Case 1: ub_expr == b.
    generalized_affine_image(var,
                             GREATER_OR_EQUAL,
                             lb_expr,
                             denominator);
    // Add the constraint `var <= b/denominator'.
    add_dbm_constraint(0, v, b, denominator);
    PPL_ASSERT(OK());
    return;
  }

  if (t == 1) {
    // Value of the one and only non-zero coefficient in `ub_expr'.
    const Coefficient& a = ub_expr.get(Variable(w - 1));
    if (a == denominator || a == minus_denom) {
      // Case 2: expr == a*w + b, with a == +/- denominator.
      if (w == v) {
        // Here `var' occurs in `ub_expr'.
        // To ease the computation, we add an additional dimension.
        const Variable new_var(bds_space_dim);
        add_space_dimensions_and_embed(1);
        // Constrain the new dimension to be equal to `ub_expr'.
        affine_image(new_var, ub_expr, denominator);
        // NOTE: enforce shortest-path closure for precision.
        shortest_path_closure_assign();
        PPL_ASSERT(!marked_empty());
        // Apply the affine lower bound.
        generalized_affine_image(var,
                                 GREATER_OR_EQUAL,
                                 lb_expr,
                                 denominator);
        // Now apply the affine upper bound, as recorded in `new_var'.
        add_constraint(var <= new_var);
        // Remove the temporarily added dimension.
        remove_higher_space_dimensions(bds_space_dim);
        return;
      }
      else {
        // Here `w != v', so that `expr' is of the form
        // +/-denominator * w + b.
        // Apply the affine lower bound.
        generalized_affine_image(var,
                                 GREATER_OR_EQUAL,
                                 lb_expr,
                                 denominator);
        if (a == denominator) {
          // Add the new constraint `v - w == b/denominator'.
          add_dbm_constraint(w, v, b, denominator);
        }
        else {
          // Here a == -denominator, so that we should be adding
          // the constraint `v + w == b/denominator'.
          // Approximate it by computing lower and upper bounds for `w'.
          const N& dbm_w0 = dbm[w][0];
          if (!is_plus_infinity(dbm_w0)) {
            // Add the constraint `v <= b/denominator - lower_w'.
            PPL_DIRTY_TEMP(N, d);
            div_round_up(d, b, denominator);
            add_assign_r(dbm[0][v], d, dbm_w0, ROUND_UP);
            reset_shortest_path_closed();
          }
        }
        PPL_ASSERT(OK());
        return;
      }
    }
  }

  // General case.
  // Either t == 2, so that
  // ub_expr == a_1*x_1 + a_2*x_2 + ... + a_n*x_n + b, where n >= 2,
  // or t == 1, ub_expr == a*w + b, but a <> +/- denominator.
  // We will remove all the constraints on `var' and add back
  // constraints providing upper and lower bounds for `var'.

  // Compute upper approximations for `ub_expr' into `pos_sum'
  // taking into account the sign of `denominator'.
  const bool is_sc = (denominator > 0);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_b);
  neg_assign(minus_b, b);
  const Coefficient& sc_b = is_sc ? b : minus_b;
  const Coefficient& sc_denom = is_sc ? denominator : minus_denom;
  const Coefficient& minus_sc_denom = is_sc ? minus_denom : denominator;
  // NOTE: here, for optimization purposes, `minus_expr' is only assigned
  // when `denominator' is negative. Do not use it unless you are sure
  // it has been correctly assigned.
  Linear_Expression minus_expr;
  if (!is_sc) {
    minus_expr = -ub_expr;
  }
  const Linear_Expression& sc_expr = is_sc ? ub_expr : minus_expr;

  PPL_DIRTY_TEMP(N, pos_sum);
  // Index of the variable that are unbounded in `this->dbm'.
  PPL_UNINITIALIZED(dimension_type, pos_pinf_index);
  // Number of unbounded variables found.
  dimension_type pos_pinf_count = 0;

  // Approximate the inhomogeneous term.
  assign_r(pos_sum, sc_b, ROUND_UP);

  // Approximate the homogeneous part of `sc_expr'.
  const DB_Row<N>& dbm_0 = dbm[0];
  // Speculative allocation of temporaries to be used in the following loop.
  PPL_DIRTY_TEMP(N, coeff_i);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_sc_i);
  // Note: indices above `w' can be disregarded, as they all have
  // a zero coefficient in `sc_expr'.
  for (Linear_Expression::const_iterator i = sc_expr.begin(),
        i_end = sc_expr.lower_bound(Variable(w)); i != i_end; ++i) {
    const Coefficient& sc_i = *i;
    const dimension_type i_dim = i.variable().space_dimension();
    const int sign_i = sgn(sc_i);
    if (sign_i > 0) {
      assign_r(coeff_i, sc_i, ROUND_UP);
      // Approximating `sc_expr'.
      if (pos_pinf_count <= 1) {
        const N& up_approx_i = dbm_0[i_dim];
        if (!is_plus_infinity(up_approx_i)) {
          add_mul_assign_r(pos_sum, coeff_i, up_approx_i, ROUND_UP);
        }
        else {
          ++pos_pinf_count;
          pos_pinf_index = i_dim;
        }
      }
    }
    else {
      PPL_ASSERT(sign_i < 0);
      neg_assign(minus_sc_i, sc_i);
      // Note: using temporary named `coeff_i' to store -coeff_i.
      assign_r(coeff_i, minus_sc_i, ROUND_UP);
      // Approximating `sc_expr'.
      if (pos_pinf_count <= 1) {
        const N& up_approx_minus_i = dbm[i_dim][0];
        if (!is_plus_infinity(up_approx_minus_i)) {
          add_mul_assign_r(pos_sum, coeff_i, up_approx_minus_i, ROUND_UP);
        }
        else {
          ++pos_pinf_count;
          pos_pinf_index = i_dim;
        }
      }
    }
  }
  // Apply the affine lower bound.
  generalized_affine_image(var,
                           GREATER_OR_EQUAL,
                           lb_expr,
                           denominator);
  // Return immediately if no approximation could be computed.
  if (pos_pinf_count > 1) {
    return;
  }

  // In the following, shortest-path closure will be definitely lost.
  reset_shortest_path_closed();

  // Exploit the upper approximation, if possible.
  if (pos_pinf_count <= 1) {
    // Compute quotient (if needed).
    if (sc_denom != 1) {
      // Before computing quotients, the denominator should be approximated
      // towards zero. Since `sc_denom' is known to be positive, this amounts to
      // rounding downwards, which is achieved as usual by rounding upwards
      // `minus_sc_denom' and negating again the result.
      PPL_DIRTY_TEMP(N, down_sc_denom);
      assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
      neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
      div_assign_r(pos_sum, pos_sum, down_sc_denom, ROUND_UP);
    }
    // Add the upper bound constraint, if meaningful.
    if (pos_pinf_count == 0) {
      // Add the constraint `v <= pos_sum'.
      dbm[0][v] = pos_sum;
      // Deduce constraints of the form `v - u', where `u != v'.
      deduce_v_minus_u_bounds(v, w, sc_expr, sc_denom, pos_sum);
    }
    // Here `pos_pinf_count == 1'.
    else if (pos_pinf_index != v
             && sc_expr.get(Variable(pos_pinf_index - 1)) == sc_denom) {
        // Add the constraint `v - pos_pinf_index <= pos_sum'.
        dbm[pos_pinf_index][v] = pos_sum;
      }
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>
::bounded_affine_preimage(const Variable var,
                          const Linear_Expression& lb_expr,
                          const Linear_Expression& ub_expr,
                          Coefficient_traits::const_reference denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("bounded_affine_preimage(v, lb, ub, d)", "d == 0");
  }
  // Dimension-compatibility checks.
  // `var' should be one of the dimensions of the BD_Shape.
  const dimension_type space_dim = space_dimension();
  const dimension_type v = var.id() + 1;
  if (v > space_dim) {
    throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)",
                                 "v", var);
  }
  // The dimension of `lb_expr' and `ub_expr' should not be
  // greater than the dimension of `*this'.
  const dimension_type lb_space_dim = lb_expr.space_dimension();
  if (space_dim < lb_space_dim) {
    throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)",
                                 "lb", lb_expr);
  }
  const dimension_type ub_space_dim = ub_expr.space_dimension();
  if (space_dim < ub_space_dim) {
    throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)",
                                 "ub", ub_expr);
  }
  // Any preimage of an empty BDS is empty.
  shortest_path_closure_assign();
  if (marked_empty()) {
    return;
  }
  if (ub_expr.coefficient(var) == 0) {
    refine(var, LESS_OR_EQUAL, ub_expr, denominator);
    generalized_affine_preimage(var, GREATER_OR_EQUAL,
                                lb_expr, denominator);
    return;
  }
  if (lb_expr.coefficient(var) == 0) {
    refine(var, GREATER_OR_EQUAL, lb_expr, denominator);
    generalized_affine_preimage(var, LESS_OR_EQUAL,
                                ub_expr, denominator);
    return;
  }

  const Coefficient& lb_expr_v = lb_expr.coefficient(var);
  // Here `var' occurs in `lb_expr' and `ub_expr'.
  // To ease the computation, we add an additional dimension.
  const Variable new_var(space_dim);
  add_space_dimensions_and_embed(1);
  const Linear_Expression lb_inverse
    = lb_expr - (lb_expr_v + denominator)*var;
  PPL_DIRTY_TEMP_COEFFICIENT(lb_inverse_denom);
  neg_assign(lb_inverse_denom, lb_expr_v);
  affine_image(new_var, lb_inverse, lb_inverse_denom);
  shortest_path_closure_assign();
  PPL_ASSERT(!marked_empty());
  generalized_affine_preimage(var, LESS_OR_EQUAL,
                              ub_expr, denominator);
  if (sgn(denominator) == sgn(lb_inverse_denom)) {
    add_constraint(var >= new_var);
  }
  else {
    add_constraint(var <= new_var);
  }
  // Remove the temporarily added dimension.
  remove_higher_space_dimensions(space_dim);
}

template <typename T>
void
BD_Shape<T>::generalized_affine_image(const Variable var,
                                      const Relation_Symbol relsym,
                                      const Linear_Expression& expr,
                                      Coefficient_traits::const_reference
                                      denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("generalized_affine_image(v, r, e, d)", "d == 0");
  }
  // Dimension-compatibility checks.
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type space_dim = space_dimension();
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible("generalized_affine_image(v, r, e, d)",
                                 "e", expr);
  }
  // `var' should be one of the dimensions of the BDS.
  const dimension_type v = var.id() + 1;
  if (v > space_dim) {
    throw_dimension_incompatible("generalized_affine_image(v, r, e, d)",
                                 var.id());
  }
  // The relation symbol cannot be a strict relation symbol.
  if (relsym == LESS_THAN || relsym == GREATER_THAN) {
    throw_invalid_argument("generalized_affine_image(v, r, e, d)",
                           "r is a strict relation symbol");
  }
  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL) {
    throw_invalid_argument("generalized_affine_image(v, r, e, d)",
                           "r is the disequality relation symbol");
  }
  if (relsym == EQUAL) {
    // The relation symbol is "=":
    // this is just an affine image computation.
    affine_image(var, expr, denominator);
    return;
  }

  // The image of an empty BDS is empty too.
  shortest_path_closure_assign();
  if (marked_empty()) {
    return;
  }
  const Coefficient& b = expr.inhomogeneous_term();
  // Number of non-zero coefficients in `expr': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t = 0;
  // Index of the last non-zero coefficient in `expr', if any.
  dimension_type w = expr.last_nonzero();

  if (w != 0) {
    ++t;
    if (!expr.all_zeroes(1, w)) {
      ++t;
    }
  }

  // Now we know the form of `expr':
  // - If t == 0, then expr == b, with `b' a constant;
  // - If t == 1, then expr == a*w + b, where `w' can be `v' or another
  //   variable; in this second case we have to check whether `a' is
  //   equal to `denominator' or `-denominator', since otherwise we have
  //   to fall back on the general form;
  // - If t == 2, the `expr' is of the general form.
  DB_Row<N>& dbm_0 = dbm[0];
  DB_Row<N>& dbm_v = dbm[v];
  PPL_DIRTY_TEMP_COEFFICIENT(minus_denom);
  neg_assign(minus_denom, denominator);

  if (t == 0) {
    // Case 1: expr == b.
    // Remove all constraints on `var'.
    forget_all_dbm_constraints(v);
    // Both shortest-path closure and reduction are lost.
    reset_shortest_path_closed();
    switch (relsym) {
    case LESS_OR_EQUAL:
      // Add the constraint `var <= b/denominator'.
      add_dbm_constraint(0, v, b, denominator);
      break;
    case GREATER_OR_EQUAL:
      // Add the constraint `var >= b/denominator',
      // i.e., `-var <= -b/denominator',
      add_dbm_constraint(v, 0, b, minus_denom);
      break;
    default:
      // We already dealt with the other cases.
      PPL_UNREACHABLE;
      break;
    }
    PPL_ASSERT(OK());
    return;
  }

  if (t == 1) {
    // Value of the one and only non-zero coefficient in `expr'.
    const Coefficient& a = expr.get(Variable(w - 1));
    if (a == denominator || a == minus_denom) {
      // Case 2: expr == a*w + b, with a == +/- denominator.
      PPL_DIRTY_TEMP(N, d);
      switch (relsym) {
      case LESS_OR_EQUAL:
        div_round_up(d, b, denominator);
        if (w == v) {
          // `expr' is of the form: a*v + b.
          // Shortest-path closure and reduction are not preserved.
          reset_shortest_path_closed();
          if (a == denominator) {
            // Translate each constraint `v - w <= dbm_wv'
            // into the constraint `v - w <= dbm_wv + b/denominator';
            // forget each constraint `w - v <= dbm_vw'.
            for (dimension_type i = space_dim + 1; i-- > 0; ) {
              N& dbm_iv = dbm[i][v];
              add_assign_r(dbm_iv, dbm_iv, d, ROUND_UP);
              assign_r(dbm_v[i], PLUS_INFINITY, ROUND_NOT_NEEDED);
            }
          }
          else {
            // Here `a == -denominator'.
            // Translate the constraint `0 - v <= dbm_v0'
            // into the constraint `0 - v <= dbm_v0 + b/denominator'.
            N& dbm_v0 = dbm_v[0];
            add_assign_r(dbm_0[v], dbm_v0, d, ROUND_UP);
            // Forget all the other constraints on `v'.
            assign_r(dbm_v0, PLUS_INFINITY, ROUND_NOT_NEEDED);
            forget_binary_dbm_constraints(v);
          }
        }
        else {
          // Here `w != v', so that `expr' is of the form
          // +/-denominator * w + b, with `w != v'.
          // Remove all constraints on `v'.
          forget_all_dbm_constraints(v);
          // Shortest-path closure is preserved, but not reduction.
          if (marked_shortest_path_reduced()) {
            reset_shortest_path_reduced();
          }
          if (a == denominator) {
            // Add the new constraint `v - w <= b/denominator'.
            add_dbm_constraint(w, v, d);
          }
          else {
            // Here a == -denominator, so that we should be adding
            // the constraint `v <= b/denominator - w'.
            // Approximate it by computing a lower bound for `w'.
            const N& dbm_w0 = dbm[w][0];
            if (!is_plus_infinity(dbm_w0)) {
              // Add the constraint `v <= b/denominator - lb_w'.
              add_assign_r(dbm_0[v], d, dbm_w0, ROUND_UP);
              // Shortest-path closure is not preserved.
              reset_shortest_path_closed();
            }
          }
        }
        break;

      case GREATER_OR_EQUAL:
        div_round_up(d, b, minus_denom);
        if (w == v) {
          // `expr' is of the form: a*w + b.
          // Shortest-path closure and reduction are not preserved.
          reset_shortest_path_closed();
          if (a == denominator) {
            // Translate each constraint `w - v <= dbm_vw'
            // into the constraint `w - v <= dbm_vw - b/denominator';
            // forget each constraint `v - w <= dbm_wv'.
            for (dimension_type i = space_dim + 1; i-- > 0; ) {
              N& dbm_vi = dbm_v[i];
              add_assign_r(dbm_vi, dbm_vi, d, ROUND_UP);
              assign_r(dbm[i][v], PLUS_INFINITY, ROUND_NOT_NEEDED);
            }
          }
          else {
            // Here `a == -denominator'.
            // Translate the constraint `0 - v <= dbm_v0'
            // into the constraint `0 - v <= dbm_0v - b/denominator'.
            N& dbm_0v = dbm_0[v];
            add_assign_r(dbm_v[0], dbm_0v, d, ROUND_UP);
            // Forget all the other constraints on `v'.
            assign_r(dbm_0v, PLUS_INFINITY, ROUND_NOT_NEEDED);
            forget_binary_dbm_constraints(v);
          }
        }
        else {
          // Here `w != v', so that `expr' is of the form
          // +/-denominator * w + b, with `w != v'.
          // Remove all constraints on `v'.
          forget_all_dbm_constraints(v);
          // Shortest-path closure is preserved, but not reduction.
          if (marked_shortest_path_reduced()) {
            reset_shortest_path_reduced();
          }
          if (a == denominator) {
            // Add the new constraint `v - w >= b/denominator',
            // i.e., `w - v <= -b/denominator'.
            add_dbm_constraint(v, w, d);
          }
          else {
            // Here a == -denominator, so that we should be adding
            // the constraint `v >= -w + b/denominator',
            // i.e., `-v <= w - b/denominator'.
            // Approximate it by computing an upper bound for `w'.
            const N& dbm_0w = dbm_0[w];
            if (!is_plus_infinity(dbm_0w)) {
              // Add the constraint `-v <= ub_w - b/denominator'.
              add_assign_r(dbm_v[0], dbm_0w, d, ROUND_UP);
              // Shortest-path closure is not preserved.
              reset_shortest_path_closed();
            }
          }
        }
        break;

      default:
        // We already dealt with the other cases.
        PPL_UNREACHABLE;
        break;
      }
      PPL_ASSERT(OK());
      return;
    }
  }

  // General case.
  // Either t == 2, so that
  // expr == a_1*x_1 + a_2*x_2 + ... + a_n*x_n + b, where n >= 2,
  // or t == 1, expr == a*w + b, but a <> +/- denominator.
  // We will remove all the constraints on `v' and add back
  // a constraint providing an upper or a lower bound for `v'
  // (depending on `relsym').
  const bool is_sc = (denominator > 0);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_b);
  neg_assign(minus_b, b);
  const Coefficient& sc_b = is_sc ? b : minus_b;
  const Coefficient& minus_sc_b = is_sc ? minus_b : b;
  const Coefficient& sc_denom = is_sc ? denominator : minus_denom;
  const Coefficient& minus_sc_denom = is_sc ? minus_denom : denominator;
  // NOTE: here, for optimization purposes, `minus_expr' is only assigned
  // when `denominator' is negative. Do not use it unless you are sure
  // it has been correctly assigned.
  Linear_Expression minus_expr;
  if (!is_sc) {
    minus_expr = -expr;
  }
  const Linear_Expression& sc_expr = is_sc ? expr : minus_expr;

  PPL_DIRTY_TEMP(N, sum);
  // Index of variable that is unbounded in `this->dbm'.
  PPL_UNINITIALIZED(dimension_type, pinf_index);
  // Number of unbounded variables found.
  dimension_type pinf_count = 0;

  // Speculative allocation of temporaries to be used in the following loops.
  PPL_DIRTY_TEMP(N, coeff_i);
  PPL_DIRTY_TEMP_COEFFICIENT(minus_sc_i);

  switch (relsym) {
  case LESS_OR_EQUAL:
    // Compute an upper approximation for `sc_expr' into `sum'.

    // Approximate the inhomogeneous term.
    assign_r(sum, sc_b, ROUND_UP);
    // Approximate the homogeneous part of `sc_expr'.
    // Note: indices above `w' can be disregarded, as they all have
    // a zero coefficient in `sc_expr'.
    PPL_ASSERT(w != 0);
    for (Linear_Expression::const_iterator i = sc_expr.begin(),
        i_end = sc_expr.lower_bound(Variable(w)); i != i_end; ++i) {
      const Coefficient& sc_i = *i;
      const dimension_type i_dim = i.variable().space_dimension();
      const int sign_i = sgn(sc_i);
      PPL_ASSERT(sign_i != 0);
      // Choose carefully: we are approximating `sc_expr'.
      const N& approx_i = (sign_i > 0) ? dbm_0[i_dim] : dbm[i_dim][0];
      if (is_plus_infinity(approx_i)) {
        if (++pinf_count > 1) {
          break;
        }
        pinf_index = i_dim;
        continue;
      }
      if (sign_i > 0) {
        assign_r(coeff_i, sc_i, ROUND_UP);
      }
      else {
        neg_assign(minus_sc_i, sc_i);
        assign_r(coeff_i, minus_sc_i, ROUND_UP);
      }
      add_mul_assign_r(sum, coeff_i, approx_i, ROUND_UP);
    }

    // Remove all constraints on `v'.
    forget_all_dbm_constraints(v);
    // Shortest-path closure is preserved, but not reduction.
    if (marked_shortest_path_reduced()) {
      reset_shortest_path_reduced();
    }
    // Return immediately if no approximation could be computed.
    if (pinf_count > 1) {
      PPL_ASSERT(OK());
      return;
    }

    // Divide by the (sign corrected) denominator (if needed).
    if (sc_denom != 1) {
      // Before computing the quotient, the denominator should be approximated
      // towards zero. Since `sc_denom' is known to be positive, this amounts to
      // rounding downwards, which is achieved as usual by rounding upwards
      // `minus_sc_denom' and negating again the result.
      PPL_DIRTY_TEMP(N, down_sc_denom);
      assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
      neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
      div_assign_r(sum, sum, down_sc_denom, ROUND_UP);
    }

    if (pinf_count == 0) {
      // Add the constraint `v <= sum'.
      add_dbm_constraint(0, v, sum);
      // Deduce constraints of the form `v - u', where `u != v'.
      deduce_v_minus_u_bounds(v, w, sc_expr, sc_denom, sum);
    }
    else if (pinf_count == 1) {
      if (pinf_index != v
          && expr.get(Variable(pinf_index - 1)) == denominator) {
        // Add the constraint `v - pinf_index <= sum'.
        add_dbm_constraint(pinf_index, v, sum);
      }
    }
    break;

  case GREATER_OR_EQUAL:
    // Compute an upper approximation for `-sc_expr' into `sum'.
    // Note: approximating `-sc_expr' from above and then negating the
    // result is the same as approximating `sc_expr' from below.

    // Approximate the inhomogeneous term.
    assign_r(sum, minus_sc_b, ROUND_UP);
    // Approximate the homogeneous part of `-sc_expr'.
    for (Linear_Expression::const_iterator i = sc_expr.begin(),
        i_end = sc_expr.lower_bound(Variable(w)); i != i_end; ++i) {
      const Coefficient& sc_i = *i;
      const int sign_i = sgn(sc_i);
      PPL_ASSERT(sign_i != 0);
      const dimension_type i_dim = i.variable().space_dimension();
      // Choose carefully: we are approximating `-sc_expr'.
      const N& approx_i = (sign_i > 0) ? dbm[i_dim][0] : dbm_0[i_dim];
      if (is_plus_infinity(approx_i)) {
        if (++pinf_count > 1) {
          break;
        }
        pinf_index = i_dim;
        continue;
      }
      if (sign_i > 0) {
        assign_r(coeff_i, sc_i, ROUND_UP);
      }
      else {
        neg_assign(minus_sc_i, sc_i);
        assign_r(coeff_i, minus_sc_i, ROUND_UP);
      }
      add_mul_assign_r(sum, coeff_i, approx_i, ROUND_UP);
    }

    // Remove all constraints on `var'.
    forget_all_dbm_constraints(v);
    // Shortest-path closure is preserved, but not reduction.
    if (marked_shortest_path_reduced()) {
      reset_shortest_path_reduced();
    }
    // Return immediately if no approximation could be computed.
    if (pinf_count > 1) {
      PPL_ASSERT(OK());
      return;
    }

    // Divide by the (sign corrected) denominator (if needed).
    if (sc_denom != 1) {
      // Before computing the quotient, the denominator should be approximated
      // towards zero. Since `sc_denom' is known to be positive, this amounts to
      // rounding downwards, which is achieved as usual by rounding upwards
      // `minus_sc_denom' and negating again the result.
      PPL_DIRTY_TEMP(N, down_sc_denom);
      assign_r(down_sc_denom, minus_sc_denom, ROUND_UP);
      neg_assign_r(down_sc_denom, down_sc_denom, ROUND_UP);
      div_assign_r(sum, sum, down_sc_denom, ROUND_UP);
    }

    if (pinf_count == 0) {
      // Add the constraint `v >= -sum', i.e., `-v <= sum'.
      add_dbm_constraint(v, 0, sum);
      // Deduce constraints of the form `u - v', where `u != v'.
      deduce_u_minus_v_bounds(v, w, sc_expr, sc_denom, sum);
    }
    else if (pinf_count == 1) {
      if (pinf_index != v
          && expr.get(Variable(pinf_index - 1)) == denominator) {
        // Add the constraint `v - pinf_index >= -sum',
        // i.e., `pinf_index - v <= sum'.
        add_dbm_constraint(v, pinf_index, sum);
      }
    }
    break;

  default:
    // We already dealt with the other cases.
    PPL_UNREACHABLE;
    break;
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::generalized_affine_image(const Linear_Expression& lhs,
                                      const Relation_Symbol relsym,
                                      const Linear_Expression& rhs) {
  // Dimension-compatibility checks.
  // The dimension of `lhs' should not be greater than the dimension
  // of `*this'.
  const dimension_type space_dim = space_dimension();
  const dimension_type lhs_space_dim = lhs.space_dimension();
  if (space_dim < lhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_image(e1, r, e2)",
                                 "e1", lhs);
  }
  // The dimension of `rhs' should not be greater than the dimension
  // of `*this'.
  const dimension_type rhs_space_dim = rhs.space_dimension();
  if (space_dim < rhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_image(e1, r, e2)",
                                 "e2", rhs);
  }
  // Strict relation symbols are not admitted for BDSs.
  if (relsym == LESS_THAN || relsym == GREATER_THAN) {
    throw_invalid_argument("generalized_affine_image(e1, r, e2)",
                           "r is a strict relation symbol");
  }
  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL) {
    throw_invalid_argument("generalized_affine_image(e1, r, e2)",
                           "r is the disequality relation symbol");
  }
  // The image of an empty BDS is empty.
  shortest_path_closure_assign();
  if (marked_empty()) {
    return;
  }
  // Number of non-zero coefficients in `lhs': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t_lhs = 0;
  // Index of the last non-zero coefficient in `lhs', if any.
  dimension_type j_lhs = lhs.last_nonzero();

  if (j_lhs != 0) {
    ++t_lhs;
    if (!lhs.all_zeroes(1, j_lhs)) {
      ++t_lhs;
    }
    --j_lhs;
  }

  const Coefficient& b_lhs = lhs.inhomogeneous_term();

  if (t_lhs == 0) {
    // `lhs' is a constant.
    // In principle, it is sufficient to add the constraint `lhs relsym rhs'.
    // Note that this constraint is a bounded difference if `t_rhs <= 1'
    // or `t_rhs > 1' and `rhs == a*v - a*w + b_rhs'. If `rhs' is of a
    // more general form, it will be simply ignored.
    // TODO: if it is not a bounded difference, should we compute
    // approximations for this constraint?
    switch (relsym) {
    case LESS_OR_EQUAL:
      refine_no_check(lhs <= rhs);
      break;
    case EQUAL:
      refine_no_check(lhs == rhs);
      break;
    case GREATER_OR_EQUAL:
      refine_no_check(lhs >= rhs);
      break;
    default:
      // We already dealt with the other cases.
      PPL_UNREACHABLE;
      break;
    }
  }
  else if (t_lhs == 1) {
    // Here `lhs == a_lhs * v + b_lhs'.
    // Independently from the form of `rhs', we can exploit the
    // method computing generalized affine images for a single variable.
    Variable v(j_lhs);
    // Compute a sign-corrected relation symbol.
    const Coefficient& denom = lhs.coefficient(v);
    Relation_Symbol new_relsym = relsym;
    if (denom < 0) {
      if (relsym == LESS_OR_EQUAL) {
        new_relsym = GREATER_OR_EQUAL;
      }
      else if (relsym == GREATER_OR_EQUAL) {
        new_relsym = LESS_OR_EQUAL;
      }
    }
    Linear_Expression expr = rhs - b_lhs;
    generalized_affine_image(v, new_relsym, expr, denom);
  }
  else {
    // Here `lhs' is of the general form, having at least two variables.
    // Compute the set of variables occurring in `lhs'.
    std::vector<Variable> lhs_vars;
    for (Linear_Expression::const_iterator i = lhs.begin(), i_end = lhs.end();
          i != i_end; ++i) {
      lhs_vars.push_back(i.variable());
    }
    const dimension_type num_common_dims = std::min(lhs_space_dim, rhs_space_dim);
    if (!lhs.have_a_common_variable(rhs, Variable(0), Variable(num_common_dims))) {
      // `lhs' and `rhs' variables are disjoint.
      // Existentially quantify all variables in the lhs.
      for (dimension_type i = lhs_vars.size(); i-- > 0; ) {
        forget_all_dbm_constraints(lhs_vars[i].id() + 1);
      }
      // Constrain the left hand side expression so that it is related to
      // the right hand side expression as dictated by `relsym'.
      // TODO: if the following constraint is NOT a bounded difference,
      // it will be simply ignored. Should we compute approximations for it?
      switch (relsym) {
      case LESS_OR_EQUAL:
        refine_no_check(lhs <= rhs);
        break;
      case EQUAL:
        refine_no_check(lhs == rhs);
        break;
      case GREATER_OR_EQUAL:
        refine_no_check(lhs >= rhs);
        break;
      default:
        // We already dealt with the other cases.
        PPL_UNREACHABLE;
        break;
      }
    }
    else {
      // Some variables in `lhs' also occur in `rhs'.

#if 1 // Simplified computation (see the TODO note below).

      for (dimension_type i = lhs_vars.size(); i-- > 0; ) {
        forget_all_dbm_constraints(lhs_vars[i].id() + 1);
      }
#else // Currently unnecessarily complex computation.

      // More accurate computation that is worth doing only if
      // the following TODO note is accurately dealt with.

      // To ease the computation, we add an additional dimension.
      const Variable new_var(space_dim);
      add_space_dimensions_and_embed(1);
      // Constrain the new dimension to be equal to `rhs'.
      // NOTE: calling affine_image() instead of refine_no_check()
      // ensures some approximation is tried even when the constraint
      // is not a bounded difference.
      affine_image(new_var, rhs);
      // Existentially quantify all variables in the lhs.
      // NOTE: enforce shortest-path closure for precision.
      shortest_path_closure_assign();
      PPL_ASSERT(!marked_empty());
      for (dimension_type i = lhs_vars.size(); i-- > 0; ) {
        forget_all_dbm_constraints(lhs_vars[i].id() + 1);
      }
      // Constrain the new dimension so that it is related to
      // the left hand side as dictated by `relsym'.
      // TODO: each one of the following constraints is definitely NOT
      // a bounded differences (since it has 3 variables at least).
      // Thus, the method refine_no_check() will simply ignore it.
      // Should we compute approximations for this constraint?
      switch (relsym) {
      case LESS_OR_EQUAL:
        refine_no_check(lhs <= new_var);
        break;
      case EQUAL:
        refine_no_check(lhs == new_var);
        break;
      case GREATER_OR_EQUAL:
        refine_no_check(lhs >= new_var);
        break;
      default:
        // We already dealt with the other cases.
        PPL_UNREACHABLE;
        break;
      }
      // Remove the temporarily added dimension.
      remove_higher_space_dimensions(space_dim-1);
#endif // Currently unnecessarily complex computation.
    }
  }

  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::generalized_affine_preimage(const Variable var,
                                         const Relation_Symbol relsym,
                                         const Linear_Expression& expr,
                                         Coefficient_traits::const_reference
                                         denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("generalized_affine_preimage(v, r, e, d)",
                           "d == 0");
  }
  // Dimension-compatibility checks.
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  const dimension_type space_dim = space_dimension();
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible("generalized_affine_preimage(v, r, e, d)",
                                 "e", expr);
  }
  // `var' should be one of the dimensions of the BDS.
  const dimension_type v = var.id() + 1;
  if (v > space_dim) {
    throw_dimension_incompatible("generalized_affine_preimage(v, r, e, d)",
                                 var.id());
  }
  // The relation symbol cannot be a strict relation symbol.
  if (relsym == LESS_THAN || relsym == GREATER_THAN) {
    throw_invalid_argument("generalized_affine_preimage(v, r, e, d)",
                           "r is a strict relation symbol");
  }
  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL)  {
    throw_invalid_argument("generalized_affine_preimage(v, r, e, d)",
                           "r is the disequality relation symbol");
  }
  if (relsym == EQUAL) {
    // The relation symbol is "=":
    // this is just an affine preimage computation.
    affine_preimage(var, expr, denominator);
    return;
  }

  // The preimage of an empty BDS is empty too.
  shortest_path_closure_assign();
  if (marked_empty()) {
    return;
  }
  // Check whether the preimage of this affine relation can be easily
  // computed as the image of its inverse relation.
  const Coefficient& expr_v = expr.coefficient(var);
  if (expr_v != 0) {
    const Relation_Symbol reversed_relsym = (relsym == LESS_OR_EQUAL)
      ? GREATER_OR_EQUAL : LESS_OR_EQUAL;
    const Linear_Expression inverse
      = expr - (expr_v + denominator)*var;
    PPL_DIRTY_TEMP_COEFFICIENT(inverse_denom);
    neg_assign(inverse_denom, expr_v);
    const Relation_Symbol inverse_relsym
      = (sgn(denominator) == sgn(inverse_denom)) ? relsym : reversed_relsym;
    generalized_affine_image(var, inverse_relsym, inverse, inverse_denom);
    return;
  }

  refine(var, relsym, expr, denominator);
  // If the shrunk BD_Shape is empty, its preimage is empty too; ...
  if (is_empty()) {
    return;
  }
  // ...  otherwise, since the relation was not invertible,
  // we just forget all constraints on `v'.
  forget_all_dbm_constraints(v);
  // Shortest-path closure is preserved, but not reduction.
  if (marked_shortest_path_reduced()) {
    reset_shortest_path_reduced();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::generalized_affine_preimage(const Linear_Expression& lhs,
                                         const Relation_Symbol relsym,
                                         const Linear_Expression& rhs) {
  // Dimension-compatibility checks.
  // The dimension of `lhs' should not be greater than the dimension
  // of `*this'.
  const dimension_type bds_space_dim = space_dimension();
  const dimension_type lhs_space_dim = lhs.space_dimension();
  if (bds_space_dim < lhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_preimage(e1, r, e2)",
                                 "e1", lhs);
  }
  // The dimension of `rhs' should not be greater than the dimension
  // of `*this'.
  const dimension_type rhs_space_dim = rhs.space_dimension();
  if (bds_space_dim < rhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_preimage(e1, r, e2)",
                                 "e2", rhs);
  }
  // Strict relation symbols are not admitted for BDSs.
  if (relsym == LESS_THAN || relsym == GREATER_THAN) {
    throw_invalid_argument("generalized_affine_preimage(e1, r, e2)",
                           "r is a strict relation symbol");
  }
  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL) {
    throw_invalid_argument("generalized_affine_preimage(e1, r, e2)",
                           "r is the disequality relation symbol");
  }
  // The preimage of an empty BDS is empty.
  shortest_path_closure_assign();
  if (marked_empty()) {
    return;
  }
  // Number of non-zero coefficients in `lhs': will be set to
  // 0, 1, or 2, the latter value meaning any value greater than 1.
  dimension_type t_lhs = 0;
  // Index of the last non-zero coefficient in `lhs', if any.
  dimension_type j_lhs = lhs.last_nonzero();

  if (j_lhs != 0) {
    ++t_lhs;
    if (!lhs.all_zeroes(1, j_lhs)) {
      ++t_lhs;
    }
    --j_lhs;
  }

  const Coefficient& b_lhs = lhs.inhomogeneous_term();

  if (t_lhs == 0) {
    // `lhs' is a constant.
    // In this case, preimage and image happen to be the same.
    generalized_affine_image(lhs, relsym, rhs);
    return;
  }
  else if (t_lhs == 1) {
    // Here `lhs == a_lhs * v + b_lhs'.
    // Independently from the form of `rhs', we can exploit the
    // method computing generalized affine preimages for a single variable.
    Variable v(j_lhs);
    // Compute a sign-corrected relation symbol.
    const Coefficient& denom = lhs.coefficient(v);
    Relation_Symbol new_relsym = relsym;
    if (denom < 0) {
      if (relsym == LESS_OR_EQUAL) {
        new_relsym = GREATER_OR_EQUAL;
      }
      else if (relsym == GREATER_OR_EQUAL) {
        new_relsym = LESS_OR_EQUAL;
      }
    }
    Linear_Expression expr = rhs - b_lhs;
    generalized_affine_preimage(v, new_relsym, expr, denom);
  }
  else {
    // Here `lhs' is of the general form, having at least two variables.
    // Compute the set of variables occurring in `lhs'.
    std::vector<Variable> lhs_vars;
    for (Linear_Expression::const_iterator i = lhs.begin(), i_end = lhs.end();
          i != i_end; ++i) {
      lhs_vars.push_back(i.variable());
    }
    const dimension_type num_common_dims = std::min(lhs_space_dim, rhs_space_dim);
    if (!lhs.have_a_common_variable(rhs, Variable(0), Variable(num_common_dims))) {
      // `lhs' and `rhs' variables are disjoint.

      // Constrain the left hand side expression so that it is related to
      // the right hand side expression as dictated by `relsym'.
      // TODO: if the following constraint is NOT a bounded difference,
      // it will be simply ignored. Should we compute approximations for it?
      switch (relsym) {
      case LESS_OR_EQUAL:
        refine_no_check(lhs <= rhs);
        break;
      case EQUAL:
        refine_no_check(lhs == rhs);
        break;
      case GREATER_OR_EQUAL:
        refine_no_check(lhs >= rhs);
        break;
      default:
        // We already dealt with the other cases.
        PPL_UNREACHABLE;
        break;
      }

      // If the shrunk BD_Shape is empty, its preimage is empty too; ...
      if (is_empty()) {
        return;
      }
      // Existentially quantify all variables in the lhs.
      for (dimension_type i = lhs_vars.size(); i-- > 0; ) {
        forget_all_dbm_constraints(lhs_vars[i].id() + 1);
      }
    }
    else {

      // Some variables in `lhs' also occur in `rhs'.
      // To ease the computation, we add an additional dimension.
      const Variable new_var(bds_space_dim);
      add_space_dimensions_and_embed(1);
      // Constrain the new dimension to be equal to `lhs'.
      // NOTE: calling affine_image() instead of refine_no_check()
      // ensures some approximation is tried even when the constraint
      // is not a bounded difference.
      affine_image(new_var, lhs);
      // Existentiallly quantify all variables in the lhs.
      // NOTE: enforce shortest-path closure for precision.
      shortest_path_closure_assign();
      PPL_ASSERT(!marked_empty());
      for (dimension_type i = lhs_vars.size(); i-- > 0; ) {
        forget_all_dbm_constraints(lhs_vars[i].id() + 1);
      }
      // Constrain the new dimension so that it is related to
      // the left hand side as dictated by `relsym'.
      // Note: if `rhs == a_rhs*v + b_rhs' where `a_rhs' is in {0, 1},
      // then one of the following constraints will be added,
      // since it is a bounded difference. Else the method
      // refine_no_check() will ignore it, because the
      // constraint is NOT a bounded difference.
      switch (relsym) {
      case LESS_OR_EQUAL:
        refine_no_check(new_var <= rhs);
        break;
      case EQUAL:
        refine_no_check(new_var == rhs);
        break;
      case GREATER_OR_EQUAL:
        refine_no_check(new_var >= rhs);
        break;
      default:
        // We already dealt with the other cases.
        PPL_UNREACHABLE;
        break;
      }
      // Remove the temporarily added dimension.
      remove_higher_space_dimensions(bds_space_dim);
    }
  }

  PPL_ASSERT(OK());
}

template <typename T>
Constraint_System
BD_Shape<T>::constraints() const {
  const dimension_type space_dim = space_dimension();
  Constraint_System cs;
  cs.set_space_dimension(space_dim);

  if (space_dim == 0) {
    if (marked_empty()) {
      cs = Constraint_System::zero_dim_empty();
    }
    return cs;
  }

  if (marked_empty()) {
    cs.insert(Constraint::zero_dim_false());
    return cs;
  }

  if (marked_shortest_path_reduced()) {
    // Disregard redundant constraints.
    cs = minimized_constraints();
    return cs;
  }

  PPL_DIRTY_TEMP_COEFFICIENT(a);
  PPL_DIRTY_TEMP_COEFFICIENT(b);
  // Go through all the unary constraints in `dbm'.
  const DB_Row<N>& dbm_0 = dbm[0];
  for (dimension_type j = 1; j <= space_dim; ++j) {
    const Variable x(j-1);
    const N& dbm_0j = dbm_0[j];
    const N& dbm_j0 = dbm[j][0];
    if (is_additive_inverse(dbm_j0, dbm_0j)) {
      // We have a unary equality constraint.
      numer_denom(dbm_0j, b, a);
      cs.insert(a*x == b);
    }
    else {
      // We have 0, 1 or 2 unary inequality constraints.
      if (!is_plus_infinity(dbm_0j)) {
        numer_denom(dbm_0j, b, a);
        cs.insert(a*x <= b);
      }
      if (!is_plus_infinity(dbm_j0)) {
        numer_denom(dbm_j0, b, a);
        cs.insert(-a*x <= b);
      }
    }
  }

  // Go through all the binary constraints in `dbm'.
  for (dimension_type i = 1; i <= space_dim; ++i) {
    const Variable y(i-1);
    const DB_Row<N>& dbm_i = dbm[i];
    for (dimension_type j = i + 1; j <= space_dim; ++j) {
      const Variable x(j-1);
      const N& dbm_ij = dbm_i[j];
      const N& dbm_ji = dbm[j][i];
      if (is_additive_inverse(dbm_ji, dbm_ij)) {
        // We have a binary equality constraint.
        numer_denom(dbm_ij, b, a);
        cs.insert(a*x - a*y == b);
      }
      else {
        // We have 0, 1 or 2 binary inequality constraints.
        if (!is_plus_infinity(dbm_ij)) {
          numer_denom(dbm_ij, b, a);
          cs.insert(a*x - a*y <= b);
        }
        if (!is_plus_infinity(dbm_ji)) {
          numer_denom(dbm_ji, b, a);
          cs.insert(a*y - a*x <= b);
        }
      }
    }
  }
  return cs;
}

template <typename T>
Constraint_System
BD_Shape<T>::minimized_constraints() const {
  shortest_path_reduction_assign();
  const dimension_type space_dim = space_dimension();
  Constraint_System cs;
  cs.set_space_dimension(space_dim);

  if (space_dim == 0) {
    if (marked_empty()) {
      cs = Constraint_System::zero_dim_empty();
    }
    return cs;
  }

  if (marked_empty()) {
    cs.insert(Constraint::zero_dim_false());
    return cs;
  }

  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);

  // Compute leader information.
  std::vector<dimension_type> leaders;
  compute_leaders(leaders);
  std::vector<dimension_type> leader_indices;
  compute_leader_indices(leaders, leader_indices);
  const dimension_type num_leaders = leader_indices.size();

  // Go through the non-leaders to generate equality constraints.
  const DB_Row<N>& dbm_0 = dbm[0];
  for (dimension_type i = 1; i <= space_dim; ++i) {
    const dimension_type leader = leaders[i];
    if (i != leader) {
      // Generate the constraint relating `i' and its leader.
      if (leader == 0) {
        // A unary equality has to be generated.
        PPL_ASSERT(!is_plus_infinity(dbm_0[i]));
        numer_denom(dbm_0[i], numer, denom);
        cs.insert(denom*Variable(i-1) == numer);
      }
      else {
        // A binary equality has to be generated.
        PPL_ASSERT(!is_plus_infinity(dbm[i][leader]));
        numer_denom(dbm[i][leader], numer, denom);
        cs.insert(denom*Variable(leader-1) - denom*Variable(i-1) == numer);
      }
    }
  }

  // Go through the leaders to generate inequality constraints.
  // First generate all the unary inequalities.
  const Bit_Row& red_0 = redundancy_dbm[0];
  for (dimension_type l_i = 1; l_i < num_leaders; ++l_i) {
    const dimension_type i = leader_indices[l_i];
    if (!red_0[i]) {
      numer_denom(dbm_0[i], numer, denom);
      cs.insert(denom*Variable(i-1) <= numer);
    }
    if (!redundancy_dbm[i][0]) {
      numer_denom(dbm[i][0], numer, denom);
      cs.insert(-denom*Variable(i-1) <= numer);
    }
  }
  // Then generate all the binary inequalities.
  for (dimension_type l_i = 1; l_i < num_leaders; ++l_i) {
    const dimension_type i = leader_indices[l_i];
    const DB_Row<N>& dbm_i = dbm[i];
    const Bit_Row& red_i = redundancy_dbm[i];
    for (dimension_type l_j = l_i + 1; l_j < num_leaders; ++l_j) {
      const dimension_type j = leader_indices[l_j];
      if (!red_i[j]) {
        numer_denom(dbm_i[j], numer, denom);
        cs.insert(denom*Variable(j-1) - denom*Variable(i-1) <= numer);
      }
      if (!redundancy_dbm[j][i]) {
        numer_denom(dbm[j][i], numer, denom);
        cs.insert(denom*Variable(i-1) - denom*Variable(j-1) <= numer);
      }
    }
  }
  return cs;
}

template <typename T>
void
BD_Shape<T>::expand_space_dimension(Variable var, dimension_type m) {
  dimension_type old_dim = space_dimension();
  // `var' should be one of the dimensions of the vector space.
  if (var.space_dimension() > old_dim) {
    throw_dimension_incompatible("expand_space_dimension(v, m)", "v", var);
  }

  // The space dimension of the resulting BDS should not
  // overflow the maximum allowed space dimension.
  if (m > max_space_dimension() - space_dimension()) {
    throw_invalid_argument("expand_dimension(v, m)",
                           "adding m new space dimensions exceeds "
                           "the maximum allowed space dimension");
  }
  // Nothing to do, if no dimensions must be added.
  if (m == 0) {
    return;
  }
  // Add the required new dimensions.
  add_space_dimensions_and_embed(m);

  // For each constraints involving variable `var', we add a
  // similar constraint with the new variable substituted for
  // variable `var'.
  const dimension_type v_id = var.id() + 1;
  const DB_Row<N>& dbm_v = dbm[v_id];
  for (dimension_type i = old_dim + 1; i-- > 0; ) {
    DB_Row<N>& dbm_i = dbm[i];
    const N& dbm_i_v = dbm[i][v_id];
    const N& dbm_v_i = dbm_v[i];
    for (dimension_type j = old_dim+1; j < old_dim+m+1; ++j) {
      dbm_i[j] = dbm_i_v;
      dbm[j][i] = dbm_v_i;
    }
  }
  // In general, adding a constraint does not preserve the shortest-path
  // closure or reduction of the bounded difference shape.
  if (marked_shortest_path_closed()) {
    reset_shortest_path_closed();
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::fold_space_dimensions(const Variables_Set& vars,
                                   Variable dest) {
  const dimension_type space_dim = space_dimension();
  // `dest' should be one of the dimensions of the BDS.
  if (dest.space_dimension() > space_dim) {
    throw_dimension_incompatible("fold_space_dimensions(vs, v)",
                                 "v", dest);
  }
  // The folding of no dimensions is a no-op.
  if (vars.empty()) {
    return;
  }
  // All variables in `vars' should be dimensions of the BDS.
  if (vars.space_dimension() > space_dim) {
    throw_dimension_incompatible("fold_space_dimensions(vs, v)",
                                 vars.space_dimension());
  }
  // Moreover, `dest.id()' should not occur in `vars'.
  if (vars.find(dest.id()) != vars.end()) {
    throw_invalid_argument("fold_space_dimensions(vs, v)",
                           "v should not occur in vs");
  }
  shortest_path_closure_assign();
  if (!marked_empty()) {
    // Recompute the elements of the row and the column corresponding
    // to variable `dest' by taking the join of their value with the
    // value of the corresponding elements in the row and column of the
    // variable `vars'.
    const dimension_type v_id = dest.id() + 1;
    DB_Row<N>& dbm_v = dbm[v_id];
    for (Variables_Set::const_iterator i = vars.begin(),
           vs_end = vars.end(); i != vs_end; ++i) {
      const dimension_type to_be_folded_id = *i + 1;
      const DB_Row<N>& dbm_to_be_folded_id = dbm[to_be_folded_id];
      for (dimension_type j = space_dim + 1; j-- > 0; ) {
        max_assign(dbm[j][v_id], dbm[j][to_be_folded_id]);
        max_assign(dbm_v[j], dbm_to_be_folded_id[j]);
      }
    }
  }
  remove_space_dimensions(vars);
}

template <typename T>
void
BD_Shape<T>::drop_some_non_integer_points(Complexity_Class) {
  if (std::numeric_limits<T>::is_integer) {
    return;
  }
  const dimension_type space_dim = space_dimension();
  shortest_path_closure_assign();
  if (space_dim == 0 || marked_empty()) {
    return;
  }
  for (dimension_type i = space_dim + 1; i-- > 0; ) {
    DB_Row<N>& dbm_i = dbm[i];
    for (dimension_type j = space_dim + 1; j-- > 0; ) {
      if (i != j) {
        drop_some_non_integer_points_helper(dbm_i[j]);
      }
    }
  }
  PPL_ASSERT(OK());
}

template <typename T>
void
BD_Shape<T>::drop_some_non_integer_points(const Variables_Set& vars,
                                          Complexity_Class) {
  // Dimension-compatibility check.
  const dimension_type space_dim = space_dimension();
  const dimension_type min_space_dim = vars.space_dimension();
  if (space_dim < min_space_dim) {
    throw_dimension_incompatible("drop_some_non_integer_points(vs, cmpl)",
                                 min_space_dim);
  }
  if (std::numeric_limits<T>::is_integer || min_space_dim == 0) {
    return;
  }
  shortest_path_closure_assign();
  if (marked_empty()) {
    return;
  }
  const Variables_Set::const_iterator v_begin = vars.begin();
  const Variables_Set::const_iterator v_end = vars.end();
  PPL_ASSERT(v_begin != v_end);
  // Unary constraints on a variable occurring in `vars'.
  DB_Row<N>& dbm_0 = dbm[0];
  for (Variables_Set::const_iterator v_i = v_begin; v_i != v_end; ++v_i) {
    const dimension_type i = *v_i + 1;
    drop_some_non_integer_points_helper(dbm_0[i]);
    drop_some_non_integer_points_helper(dbm[i][0]);
  }

  // Binary constraints where both variables occur in `vars'.
  for (Variables_Set::const_iterator v_i = v_begin; v_i != v_end; ++v_i) {
    const dimension_type i = *v_i + 1;
    DB_Row<N>& dbm_i = dbm[i];
    for (Variables_Set::const_iterator v_j = v_begin; v_j != v_end; ++v_j) {
      const dimension_type j = *v_j + 1;
      if (i != j) {
        drop_some_non_integer_points_helper(dbm_i[j]);
      }
    }
  }
  PPL_ASSERT(OK());
}

/*! \relates Parma_Polyhedra_Library::BD_Shape */
template <typename T>
std::ostream&
IO_Operators::operator<<(std::ostream& s, const BD_Shape<T>& bds) {
  typedef typename BD_Shape<T>::coefficient_type N;
  if (bds.is_universe()) {
    s << "true";
  }
  else {
    // We control empty bounded difference shape.
    dimension_type n = bds.space_dimension();
    if (bds.marked_empty()) {
      s << "false";
    }
    else {
      PPL_DIRTY_TEMP(N, v);
      bool first = true;
      for (dimension_type i = 0; i <= n; ++i) {
        for (dimension_type j = i + 1; j <= n; ++j) {
          const N& c_i_j = bds.dbm[i][j];
          const N& c_j_i = bds.dbm[j][i];
          if (is_additive_inverse(c_j_i, c_i_j)) {
            // We will print an equality.
            if (first) {
              first = false;
            }
            else {
              s << ", ";
            }
            if (i == 0) {
              // We have got a equality constraint with one variable.
              s << Variable(j - 1);
              s << " = " << c_i_j;
            }
            else {
              // We have got a equality constraint with two variables.
              if (sgn(c_i_j) >= 0) {
                s << Variable(j - 1);
                s << " - ";
                s << Variable(i - 1);
                s << " = " << c_i_j;
              }
              else {
                s << Variable(i - 1);
                s << " - ";
                s << Variable(j - 1);
                s << " = " << c_j_i;
              }
            }
          }
          else {
            // We will print a non-strict inequality.
            if (!is_plus_infinity(c_j_i)) {
              if (first) {
                first = false;
              }
              else {
                s << ", ";
              }
              if (i == 0) {
                // We have got a constraint with only one variable.
                s << Variable(j - 1);
                neg_assign_r(v, c_j_i, ROUND_DOWN);
                s << " >= " << v;
              }
              else {
                // We have got a constraint with two variables.
                if (sgn(c_j_i) >= 0) {
                  s << Variable(i - 1);
                  s << " - ";
                  s << Variable(j - 1);
                  s << " <= " << c_j_i;
                }
                else {
                  s << Variable(j - 1);
                  s << " - ";
                  s << Variable(i - 1);
                  neg_assign_r(v, c_j_i, ROUND_DOWN);
                  s << " >= " << v;
                }
              }
            }
            if (!is_plus_infinity(c_i_j)) {
              if (first) {
                first = false;
              }
              else {
                s << ", ";
              }
              if (i == 0) {
                // We have got a constraint with only one variable.
                s << Variable(j - 1);
                s << " <= " << c_i_j;
              }
              else {
                // We have got a constraint with two variables.
                if (sgn(c_i_j) >= 0) {
                  s << Variable(j - 1);
                  s << " - ";
                  s << Variable(i - 1);
                  s << " <= " << c_i_j;
                }
                else {
                  s << Variable(i - 1);
                  s << " - ";
                  s << Variable(j - 1);
                  neg_assign_r(v, c_i_j, ROUND_DOWN);
                  s << " >= " << v;
                }
              }
            }
          }
        }
      }
    }
  }
  return s;
}

template <typename T>
void
BD_Shape<T>::ascii_dump(std::ostream& s) const {
  status.ascii_dump(s);
  s << "\n";
  dbm.ascii_dump(s);
  s << "\n";
  redundancy_dbm.ascii_dump(s);
}

PPL_OUTPUT_TEMPLATE_DEFINITIONS(T, BD_Shape<T>)

template <typename T>
bool
BD_Shape<T>::ascii_load(std::istream& s) {
  if (!status.ascii_load(s)) {
    return false;
  }
  if (!dbm.ascii_load(s)) {
    return false;
  }
  if (!redundancy_dbm.ascii_load(s)) {
    return false;
  }
  return true;
}

template <typename T>
memory_size_type
BD_Shape<T>::external_memory_in_bytes() const {
  return dbm.external_memory_in_bytes()
    + redundancy_dbm.external_memory_in_bytes();
}

template <typename T>
bool
BD_Shape<T>::OK() const {
  // Check whether the difference-bound matrix is well-formed.
  if (!dbm.OK()) {
    return false;
  }
  // Check whether the status information is legal.
  if (!status.OK()) {
    return false;
  }
  // An empty BDS is OK.
  if (marked_empty()) {
    return true;
  }
  // MINUS_INFINITY cannot occur at all.
  for (dimension_type i = dbm.num_rows(); i-- > 0; ) {
    for (dimension_type j = dbm.num_rows(); j-- > 0; ) {
      if (is_minus_infinity(dbm[i][j])) {
#ifndef NDEBUG
        using namespace Parma_Polyhedra_Library::IO_Operators;
        std::cerr << "BD_Shape::dbm[" << i << "][" << j << "] = "
                  << dbm[i][j] << "!"
                  << std::endl;
#endif
        return false;
      }
    }
  }
  // On the main diagonal only PLUS_INFINITY can occur.
  for (dimension_type i = dbm.num_rows(); i-- > 0; ) {
    if (!is_plus_infinity(dbm[i][i])) {
#ifndef NDEBUG
      using namespace Parma_Polyhedra_Library::IO_Operators;
      std::cerr << "BD_Shape::dbm[" << i << "][" << i << "] = "
                << dbm[i][i] << "!  (+inf was expected.)"
                << std::endl;
#endif
      return false;
    }
  }
  // Check whether the shortest-path closure information is legal.
  if (marked_shortest_path_closed()) {
    BD_Shape x = *this;
    x.reset_shortest_path_closed();
    x.shortest_path_closure_assign();
    if (x.dbm != dbm) {
#ifndef NDEBUG
      std::cerr << "BD_Shape is marked as closed but it is not!"
                << std::endl;
#endif
      return false;
    }
  }

  // The following tests might result in false alarms when using floating
  // point coefficients: they are only meaningful if the coefficient type
  // base is exact (since otherwise shortest-path closure is approximated).
  if (std::numeric_limits<coefficient_type_base>::is_exact) {

    // Check whether the shortest-path reduction information is legal.
    if (marked_shortest_path_reduced()) {
      // A non-redundant constraint cannot be equal to PLUS_INFINITY.
      for (dimension_type i = dbm.num_rows(); i-- > 0; ) {
        for (dimension_type j = dbm.num_rows(); j-- > 0; ) {
          if (!redundancy_dbm[i][j] && is_plus_infinity(dbm[i][j])) {
#ifndef NDEBUG
            using namespace Parma_Polyhedra_Library::IO_Operators;
            std::cerr << "BD_Shape::dbm[" << i << "][" << j << "] = "
                      << dbm[i][j] << " is marked as non-redundant!"
                      << std::endl;
#endif
            return false;
          }
        }
      }
      BD_Shape x = *this;
      x.reset_shortest_path_reduced();
      x.shortest_path_reduction_assign();
      if (x.redundancy_dbm != redundancy_dbm) {
#ifndef NDEBUG
        std::cerr << "BD_Shape is marked as reduced but it is not!"
                  << std::endl;
#endif
        return false;
      }
    }
  }

  // All checks passed.
  return true;
}

template <typename T>
void
BD_Shape<T>::throw_dimension_incompatible(const char* method,
                                          const BD_Shape& y) const {
  std::ostringstream s;
  s << "PPL::BD_Shape::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", y->space_dimension() == " << y.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
BD_Shape<T>::throw_dimension_incompatible(const char* method,
                                          dimension_type required_dim) const {
  std::ostringstream s;
  s << "PPL::BD_Shape::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", required dimension == " << required_dim << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
BD_Shape<T>::throw_dimension_incompatible(const char* method,
                                          const Constraint& c) const {
  std::ostringstream s;
  s << "PPL::BD_Shape::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", c->space_dimension == " << c.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
BD_Shape<T>::throw_dimension_incompatible(const char* method,
                                          const Congruence& cg) const {
  std::ostringstream s;
  s << "PPL::BD_Shape::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", cg->space_dimension == " << cg.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
BD_Shape<T>::throw_dimension_incompatible(const char* method,
                                          const Generator& g) const {
  std::ostringstream s;
  s << "PPL::BD_Shape::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", g->space_dimension == " << g.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
BD_Shape<T>::throw_expression_too_complex(const char* method,
                                          const Linear_Expression& le) {
  using namespace IO_Operators;
  std::ostringstream s;
  s << "PPL::BD_Shape::" << method << ":" << std::endl
    << le << " is too complex.";
  throw std::invalid_argument(s.str());
}


template <typename T>
void
BD_Shape<T>::throw_dimension_incompatible(const char* method,
                                          const char* le_name,
                                          const Linear_Expression& le) const {
  std::ostringstream s;
  s << "PPL::BD_Shape::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", " << le_name << "->space_dimension() == "
    << le.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
template<typename Interval_Info>
void
BD_Shape<T>::throw_dimension_incompatible(const char* method,
                                          const char* lf_name,
                                          const Linear_Form< Interval<T,
                                          Interval_Info> >& lf) const {
  std::ostringstream s;
  s << "PPL::BD_Shape::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", " << lf_name << "->space_dimension() == "
    << lf.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename T>
void
BD_Shape<T>::throw_invalid_argument(const char* method, const char* reason) {
  std::ostringstream s;
  s << "PPL::BD_Shape::" << method << ":" << std::endl
    << reason << ".";
  throw std::invalid_argument(s.str());
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/BD_Shape_defs.hh line 2373. */

/* Automatically generated from PPL source file ../src/Rational_Interval.hh line 1. */
/* Rational_Interval class declaration and implementation.
*/


/* Automatically generated from PPL source file ../src/Rational_Interval.hh line 28. */
#include <gmpxx.h>

namespace Parma_Polyhedra_Library {

struct Rational_Interval_Info_Policy {
  const_bool_nodef(store_special, true);
  const_bool_nodef(store_open, true);
  const_bool_nodef(cache_empty, true);
  const_bool_nodef(cache_singleton, true);
  const_bool_nodef(cache_normalized, false);
  const_int_nodef(next_bit, 0);
  const_bool_nodef(may_be_empty, true);
  const_bool_nodef(may_contain_infinity, false);
  const_bool_nodef(check_inexact, false);
};

typedef Interval_Info_Bitset<unsigned int,
                             Rational_Interval_Info_Policy> Rational_Interval_Info;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! An interval with rational, possibly open boundaries.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
typedef Interval<mpq_class, Rational_Interval_Info> Rational_Interval;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Box_templates.hh line 42. */
#include <vector>
#include <map>
#include <iostream>

namespace Parma_Polyhedra_Library {

template <typename ITV>
inline
Box<ITV>::Box(dimension_type num_dimensions, Degenerate_Element kind)
  : seq(check_space_dimension_overflow(num_dimensions,
                                       max_space_dimension(),
                                       "PPL::Box::",
                                       "Box(n, k)",
                                       "n exceeds the maximum "
                                       "allowed space dimension")),
    status() {
  // In a box that is marked empty the intervals are completely
  // meaningless: we exploit this by avoiding their initialization.
  if (kind == UNIVERSE) {
    for (dimension_type i = num_dimensions; i-- > 0; ) {
      seq[i].assign(UNIVERSE);
    }
    set_empty_up_to_date();
  }
  else {
    set_empty();
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
inline
Box<ITV>::Box(const Constraint_System& cs)
  : seq(check_space_dimension_overflow(cs.space_dimension(),
                                       max_space_dimension(),
                                       "PPL::Box::",
                                       "Box(cs)",
                                       "cs exceeds the maximum "
                                       "allowed space dimension")),
    status() {
  // FIXME: check whether we can avoid the double initialization.
  for (dimension_type i = cs.space_dimension(); i-- > 0; ) {
    seq[i].assign(UNIVERSE);
  }
  add_constraints_no_check(cs);
}

template <typename ITV>
inline
Box<ITV>::Box(const Congruence_System& cgs)
  : seq(check_space_dimension_overflow(cgs.space_dimension(),
                                       max_space_dimension(),
                                       "PPL::Box::",
                                       "Box(cgs)",
                                       "cgs exceeds the maximum "
                                       "allowed space dimension")),
    status() {
  // FIXME: check whether we can avoid the double initialization.
  for (dimension_type i = cgs.space_dimension(); i-- > 0; ) {
    seq[i].assign(UNIVERSE);
  }
  add_congruences_no_check(cgs);
}

template <typename ITV>
template <typename Other_ITV>
inline
Box<ITV>::Box(const Box<Other_ITV>& y, Complexity_Class)
  : seq(y.space_dimension()),
    // FIXME: why the following does not work?
    // status(y.status) {
    status() {
  // FIXME: remove when the above is fixed.
  if (y.marked_empty()) {
    set_empty();
  }

  if (!y.marked_empty()) {
    for (dimension_type k = y.space_dimension(); k-- > 0; ) {
      seq[k].assign(y.seq[k]);
    }
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
Box<ITV>::Box(const Generator_System& gs)
  : seq(check_space_dimension_overflow(gs.space_dimension(),
                                       max_space_dimension(),
                                       "PPL::Box::",
                                       "Box(gs)",
                                       "gs exceeds the maximum "
                                       "allowed space dimension")),
    status() {
  const Generator_System::const_iterator gs_begin = gs.begin();
  const Generator_System::const_iterator gs_end = gs.end();
  if (gs_begin == gs_end) {
    // An empty generator system defines the empty box.
    set_empty();
    return;
  }

  // The empty flag will be meaningful, whatever happens from now on.
  set_empty_up_to_date();

  const dimension_type space_dim = space_dimension();
  PPL_DIRTY_TEMP(mpq_class, q);
  bool point_seen = false;
  // Going through all the points.
  for (Generator_System::const_iterator
         gs_i = gs_begin; gs_i != gs_end; ++gs_i) {
    const Generator& g = *gs_i;
    if (g.is_point()) {
      const Coefficient& d = g.divisor();
      if (point_seen) {
        // This is not the first point: `seq' already contains valid values.
        // TODO: If the variables in the expression that have coefficient 0
        // have no effect on seq[i], this loop can be optimized using
        // Generator::expr_type::const_iterator.
        for (dimension_type i = space_dim; i-- > 0; ) {
          assign_r(q.get_num(), g.coefficient(Variable(i)), ROUND_NOT_NEEDED);
          assign_r(q.get_den(), d, ROUND_NOT_NEEDED);
          q.canonicalize();
          PPL_DIRTY_TEMP(ITV, iq);
          iq.build(i_constraint(EQUAL, q));
          seq[i].join_assign(iq);
        }
      }
      else {
        // This is the first point seen: initialize `seq'.
        point_seen = true;
        // TODO: If the variables in the expression that have coefficient 0
        // have no effect on seq[i], this loop can be optimized using
        // Generator::expr_type::const_iterator.
        for (dimension_type i = space_dim; i-- > 0; ) {
          assign_r(q.get_num(), g.coefficient(Variable(i)), ROUND_NOT_NEEDED);
          assign_r(q.get_den(), d, ROUND_NOT_NEEDED);
          q.canonicalize();
          seq[i].build(i_constraint(EQUAL, q));
        }
      }
    }
  }

  if (!point_seen) {
    // The generator system is not empty, but contains no points.
    throw std::invalid_argument("PPL::Box<ITV>::Box(gs):\n"
                                "the non-empty generator system gs "
                                "contains no points.");
  }

  // Going through all the lines, rays and closure points.
  for (Generator_System::const_iterator gs_i = gs_begin;
       gs_i != gs_end; ++gs_i) {
    const Generator& g = *gs_i;
    switch (g.type()) {
    case Generator::LINE:
      for (Generator::expr_type::const_iterator i = g.expression().begin(),
              i_end = g.expression().end();
              i != i_end; ++i) {
          seq[i.variable().id()].assign(UNIVERSE);
      }
      break;
    case Generator::RAY:
      for (Generator::expr_type::const_iterator i = g.expression().begin(),
              i_end = g.expression().end();
              i != i_end; ++i) {
        switch (sgn(*i)) {
        case 1:
          seq[i.variable().id()].upper_extend();
          break;
        case -1:
          seq[i.variable().id()].lower_extend();
          break;
        default:
          PPL_UNREACHABLE;
          break;
        }
      }
      break;
    case Generator::CLOSURE_POINT:
      {
        const Coefficient& d = g.divisor();
        // TODO: If the variables in the expression that have coefficient 0
        // have no effect on seq[i], this loop can be optimized using
        // Generator::expr_type::const_iterator.
        for (dimension_type i = space_dim; i-- > 0; ) {
          assign_r(q.get_num(), g.coefficient(Variable(i)), ROUND_NOT_NEEDED);
          assign_r(q.get_den(), d, ROUND_NOT_NEEDED);
          q.canonicalize();
          ITV& seq_i = seq[i];
          seq_i.lower_extend(i_constraint(GREATER_THAN, q));
          seq_i.upper_extend(i_constraint(LESS_THAN, q));
        }
      }
      break;
    default:
      // Points already dealt with.
      break;
    }
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
template <typename T>
Box<ITV>::Box(const BD_Shape<T>& bds, Complexity_Class)
  : seq(check_space_dimension_overflow(bds.space_dimension(),
                                       max_space_dimension(),
                                       "PPL::Box::",
                                       "Box(bds)",
                                       "bds exceeds the maximum "
                                       "allowed space dimension")),
    status() {
  // Expose all the interval constraints.
  bds.shortest_path_closure_assign();
  if (bds.marked_empty()) {
    set_empty();
    PPL_ASSERT(OK());
    return;
  }

  // The empty flag will be meaningful, whatever happens from now on.
  set_empty_up_to_date();

  const dimension_type space_dim = space_dimension();
  if (space_dim == 0) {
    PPL_ASSERT(OK());
    return;
  }

  typedef typename BD_Shape<T>::coefficient_type Coeff;
  PPL_DIRTY_TEMP(Coeff, tmp);
  const DB_Row<Coeff>& dbm_0 = bds.dbm[0];
  for (dimension_type i = space_dim; i-- > 0; ) {
    I_Constraint<Coeff> lower;
    I_Constraint<Coeff> upper;
    ITV& seq_i = seq[i];

    // Set the upper bound.
    const Coeff& u = dbm_0[i+1];
    if (!is_plus_infinity(u)) {
      upper.set(LESS_OR_EQUAL, u, true);
    }

    // Set the lower bound.
    const Coeff& negated_l = bds.dbm[i+1][0];
    if (!is_plus_infinity(negated_l)) {
      neg_assign_r(tmp, negated_l, ROUND_DOWN);
      lower.set(GREATER_OR_EQUAL, tmp);
    }

    seq_i.build(lower, upper);
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
template <typename T>
Box<ITV>::Box(const Octagonal_Shape<T>& oct, Complexity_Class)
  : seq(check_space_dimension_overflow(oct.space_dimension(),
                                       max_space_dimension(),
                                       "PPL::Box::",
                                       "Box(oct)",
                                       "oct exceeds the maximum "
                                       "allowed space dimension")),
    status() {
  // Expose all the interval constraints.
  oct.strong_closure_assign();
  if (oct.marked_empty()) {
    set_empty();
    return;
  }

  // The empty flag will be meaningful, whatever happens from now on.
  set_empty_up_to_date();

  const dimension_type space_dim = space_dimension();
  if (space_dim == 0) {
    return;
  }

  PPL_DIRTY_TEMP(mpq_class, lower_bound);
  PPL_DIRTY_TEMP(mpq_class, upper_bound);
  for (dimension_type i = space_dim; i-- > 0; ) {
    typedef typename Octagonal_Shape<T>::coefficient_type Coeff;
    I_Constraint<mpq_class> lower;
    I_Constraint<mpq_class> upper;
    ITV& seq_i = seq[i];
    const dimension_type ii = 2*i;
    const dimension_type cii = ii + 1;

    // Set the upper bound.
    const Coeff& twice_ub = oct.matrix[cii][ii];
    if (!is_plus_infinity(twice_ub)) {
      assign_r(upper_bound, twice_ub, ROUND_NOT_NEEDED);
      div_2exp_assign_r(upper_bound, upper_bound, 1, ROUND_NOT_NEEDED);
      upper.set(LESS_OR_EQUAL, upper_bound);
    }

    // Set the lower bound.
    const Coeff& twice_lb = oct.matrix[ii][cii];
    if (!is_plus_infinity(twice_lb)) {
      assign_r(lower_bound, twice_lb, ROUND_NOT_NEEDED);
      neg_assign_r(lower_bound, lower_bound, ROUND_NOT_NEEDED);
      div_2exp_assign_r(lower_bound, lower_bound, 1, ROUND_NOT_NEEDED);
      lower.set(GREATER_OR_EQUAL, lower_bound);
    }
    seq_i.build(lower, upper);
  }
}

template <typename ITV>
Box<ITV>::Box(const Polyhedron& ph, Complexity_Class complexity)
  : seq(check_space_dimension_overflow(ph.space_dimension(),
                                       max_space_dimension(),
                                       "PPL::Box::",
                                       "Box(ph)",
                                       "ph exceeds the maximum "
                                       "allowed space dimension")),
    status() {
  // The empty flag will be meaningful, whatever happens from now on.
  set_empty_up_to_date();

  // We do not need to bother about `complexity' if:
  // a) the polyhedron is already marked empty; or ...
  if (ph.marked_empty()) {
    set_empty();
    return;
  }

  // b) the polyhedron is zero-dimensional; or ...
  const dimension_type space_dim = ph.space_dimension();
  if (space_dim == 0) {
    return;
  }

  // c) the polyhedron is already described by a generator system.
  if (ph.generators_are_up_to_date() && !ph.has_pending_constraints()) {
    Box tmp(ph.generators());
    m_swap(tmp);
    return;
  }

  // Here generators are not up-to-date or there are pending constraints.
  PPL_ASSERT(ph.constraints_are_up_to_date());

  if (complexity == POLYNOMIAL_COMPLEXITY) {
    // FIXME: is there a way to avoid this initialization?
    for (dimension_type i = space_dim; i-- > 0; ) {
      seq[i].assign(UNIVERSE);
    }
    // Get a simplified version of the constraints.
    const Constraint_System cs = ph.simplified_constraints();
    // Propagate easy-to-find bounds from the constraints,
    // allowing for a limited number of iterations.
    // FIXME: 20 is just a wild guess.
    const dimension_type max_iterations = 20;
    propagate_constraints_no_check(cs, max_iterations);
  }
  else if (complexity == SIMPLEX_COMPLEXITY) {
    MIP_Problem lp(space_dim);
    const Constraint_System& ph_cs = ph.constraints();
    if (!ph_cs.has_strict_inequalities()) {
      lp.add_constraints(ph_cs);
    }
    else {
      // Adding to `lp' a topologically closed version of `ph_cs'.
      for (Constraint_System::const_iterator i = ph_cs.begin(),
             ph_cs_end = ph_cs.end(); i != ph_cs_end; ++i) {
        const Constraint& c = *i;
        if (c.is_strict_inequality()) {
          const Linear_Expression expr(c.expression());
          lp.add_constraint(expr >= 0);
        }
        else {
          lp.add_constraint(c);
        }
      }
    }
    // Check for unsatisfiability.
    if (!lp.is_satisfiable()) {
      set_empty();
      return;
    }
    // Get all the bounds for the space dimensions.
    Generator g(point());
    PPL_DIRTY_TEMP(mpq_class, lower_bound);
    PPL_DIRTY_TEMP(mpq_class, upper_bound);
    PPL_DIRTY_TEMP_COEFFICIENT(bound_numer);
    PPL_DIRTY_TEMP_COEFFICIENT(bound_denom);
    for (dimension_type i = space_dim; i-- > 0; ) {
      I_Constraint<mpq_class> lower;
      I_Constraint<mpq_class> upper;
      ITV& seq_i = seq[i];
      lp.set_objective_function(Variable(i));
      // Evaluate upper bound.
      lp.set_optimization_mode(MAXIMIZATION);
      if (lp.solve() == OPTIMIZED_MIP_PROBLEM) {
        g = lp.optimizing_point();
        lp.evaluate_objective_function(g, bound_numer, bound_denom);
        assign_r(upper_bound.get_num(), bound_numer, ROUND_NOT_NEEDED);
        assign_r(upper_bound.get_den(), bound_denom, ROUND_NOT_NEEDED);
        PPL_ASSERT(is_canonical(upper_bound));
        upper.set(LESS_OR_EQUAL, upper_bound);
      }
      // Evaluate optimal lower bound.
      lp.set_optimization_mode(MINIMIZATION);
      if (lp.solve() == OPTIMIZED_MIP_PROBLEM) {
        g = lp.optimizing_point();
        lp.evaluate_objective_function(g, bound_numer, bound_denom);
        assign_r(lower_bound.get_num(), bound_numer, ROUND_NOT_NEEDED);
        assign_r(lower_bound.get_den(), bound_denom, ROUND_NOT_NEEDED);
        PPL_ASSERT(is_canonical(lower_bound));
        lower.set(GREATER_OR_EQUAL, lower_bound);
      }
      seq_i.build(lower, upper);
    }
  }
  else {
    PPL_ASSERT(complexity == ANY_COMPLEXITY);
    if (ph.is_empty()) {
      set_empty();
    }
    else {
      Box tmp(ph.generators());
      m_swap(tmp);
    }
  }
}

template <typename ITV>
Box<ITV>::Box(const Grid& gr, Complexity_Class)
  : seq(check_space_dimension_overflow(gr.space_dimension(),
                                       max_space_dimension(),
                                       "PPL::Box::",
                                       "Box(gr)",
                                       "gr exceeds the maximum "
                                       "allowed space dimension")),
    status() {

  if (gr.marked_empty()) {
    set_empty();
    return;
  }

  // The empty flag will be meaningful, whatever happens from now on.
  set_empty_up_to_date();

  const dimension_type space_dim = gr.space_dimension();

  if (space_dim == 0) {
    return;
  }

  if (!gr.generators_are_up_to_date() && !gr.update_generators()) {
    // Updating found the grid empty.
    set_empty();
    return;
  }

  PPL_ASSERT(!gr.gen_sys.empty());

  // For each dimension that is bounded by the grid, set both bounds
  // of the interval to the value of the associated coefficient in a
  // generator point.
  PPL_DIRTY_TEMP(mpq_class, bound);
  PPL_DIRTY_TEMP_COEFFICIENT(bound_numer);
  PPL_DIRTY_TEMP_COEFFICIENT(bound_denom);
  for (dimension_type i = space_dim; i-- > 0; ) {
    ITV& seq_i = seq[i];
    Variable var(i);
    bool max;
    if (gr.maximize(var, bound_numer, bound_denom, max)) {
      assign_r(bound.get_num(), bound_numer, ROUND_NOT_NEEDED);
      assign_r(bound.get_den(), bound_denom, ROUND_NOT_NEEDED);
      bound.canonicalize();
      seq_i.build(i_constraint(EQUAL, bound));
    }
    else {
      seq_i.assign(UNIVERSE);
    }
  }
}

template <typename ITV>
template <typename D1, typename D2, typename R>
Box<ITV>::Box(const Partially_Reduced_Product<D1, D2, R>& dp,
              Complexity_Class complexity)
  : seq(), status() {
  check_space_dimension_overflow(dp.space_dimension(),
                                 max_space_dimension(),
                                 "PPL::Box::",
                                 "Box(dp)",
                                 "dp exceeds the maximum "
                                 "allowed space dimension");
  Box tmp1(dp.domain1(), complexity);
  Box tmp2(dp.domain2(), complexity);
  tmp1.intersection_assign(tmp2);
  m_swap(tmp1);
}

template <typename ITV>
inline void
Box<ITV>::add_space_dimensions_and_embed(const dimension_type m) {
  // Adding no dimensions is a no-op.
  if (m == 0) {
    return;
  }
  check_space_dimension_overflow(m, max_space_dimension() - space_dimension(),
                                 "PPL::Box::",
                                 "add_space_dimensions_and_embed(m)",
                                 "adding m new space dimensions exceeds "
                                 "the maximum allowed space dimension");
  // To embed an n-dimension space box in a (n+m)-dimension space,
  // we just add `m' new universe elements to the sequence.
  seq.insert(seq.end(), m, ITV(UNIVERSE));
  PPL_ASSERT(OK());
}

template <typename ITV>
inline void
Box<ITV>::add_space_dimensions_and_project(const dimension_type m) {
  // Adding no dimensions is a no-op.
  if (m == 0) {
    return;
  }
  check_space_dimension_overflow(m, max_space_dimension() - space_dimension(),
                                 "PPL::Box::",
                                 "add_space_dimensions_and_project(m)",
                                 "adding m new space dimensions exceeds "
                                 "the maximum allowed space dimension");
  // Add `m' new zero elements to the sequence.
  seq.insert(seq.end(), m, ITV(0));
  PPL_ASSERT(OK());
}

template <typename ITV>
bool
operator==(const Box<ITV>& x, const Box<ITV>& y) {
  const dimension_type x_space_dim = x.space_dimension();
  if (x_space_dim != y.space_dimension()) {
    return false;
  }

  if (x.is_empty()) {
    return y.is_empty();
  }

  if (y.is_empty()) {
    return x.is_empty();
  }

  for (dimension_type k = x_space_dim; k-- > 0; ) {
    if (x.seq[k] != y.seq[k]) {
      return false;
    }
  }
  return true;
}

template <typename ITV>
bool
Box<ITV>::bounds(const Linear_Expression& expr, const bool from_above) const {
  // `expr' should be dimension-compatible with `*this'.
  const dimension_type expr_space_dim = expr.space_dimension();
  const dimension_type space_dim = space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible((from_above
                                  ? "bounds_from_above(e)"
                                  : "bounds_from_below(e)"), "e", expr);
  }
  // A zero-dimensional or empty Box bounds everything.
  if (space_dim == 0 || is_empty()) {
    return true;
  }
  const int from_above_sign = from_above ? 1 : -1;
  // TODO: This loop can be optimized more, if needed, exploiting the
  // (possible) sparseness of expr.
  for (Linear_Expression::const_iterator i = expr.begin(),
          i_end = expr.end(); i != i_end; ++i) {
    const Variable v = i.variable();
    switch (sgn(*i) * from_above_sign) {
    case 1:
      if (seq[v.id()].upper_is_boundary_infinity()) {
        return false;
      }
      break;
    case 0:
      PPL_UNREACHABLE;
      break;
    case -1:
      if (seq[v.id()].lower_is_boundary_infinity()) {
        return false;
      }
      break;
    }
  }
  return true;
}

template <typename ITV>
Poly_Con_Relation
interval_relation(const ITV& i,
                  const Constraint::Type constraint_type,
                  Coefficient_traits::const_reference numer,
                  Coefficient_traits::const_reference denom) {

  if (i.is_universe()) {
    return Poly_Con_Relation::strictly_intersects();
  }

  PPL_DIRTY_TEMP(mpq_class, bound);
  assign_r(bound.get_num(), numer, ROUND_NOT_NEEDED);
  assign_r(bound.get_den(), denom, ROUND_NOT_NEEDED);
  bound.canonicalize();
  neg_assign_r(bound, bound, ROUND_NOT_NEEDED);
  const bool is_lower_bound = (denom > 0);

  PPL_DIRTY_TEMP(mpq_class, bound_diff);
  if (constraint_type == Constraint::EQUALITY) {
    if (i.lower_is_boundary_infinity()) {
      PPL_ASSERT(!i.upper_is_boundary_infinity());
      assign_r(bound_diff, i.upper(), ROUND_NOT_NEEDED);
      sub_assign_r(bound_diff, bound_diff, bound, ROUND_NOT_NEEDED);
      switch (sgn(bound_diff)) {
      case 1:
        return Poly_Con_Relation::strictly_intersects();
      case 0:
        return i.upper_is_open()
          ? Poly_Con_Relation::is_disjoint()
          : Poly_Con_Relation::strictly_intersects();
      case -1:
        return Poly_Con_Relation::is_disjoint();
      }
    }
    else {
      assign_r(bound_diff, i.lower(), ROUND_NOT_NEEDED);
      sub_assign_r(bound_diff, bound_diff, bound, ROUND_NOT_NEEDED);
      switch (sgn(bound_diff)) {
      case 1:
        return Poly_Con_Relation::is_disjoint();
      case 0:
        if (i.lower_is_open()) {
          return Poly_Con_Relation::is_disjoint();
        }
        if (i.is_singleton()) {
          return Poly_Con_Relation::is_included()
            && Poly_Con_Relation::saturates();
        }
        return Poly_Con_Relation::strictly_intersects();
      case -1:
        if (i.upper_is_boundary_infinity()) {
          return Poly_Con_Relation::strictly_intersects();
        }
        else {
          assign_r(bound_diff, i.upper(), ROUND_NOT_NEEDED);
          sub_assign_r(bound_diff, bound_diff, bound, ROUND_NOT_NEEDED);
          switch (sgn(bound_diff)) {
          case 1:
            return Poly_Con_Relation::strictly_intersects();
          case 0:
            if (i.upper_is_open()) {
              return Poly_Con_Relation::is_disjoint();
            }
            else {
              return Poly_Con_Relation::strictly_intersects();
            }
          case -1:
            return Poly_Con_Relation::is_disjoint();
          }
        }
      }
    }
  }

  PPL_ASSERT(constraint_type != Constraint::EQUALITY);
  if (is_lower_bound) {
    if (i.lower_is_boundary_infinity()) {
      PPL_ASSERT(!i.upper_is_boundary_infinity());
      assign_r(bound_diff, i.upper(), ROUND_NOT_NEEDED);
      sub_assign_r(bound_diff, bound_diff, bound, ROUND_NOT_NEEDED);
      switch (sgn(bound_diff)) {
      case 1:
        return Poly_Con_Relation::strictly_intersects();
      case 0:
        if (constraint_type == Constraint::STRICT_INEQUALITY
            || i.upper_is_open()) {
          return Poly_Con_Relation::is_disjoint();
        }
        else {
          return Poly_Con_Relation::strictly_intersects();
        }
      case -1:
        return Poly_Con_Relation::is_disjoint();
      }
    }
    else {
      assign_r(bound_diff, i.lower(), ROUND_NOT_NEEDED);
      sub_assign_r(bound_diff, bound_diff, bound, ROUND_NOT_NEEDED);
      switch (sgn(bound_diff)) {
      case 1:
        return Poly_Con_Relation::is_included();
      case 0:
        if (constraint_type == Constraint::NONSTRICT_INEQUALITY
            || i.lower_is_open()) {
          Poly_Con_Relation result = Poly_Con_Relation::is_included();
          if (i.is_singleton()) {
            result = result && Poly_Con_Relation::saturates();
          }
          return result;
        }
        else {
          PPL_ASSERT(constraint_type == Constraint::STRICT_INEQUALITY
                 && !i.lower_is_open());
          if (i.is_singleton()) {
            return Poly_Con_Relation::is_disjoint()
              && Poly_Con_Relation::saturates();
          }
          else {
            return Poly_Con_Relation::strictly_intersects();
          }
        }
      case -1:
        if (i.upper_is_boundary_infinity()) {
          return Poly_Con_Relation::strictly_intersects();
        }
        else {
          assign_r(bound_diff, i.upper(), ROUND_NOT_NEEDED);
          sub_assign_r(bound_diff, bound_diff, bound, ROUND_NOT_NEEDED);
          switch (sgn(bound_diff)) {
          case 1:
            return Poly_Con_Relation::strictly_intersects();
          case 0:
            if (constraint_type == Constraint::STRICT_INEQUALITY
                || i.upper_is_open()) {
              return Poly_Con_Relation::is_disjoint();
            }
            else {
              return Poly_Con_Relation::strictly_intersects();
            }
          case -1:
            return Poly_Con_Relation::is_disjoint();
          }
        }
      }
    }
  }
  else {
    // `c' is an upper bound.
    if (i.upper_is_boundary_infinity()) {
      return Poly_Con_Relation::strictly_intersects();
    }
    else {
      assign_r(bound_diff, i.upper(), ROUND_NOT_NEEDED);
      sub_assign_r(bound_diff, bound_diff, bound, ROUND_NOT_NEEDED);
      switch (sgn(bound_diff)) {
      case -1:
        return Poly_Con_Relation::is_included();
      case 0:
        if (constraint_type == Constraint::NONSTRICT_INEQUALITY
            || i.upper_is_open()) {
          Poly_Con_Relation result = Poly_Con_Relation::is_included();
          if (i.is_singleton()) {
            result = result && Poly_Con_Relation::saturates();
          }
          return result;
        }
        else {
          PPL_ASSERT(constraint_type == Constraint::STRICT_INEQUALITY
                 && !i.upper_is_open());
          if (i.is_singleton()) {
            return Poly_Con_Relation::is_disjoint()
              && Poly_Con_Relation::saturates();
          }
          else {
            return Poly_Con_Relation::strictly_intersects();
          }
        }
      case 1:
        if (i.lower_is_boundary_infinity()) {
          return Poly_Con_Relation::strictly_intersects();
        }
        else {
          assign_r(bound_diff, i.lower(), ROUND_NOT_NEEDED);
          sub_assign_r(bound_diff, bound_diff, bound, ROUND_NOT_NEEDED);
          switch (sgn(bound_diff)) {
          case -1:
            return Poly_Con_Relation::strictly_intersects();
          case 0:
            if (constraint_type == Constraint::STRICT_INEQUALITY
                || i.lower_is_open()) {
              return Poly_Con_Relation::is_disjoint();
            }
            else {
              return Poly_Con_Relation::strictly_intersects();
            }
          case 1:
            return Poly_Con_Relation::is_disjoint();
          }
        }
      }
    }
  }

  // Quiet a compiler warning: this program point is unreachable.
  PPL_UNREACHABLE;
  return Poly_Con_Relation::nothing();
}

template <typename ITV>
Poly_Con_Relation
Box<ITV>::relation_with(const Congruence& cg) const {
  const dimension_type cg_space_dim = cg.space_dimension();
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (cg_space_dim > space_dim) {
    throw_dimension_incompatible("relation_with(cg)", cg);
  }
  if (is_empty()) {
    return Poly_Con_Relation::saturates()
      && Poly_Con_Relation::is_included()
      && Poly_Con_Relation::is_disjoint();
  }

  if (space_dim == 0) {
    if (cg.is_inconsistent()) {
      return Poly_Con_Relation::is_disjoint();
    }
    else {
      return Poly_Con_Relation::saturates()
        && Poly_Con_Relation::is_included();
    }
  }

  if (cg.is_equality()) {
    const Constraint c(cg);
    return relation_with(c);
  }

  PPL_DIRTY_TEMP(Rational_Interval, r);
  PPL_DIRTY_TEMP(Rational_Interval, t);
  PPL_DIRTY_TEMP(mpq_class, m);
  r = 0;
  for (Congruence::expr_type::const_iterator i = cg.expression().begin(),
      i_end = cg.expression().end(); i != i_end; ++i) {
    const Coefficient& cg_i = *i;
    const Variable v = i.variable();
    assign_r(m, cg_i, ROUND_NOT_NEEDED);
    // FIXME: an add_mul_assign() method would come handy here.
    t.build(seq[v.id()].lower_constraint(), seq[v.id()].upper_constraint());
    t *= m;
    r += t;
  }

  if (r.lower_is_boundary_infinity() || r.upper_is_boundary_infinity()) {
    return Poly_Con_Relation::strictly_intersects();
  }

  // Find the value that satisfies the congruence and is
  // nearest to the lower bound such that the point lies on or above it.

  PPL_DIRTY_TEMP_COEFFICIENT(lower);
  PPL_DIRTY_TEMP_COEFFICIENT(mod);
  PPL_DIRTY_TEMP_COEFFICIENT(v);
  mod = cg.modulus();
  v = cg.inhomogeneous_term() % mod;
  assign_r(lower, r.lower(), ROUND_DOWN);
  v -= ((lower / mod) * mod);
  if (v + lower > 0) {
    v -= mod;
  }
  return interval_relation(r, Constraint::EQUALITY, v);
}

template <typename ITV>
Poly_Con_Relation
Box<ITV>::relation_with(const Constraint& c) const {
  const dimension_type c_space_dim = c.space_dimension();
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (c_space_dim > space_dim) {
    throw_dimension_incompatible("relation_with(c)", c);
  }

  if (is_empty()) {
    return Poly_Con_Relation::saturates()
      && Poly_Con_Relation::is_included()
      && Poly_Con_Relation::is_disjoint();
  }

  if (space_dim == 0) {
    if ((c.is_equality() && c.inhomogeneous_term() != 0)
        || (c.is_inequality() && c.inhomogeneous_term() < 0)) {
      return Poly_Con_Relation::is_disjoint();
    }
    else if (c.is_strict_inequality() && c.inhomogeneous_term() == 0) {
      // The constraint 0 > 0 implicitly defines the hyperplane 0 = 0;
      // thus, the zero-dimensional point also saturates it.
      return Poly_Con_Relation::saturates()
        && Poly_Con_Relation::is_disjoint();
    }
    else if (c.is_equality() || c.inhomogeneous_term() == 0) {
      return Poly_Con_Relation::saturates()
        && Poly_Con_Relation::is_included();
    }
    else {
      // The zero-dimensional point saturates
      // neither the positivity constraint 1 >= 0,
      // nor the strict positivity constraint 1 > 0.
      return Poly_Con_Relation::is_included();
    }
  }

  dimension_type c_num_vars = 0;
  dimension_type c_only_var = 0;

  if (Box_Helpers::extract_interval_constraint(c, c_num_vars, c_only_var)) {
    if (c_num_vars == 0) {
      // c is a trivial constraint.
      switch (sgn(c.inhomogeneous_term())) {
      case -1:
        return Poly_Con_Relation::is_disjoint();
      case 0:
        if (c.is_strict_inequality()) {
          return Poly_Con_Relation::saturates()
            && Poly_Con_Relation::is_disjoint();
        }
        else {
          return Poly_Con_Relation::saturates()
            && Poly_Con_Relation::is_included();
        }
      case 1:
        return Poly_Con_Relation::is_included();
      }
    }
    else {
      // c is an interval constraint.
      return interval_relation(seq[c_only_var],
                               c.type(),
                               c.inhomogeneous_term(),
                               c.coefficient(Variable(c_only_var)));
    }
  }
  else {
    // Deal with a non-trivial and non-interval constraint.
    PPL_DIRTY_TEMP(Rational_Interval, r);
    PPL_DIRTY_TEMP(Rational_Interval, t);
    PPL_DIRTY_TEMP(mpq_class, m);
    r = 0;
    const Constraint::expr_type& e = c.expression();
    for (Constraint::expr_type::const_iterator i = e.begin(), i_end = e.end();
          i != i_end; ++i) {
      assign_r(m, *i, ROUND_NOT_NEEDED);
      const Variable v = i.variable();
      // FIXME: an add_mul_assign() method would come handy here.
      t.build(seq[v.id()].lower_constraint(), seq[v.id()].upper_constraint());
      t *= m;
      r += t;
    }
    return interval_relation(r,
                             c.type(),
                             c.inhomogeneous_term());
  }

  // Quiet a compiler warning: this program point is unreachable.
  PPL_UNREACHABLE;
  return Poly_Con_Relation::nothing();
}

template <typename ITV>
Poly_Gen_Relation
Box<ITV>::relation_with(const Generator& g) const {
  const dimension_type space_dim = space_dimension();
  const dimension_type g_space_dim = g.space_dimension();

  // Dimension-compatibility check.
  if (space_dim < g_space_dim) {
    throw_dimension_incompatible("relation_with(g)", g);
  }

  // The empty box cannot subsume a generator.
  if (is_empty()) {
    return Poly_Gen_Relation::nothing();
  }

  // A universe box in a zero-dimensional space subsumes
  // all the generators of a zero-dimensional space.
  if (space_dim == 0) {
    return Poly_Gen_Relation::subsumes();
  }

  if (g.is_line_or_ray()) {
    if (g.is_line()) {
      const Generator::expr_type& e = g.expression();
      for (Generator::expr_type::const_iterator i = e.begin(), i_end = e.end();
           i != i_end; ++i) {
        if (!seq[i.variable().id()].is_universe()) {
          return Poly_Gen_Relation::nothing();
        }
      }
      return Poly_Gen_Relation::subsumes();
    }
    else {
      PPL_ASSERT(g.is_ray());
      const Generator::expr_type& e = g.expression();
      for (Generator::expr_type::const_iterator i = e.begin(), i_end = e.end();
           i != i_end; ++i) {
        const Variable v = i.variable();
        switch (sgn(*i)) {
        case 1:
          if (!seq[v.id()].upper_is_boundary_infinity()) {
            return Poly_Gen_Relation::nothing();
          }
          break;
        case 0:
          PPL_UNREACHABLE;
          break;
        case -1:
          if (!seq[v.id()].lower_is_boundary_infinity()) {
            return Poly_Gen_Relation::nothing();
          }
          break;
        }
      }
      return Poly_Gen_Relation::subsumes();
    }
  }

  // Here `g' is a point or closure point.
  const Coefficient& g_divisor = g.divisor();
  PPL_DIRTY_TEMP(mpq_class, g_coord);
  PPL_DIRTY_TEMP(mpq_class, bound);
  // TODO: If the variables in the expression that have coefficient 0
  // have no effect on seq[i], this loop can be optimized using
  // Generator::expr_type::const_iterator.
  for (dimension_type i = g_space_dim; i-- > 0; ) {
    const ITV& seq_i = seq[i];
    if (seq_i.is_universe()) {
      continue;
    }
    assign_r(g_coord.get_num(), g.coefficient(Variable(i)), ROUND_NOT_NEEDED);
    assign_r(g_coord.get_den(), g_divisor, ROUND_NOT_NEEDED);
    g_coord.canonicalize();
    // Check lower bound.
    if (!seq_i.lower_is_boundary_infinity()) {
      assign_r(bound, seq_i.lower(), ROUND_NOT_NEEDED);
      if (g_coord <= bound) {
        if (seq_i.lower_is_open()) {
          if (g.is_point() || g_coord != bound) {
            return Poly_Gen_Relation::nothing();
          }
        }
        else if (g_coord != bound) {
          return Poly_Gen_Relation::nothing();
        }
      }
    }
    // Check upper bound.
    if (!seq_i.upper_is_boundary_infinity()) {
      assign_r(bound, seq_i.upper(), ROUND_NOT_NEEDED);
      if (g_coord >= bound) {
        if (seq_i.upper_is_open()) {
          if (g.is_point() || g_coord != bound) {
            return Poly_Gen_Relation::nothing();
          }
        }
        else if (g_coord != bound) {
          return Poly_Gen_Relation::nothing();
        }
      }
    }
  }
  return Poly_Gen_Relation::subsumes();
}


template <typename ITV>
bool
Box<ITV>::max_min(const Linear_Expression& expr,
                  const bool maximize,
                  Coefficient& ext_n, Coefficient& ext_d,
                  bool& included) const {
  // `expr' should be dimension-compatible with `*this'.
  const dimension_type space_dim = space_dimension();
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible((maximize
                                  ? "maximize(e, ...)"
                                  : "minimize(e, ...)"), "e", expr);
  }

  // Deal with zero-dim Box first.
  if (space_dim == 0) {
    if (marked_empty()) {
      return false;
    }
    else {
      ext_n = expr.inhomogeneous_term();
      ext_d = 1;
      included = true;
      return true;
    }
  }

  // For an empty Box we simply return false.
  if (is_empty()) {
    return false;
  }

  PPL_DIRTY_TEMP(mpq_class, result);
  assign_r(result, expr.inhomogeneous_term(), ROUND_NOT_NEEDED);
  bool is_included = true;
  const int maximize_sign = maximize ? 1 : -1;
  PPL_DIRTY_TEMP(mpq_class, bound_i);
  PPL_DIRTY_TEMP(mpq_class, expr_i);
  for (Linear_Expression::const_iterator i = expr.begin(),
          i_end = expr.end(); i != i_end; ++i) {
    const ITV& seq_i = seq[i.variable().id()];
    assign_r(expr_i, *i, ROUND_NOT_NEEDED);
    switch (sgn(expr_i) * maximize_sign) {
    case 1:
      if (seq_i.upper_is_boundary_infinity()) {
        return false;
      }
      assign_r(bound_i, seq_i.upper(), ROUND_NOT_NEEDED);
      add_mul_assign_r(result, bound_i, expr_i, ROUND_NOT_NEEDED);
      if (seq_i.upper_is_open()) {
        is_included = false;
      }
      break;
    case 0:
      PPL_UNREACHABLE;
      break;
    case -1:
      if (seq_i.lower_is_boundary_infinity()) {
        return false;
      }
      assign_r(bound_i, seq_i.lower(), ROUND_NOT_NEEDED);
      add_mul_assign_r(result, bound_i, expr_i, ROUND_NOT_NEEDED);
      if (seq_i.lower_is_open()) {
        is_included = false;
      }
      break;
    }
  }
  // Extract output info.
  PPL_ASSERT(is_canonical(result));
  ext_n = result.get_num();
  ext_d = result.get_den();
  included = is_included;
  return true;
}

template <typename ITV>
bool
Box<ITV>::max_min(const Linear_Expression& expr,
                  const bool maximize,
                  Coefficient& ext_n, Coefficient& ext_d,
                  bool& included,
                  Generator& g) const {
  if (!max_min(expr, maximize, ext_n, ext_d, included)) {
    return false;
  }
  // Compute generator `g'.
  Linear_Expression g_expr;
  PPL_DIRTY_TEMP_COEFFICIENT(g_divisor);
  g_divisor = 1;
  const int maximize_sign = maximize ? 1 : -1;
  PPL_DIRTY_TEMP(mpq_class, g_coord);
  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);
  PPL_DIRTY_TEMP_COEFFICIENT(lcm);
  PPL_DIRTY_TEMP_COEFFICIENT(factor);
  // TODO: Check if the following loop can be optimized to exploit the
  // (possible) sparseness of expr.
  for (dimension_type i = space_dimension(); i-- > 0; ) {
    const ITV& seq_i = seq[i];
    switch (sgn(expr.coefficient(Variable(i))) * maximize_sign) {
    case 1:
      assign_r(g_coord, seq_i.upper(), ROUND_NOT_NEEDED);
      break;
    case 0:
      // If 0 belongs to the interval, choose it
      // (and directly proceed to the next iteration).
      // FIXME: name qualification issue.
      if (seq_i.contains(0)) {
        continue;
      }
      if (!seq_i.lower_is_boundary_infinity()) {
        if (seq_i.lower_is_open()) {
          if (!seq_i.upper_is_boundary_infinity()) {
            if (seq_i.upper_is_open()) {
              // Bounded and open interval: compute middle point.
              assign_r(g_coord, seq_i.lower(), ROUND_NOT_NEEDED);
              PPL_DIRTY_TEMP(mpq_class, q_seq_i_upper);
              assign_r(q_seq_i_upper, seq_i.upper(), ROUND_NOT_NEEDED);
              g_coord += q_seq_i_upper;
              g_coord /= 2;
            }
            else {
              // The upper bound is in the interval.
              assign_r(g_coord, seq_i.upper(), ROUND_NOT_NEEDED);
            }
          }
          else {
            // Lower is open, upper is unbounded.
            assign_r(g_coord, seq_i.lower(), ROUND_NOT_NEEDED);
            ++g_coord;
          }
        }
        else {
          // The lower bound is in the interval.
          assign_r(g_coord, seq_i.lower(), ROUND_NOT_NEEDED);
        }
      }
      else {
        // Lower is unbounded, hence upper is bounded
        // (since we know that 0 does not belong to the interval).
        PPL_ASSERT(!seq_i.upper_is_boundary_infinity());
        assign_r(g_coord, seq_i.upper(), ROUND_NOT_NEEDED);
        if (seq_i.upper_is_open()) {
          --g_coord;
        }
      }
      break;
    case -1:
      assign_r(g_coord, seq_i.lower(), ROUND_NOT_NEEDED);
      break;
    }
    // Add g_coord * Variable(i) to the generator.
    assign_r(denom, g_coord.get_den(), ROUND_NOT_NEEDED);
    lcm_assign(lcm, g_divisor, denom);
    exact_div_assign(factor, lcm, g_divisor);
    g_expr *= factor;
    exact_div_assign(factor, lcm, denom);
    assign_r(numer, g_coord.get_num(), ROUND_NOT_NEEDED);
    numer *= factor;
    g_expr += numer * Variable(i);
    g_divisor = lcm;
  }
  g = Generator::point(g_expr, g_divisor);
  return true;
}

template <typename ITV>
bool
Box<ITV>::contains(const Box& y) const {
  const Box& x = *this;
  // Dimension-compatibility check.
  if (x.space_dimension() != y.space_dimension()) {
    x.throw_dimension_incompatible("contains(y)", y);
  }

  // If `y' is empty, then `x' contains `y'.
  if (y.is_empty()) {
    return true;
  }

  // If `x' is empty, then `x' cannot contain `y'.
  if (x.is_empty()) {
    return false;
  }

  for (dimension_type k = x.seq.size(); k-- > 0; ) {
    // FIXME: fix this name qualification issue.
    if (!x.seq[k].contains(y.seq[k])) {
      return false;
    }
  }
  return true;
}

template <typename ITV>
bool
Box<ITV>::is_disjoint_from(const Box& y) const {
  const Box& x = *this;
  // Dimension-compatibility check.
  if (x.space_dimension() != y.space_dimension()) {
    x.throw_dimension_incompatible("is_disjoint_from(y)", y);
  }

  // If any of `x' or `y' is marked empty, then they are disjoint.
  // Note: no need to use `is_empty', as the following loop is anyway correct.
  if (x.marked_empty() || y.marked_empty()) {
    return true;
  }

  for (dimension_type k = x.seq.size(); k-- > 0; ) {
    // FIXME: fix this name qualification issue.
    if (x.seq[k].is_disjoint_from(y.seq[k])) {
      return true;
    }
  }
  return false;
}

template <typename ITV>
inline bool
Box<ITV>::upper_bound_assign_if_exact(const Box& y) {
  Box& x = *this;

  // Dimension-compatibility check.
  if (x.space_dimension() != y.space_dimension()) {
    x.throw_dimension_incompatible("upper_bound_assign_if_exact(y)", y);
  }

  // The lub of a box with an empty box is equal to the first box.
  if (y.is_empty()) {
    return true;
  }
  if (x.is_empty()) {
    x = y;
    return true;
  }

  bool x_j_does_not_contain_y_j = false;
  bool y_j_does_not_contain_x_j = false;

  for (dimension_type i = x.seq.size(); i-- > 0; ) {
    const ITV& x_seq_i = x.seq[i];
    const ITV& y_seq_i = y.seq[i];

    if (!x_seq_i.can_be_exactly_joined_to(y_seq_i)) {
      return false;
    }

    // Note: the use of `y_i_does_not_contain_x_i' is needed
    // because we want to temporarily preserve the old value
    // of `y_j_does_not_contain_x_j'.
    bool y_i_does_not_contain_x_i = !y_seq_i.contains(x_seq_i);
    if (y_i_does_not_contain_x_i && x_j_does_not_contain_y_j) {
      return false;
    }
    if (!x_seq_i.contains(y_seq_i)) {
      if (y_j_does_not_contain_x_j) {
        return false;
      }
      else {
        x_j_does_not_contain_y_j = true;
      }
    }
    if (y_i_does_not_contain_x_i) {
      y_j_does_not_contain_x_j = true;
    }
  }

  // The upper bound is exact: compute it into *this.
  for (dimension_type k = x.seq.size(); k-- > 0; ) {
    x.seq[k].join_assign(y.seq[k]);
  }
  return true;
}

template <typename ITV>
bool
Box<ITV>::OK() const {
  if (status.test_empty_up_to_date() && !status.test_empty()) {
    Box tmp = *this;
    tmp.reset_empty_up_to_date();
    if (tmp.check_empty()) {
#ifndef NDEBUG
      std::cerr << "The box is empty, but it is marked as non-empty."
                << std::endl;
#endif // NDEBUG
      return false;
    }
  }

  // A box that is not marked empty must have meaningful intervals.
  if (!marked_empty()) {
    for (dimension_type k = seq.size(); k-- > 0; ) {
      if (!seq[k].OK()) {
        return false;
      }
    }
  }

  return true;
}

template <typename ITV>
dimension_type
Box<ITV>::affine_dimension() const {
  dimension_type d = space_dimension();
  // A zero-space-dim box always has affine dimension zero.
  if (d == 0) {
    return 0;
  }

  // An empty box has affine dimension zero.
  if (is_empty()) {
    return 0;
  }

  for (dimension_type k = d; k-- > 0; ) {
    if (seq[k].is_singleton()) {
      --d;
    }
  }

  return d;
}

template <typename ITV>
bool
Box<ITV>::check_empty() const {
  PPL_ASSERT(!marked_empty());
  Box<ITV>& x = const_cast<Box<ITV>&>(*this);
  for (dimension_type k = seq.size(); k-- > 0; ) {
    if (seq[k].is_empty()) {
      x.set_empty();
      return true;
    }
  }
  x.set_nonempty();
  return false;
}

template <typename ITV>
bool
Box<ITV>::is_universe() const {
  if (marked_empty()) {
    return false;
  }
  for (dimension_type k = seq.size(); k-- > 0; ) {
    if (!seq[k].is_universe()) {
      return false;
    }
  }
  return true;
}

template <typename ITV>
bool
Box<ITV>::is_topologically_closed() const {
  if (ITV::is_always_topologically_closed() || is_empty()) {
    return true;
  }

  for (dimension_type k = seq.size(); k-- > 0; ) {
    if (!seq[k].is_topologically_closed()) {
      return false;
    }
  }
  return true;
}

template <typename ITV>
bool
Box<ITV>::is_discrete() const {
  if (is_empty()) {
    return true;
  }
  for (dimension_type k = seq.size(); k-- > 0; ) {
    if (!seq[k].is_singleton()) {
      return false;
    }
  }
  return true;
}

template <typename ITV>
bool
Box<ITV>::is_bounded() const {
  if (is_empty()) {
    return true;
  }
  for (dimension_type k = seq.size(); k-- > 0; ) {
    if (!seq[k].is_bounded()) {
      return false;
    }
  }
  return true;
}

template <typename ITV>
bool
Box<ITV>::contains_integer_point() const {
  if (marked_empty()) {
    return false;
  }
  for (dimension_type k = seq.size(); k-- > 0; ) {
    if (!seq[k].contains_integer_point()) {
      return false;
    }
  }
  return true;
}

template <typename ITV>
bool
Box<ITV>::frequency(const Linear_Expression& expr,
                  Coefficient& freq_n, Coefficient& freq_d,
                  Coefficient& val_n, Coefficient& val_d) const {
  dimension_type space_dim = space_dimension();
  // The dimension of `expr' must be at most the dimension of *this.
  if (space_dim < expr.space_dimension()) {
    throw_dimension_incompatible("frequency(e, ...)", "e", expr);
  }

  // Check if `expr' has a constant value.
  // If it is constant, set the frequency `freq_n' to 0
  // and return true. Otherwise the values for \p expr
  // are not discrete so return false.

  // Space dimension is 0: if empty, then return false;
  // otherwise the frequency is 0 and the value is the inhomogeneous term.
  if (space_dim == 0) {
    if (is_empty()) {
      return false;
    }
    freq_n = 0;
    freq_d = 1;
    val_n = expr.inhomogeneous_term();
    val_d = 1;
    return true;
  }

  // For an empty Box, we simply return false.
  if (is_empty()) {
    return false;
  }

  // The Box has at least 1 dimension and is not empty.
  PPL_DIRTY_TEMP_COEFFICIENT(numer);
  PPL_DIRTY_TEMP_COEFFICIENT(denom);
  PPL_DIRTY_TEMP(mpq_class, tmp);
  Coefficient c = expr.inhomogeneous_term();

  PPL_DIRTY_TEMP_COEFFICIENT(val_denom);
  val_denom = 1;

  for (Linear_Expression::const_iterator i = expr.begin(), i_end = expr.end();
       i != i_end; ++i) {
    const ITV& seq_i = seq[i.variable().id()];
    // Check if `v' is constant in the BD shape.
    if (seq_i.is_singleton()) {
      // If `v' is constant, replace it in `le' by the value.
      assign_r(tmp, seq_i.lower(), ROUND_NOT_NEEDED);
      numer = tmp.get_num();
      denom = tmp.get_den();
      c *= denom;
      c += numer * val_denom * (*i);
      val_denom *= denom;
      continue;
    }
    // The expression `expr' is not constant.
    return false;
  }

  // The expression `expr' is constant.
  freq_n = 0;
  freq_d = 1;

  // Reduce `val_n' and `val_d'.
  normalize2(c, val_denom, val_n, val_d);
  return true;
}

template <typename ITV>
bool
Box<ITV>::constrains(Variable var) const {
  // `var' should be one of the dimensions of the polyhedron.
  const dimension_type var_space_dim = var.space_dimension();
  if (space_dimension() < var_space_dim) {
    throw_dimension_incompatible("constrains(v)", "v", var);
  }

  if (marked_empty() || !seq[var_space_dim-1].is_universe()) {
    return true;
  }
  // Now force an emptiness check.
  return is_empty();
}

template <typename ITV>
void
Box<ITV>::unconstrain(const Variables_Set& vars) {
  // The cylindrification with respect to no dimensions is a no-op.
  // This case also captures the only legal cylindrification
  // of a box in a 0-dim space.
  if (vars.empty()) {
    return;
  }

  // Dimension-compatibility check.
  const dimension_type min_space_dim = vars.space_dimension();
  if (space_dimension() < min_space_dim) {
    throw_dimension_incompatible("unconstrain(vs)", min_space_dim);
  }
  // If the box is already empty, there is nothing left to do.
  if (marked_empty()) {
    return;
  }

  // Here the box might still be empty (but we haven't detected it yet):
  // check emptiness of the interval for each of the variables in
  // `vars' before cylindrification.
  for (Variables_Set::const_iterator vsi = vars.begin(),
         vsi_end = vars.end(); vsi != vsi_end; ++vsi) {
    ITV& seq_vsi = seq[*vsi];
    if (!seq_vsi.is_empty()) {
      seq_vsi.assign(UNIVERSE);
    }
    else {
      set_empty();
      break;
    }
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>::topological_closure_assign() {
  if (ITV::is_always_topologically_closed() || is_empty()) {
    return;
  }

  for (dimension_type k = seq.size(); k-- > 0; ) {
    seq[k].topological_closure_assign();
  }
}

template <typename ITV>
void
Box<ITV>::wrap_assign(const Variables_Set& vars,
                      Bounded_Integer_Type_Width w,
                      Bounded_Integer_Type_Representation r,
                      Bounded_Integer_Type_Overflow o,
                      const Constraint_System* cs_p,
                      unsigned complexity_threshold,
                      bool wrap_individually) {
#if 0 // Generic implementation commented out.
  Implementation::wrap_assign(*this,
                              vars, w, r, o, cs_p,
                              complexity_threshold, wrap_individually,
                              "Box");
#else // Specialized implementation.
  PPL_USED(wrap_individually);
  PPL_USED(complexity_threshold);
  Box& x = *this;

  // Dimension-compatibility check for `*cs_p', if any.
  const dimension_type vars_space_dim = vars.space_dimension();
  if (cs_p != 0 && cs_p->space_dimension() > vars_space_dim) {
    std::ostringstream s;
    s << "PPL::Box<ITV>::wrap_assign(vars, w, r, o, cs_p, ...):"
      << std::endl
      << "vars.space_dimension() == " << vars_space_dim
      << ", cs_p->space_dimension() == " << cs_p->space_dimension() << ".";
    throw std::invalid_argument(s.str());
  }

  // Wrapping no variable only requires refining with *cs_p, if any.
  if (vars.empty()) {
    if (cs_p != 0) {
      refine_with_constraints(*cs_p);
    }
    return;
  }

  // Dimension-compatibility check for `vars'.
  const dimension_type space_dim = x.space_dimension();
  if (space_dim < vars_space_dim) {
    std::ostringstream s;
    s << "PPL::Box<ITV>::wrap_assign(vars, ...):"
      << std::endl
      << "this->space_dimension() == " << space_dim
      << ", required space dimension == " << vars_space_dim << ".";
    throw std::invalid_argument(s.str());
  }

  // Wrapping an empty polyhedron is a no-op.
  if (x.is_empty()) {
    return;
  }

  // FIXME: temporarily (ab-) using Coefficient.
  // Set `min_value' and `max_value' to the minimum and maximum values
  // a variable of width `w' and signedness `s' can take.
  PPL_DIRTY_TEMP_COEFFICIENT(min_value);
  PPL_DIRTY_TEMP_COEFFICIENT(max_value);
  if (r == UNSIGNED) {
    min_value = 0;
    mul_2exp_assign(max_value, Coefficient_one(), w);
    --max_value;
  }
  else {
    PPL_ASSERT(r == SIGNED_2_COMPLEMENT);
    mul_2exp_assign(max_value, Coefficient_one(), w-1);
    neg_assign(min_value, max_value);
    --max_value;
  }

  // FIXME: Build the (integer) quadrant interval.
  PPL_DIRTY_TEMP(ITV, integer_quadrant_itv);
  PPL_DIRTY_TEMP(ITV, rational_quadrant_itv);
  {
    I_Constraint<Coefficient> lower = i_constraint(GREATER_OR_EQUAL, min_value);
    I_Constraint<Coefficient> upper = i_constraint(LESS_OR_EQUAL, max_value);
    integer_quadrant_itv.build(lower, upper);
    // The rational quadrant is only needed if overflow is undefined.
    if (o == OVERFLOW_UNDEFINED) {
      ++max_value;
      upper = i_constraint(LESS_THAN, max_value);
      rational_quadrant_itv.build(lower, upper);
    }
  }

  const Variables_Set::const_iterator vs_end = vars.end();

  if (cs_p == 0) {
    // No constraint refinement is needed here.
    switch (o) {
    case OVERFLOW_WRAPS:
      for (Variables_Set::const_iterator i = vars.begin(); i != vs_end; ++i) {
        x.seq[*i].wrap_assign(w, r, integer_quadrant_itv);
      }
      reset_empty_up_to_date();
      break;
    case OVERFLOW_UNDEFINED:
      for (Variables_Set::const_iterator i = vars.begin(); i != vs_end; ++i) {
        ITV& x_seq_v = x.seq[*i];
        if (!rational_quadrant_itv.contains(x_seq_v)) {
          x_seq_v.assign(integer_quadrant_itv);
        }
      }
      break;
    case OVERFLOW_IMPOSSIBLE:
      for (Variables_Set::const_iterator i = vars.begin(); i != vs_end; ++i) {
        x.seq[*i].intersect_assign(integer_quadrant_itv);
      }
      reset_empty_up_to_date();
      break;
    }
    PPL_ASSERT(x.OK());
    return;
  }

  PPL_ASSERT(cs_p != 0);
  const Constraint_System& cs = *cs_p;
  // A map associating interval constraints to variable indexes.
  typedef std::map<dimension_type, std::vector<const Constraint*> > map_type;
  map_type var_cs_map;
  for (Constraint_System::const_iterator i = cs.begin(),
         i_end = cs.end(); i != i_end; ++i) {
    const Constraint& c = *i;
    dimension_type c_num_vars = 0;
    dimension_type c_only_var = 0;
    if (Box_Helpers::extract_interval_constraint(c, c_num_vars, c_only_var)) {
      if (c_num_vars == 1) {
        // An interval constraint on variable index `c_only_var'.
        PPL_ASSERT(c_only_var < space_dim);
        // We do care about c if c_only_var is going to be wrapped.
        if (vars.find(c_only_var) != vs_end) {
          var_cs_map[c_only_var].push_back(&c);
        }
      }
      else {
        PPL_ASSERT(c_num_vars == 0);
        // Note: tautologies have been filtered out by iterators.
        PPL_ASSERT(c.is_inconsistent());
        x.set_empty();
        return;
      }
    }
  }

  PPL_DIRTY_TEMP(ITV, refinement_itv);
  const map_type::const_iterator var_cs_map_end = var_cs_map.end();
  // Loop through the variable indexes in `vars'.
  for (Variables_Set::const_iterator i = vars.begin(); i != vs_end; ++i) {
    const dimension_type v = *i;
    refinement_itv = integer_quadrant_itv;
    // Look for the refinement constraints for space dimension index `v'.
    map_type::const_iterator var_cs_map_iter = var_cs_map.find(v);
    if (var_cs_map_iter != var_cs_map_end) {
      // Refine interval for variable `v'.
      const map_type::mapped_type& var_cs = var_cs_map_iter->second;
      for (dimension_type j = var_cs.size(); j-- > 0; ) {
        const Constraint& c = *var_cs[j];
        refine_interval_no_check(refinement_itv,
                                 c.type(),
                                 c.inhomogeneous_term(),
                                 c.coefficient(Variable(v)));
      }
    }
    // Wrap space dimension index `v'.
    ITV& x_seq_v = x.seq[v];
    switch (o) {
    case OVERFLOW_WRAPS:
      x_seq_v.wrap_assign(w, r, refinement_itv);
      break;
    case OVERFLOW_UNDEFINED:
      if (!rational_quadrant_itv.contains(x_seq_v)) {
        x_seq_v.assign(UNIVERSE);
      }
      break;
    case OVERFLOW_IMPOSSIBLE:
      x_seq_v.intersect_assign(refinement_itv);
      break;
    }
  }
  PPL_ASSERT(x.OK());
#endif
}

template <typename ITV>
void
Box<ITV>::drop_some_non_integer_points(Complexity_Class) {
  if (std::numeric_limits<typename ITV::boundary_type>::is_integer
      && !ITV::info_type::store_open) {
    return;
  }

  if (marked_empty()) {
    return;
  }

  for (dimension_type k = seq.size(); k-- > 0; ) {
    seq[k].drop_some_non_integer_points();
  }

  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>::drop_some_non_integer_points(const Variables_Set& vars,
                                       Complexity_Class) {
  // Dimension-compatibility check.
  const dimension_type min_space_dim = vars.space_dimension();
  if (space_dimension() < min_space_dim) {
    throw_dimension_incompatible("drop_some_non_integer_points(vs, cmpl)",
                                 min_space_dim);
  }
  if (std::numeric_limits<typename ITV::boundary_type>::is_integer
      && !ITV::info_type::store_open) {
    return;
  }

  if (marked_empty()) {
    return;
  }

  for (Variables_Set::const_iterator v_i = vars.begin(),
         v_end = vars.end(); v_i != v_end; ++v_i) {
    seq[*v_i].drop_some_non_integer_points();
  }

  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>::intersection_assign(const Box& y) {
  Box& x = *this;
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (space_dim != y.space_dimension()) {
    x.throw_dimension_incompatible("intersection_assign(y)", y);
  }

  // If one of the two boxes is empty, the intersection is empty.
  if (x.marked_empty()) {
    return;
  }
  if (y.marked_empty()) {
    x.set_empty();
    return;
  }

  // If both boxes are zero-dimensional, then at this point they are
  // necessarily non-empty, so that their intersection is non-empty too.
  if (space_dim == 0) {
    return;
  }

  // FIXME: here we may conditionally exploit a capability of the
  // underlying interval to eagerly detect empty results.
  reset_empty_up_to_date();

  for (dimension_type k = space_dim; k-- > 0; ) {
    x.seq[k].intersect_assign(y.seq[k]);
  }

  PPL_ASSERT(x.OK());
}

template <typename ITV>
void
Box<ITV>::upper_bound_assign(const Box& y) {
  Box& x = *this;

  // Dimension-compatibility check.
  if (x.space_dimension() != y.space_dimension()) {
    x.throw_dimension_incompatible("upper_bound_assign(y)", y);
  }

  // The lub of a box with an empty box is equal to the first box.
  if (y.is_empty()) {
    return;
  }
  if (x.is_empty()) {
    x = y;
    return;
  }

  for (dimension_type k = x.seq.size(); k-- > 0; ) {
    x.seq[k].join_assign(y.seq[k]);
  }

  PPL_ASSERT(x.OK());
}

template <typename ITV>
void
Box<ITV>::concatenate_assign(const Box& y) {
  Box& x = *this;
  const dimension_type x_space_dim = x.space_dimension();
  const dimension_type y_space_dim = y.space_dimension();

  // If `y' is marked empty, the result will be empty too.
  if (y.marked_empty()) {
    x.set_empty();
  }

  // If `y' is a 0-dim space box, there is nothing left to do.
  if (y_space_dim == 0) {
    return;
  }
  // The resulting space dimension must be at most the maximum.
  check_space_dimension_overflow(y.space_dimension(),
                                 max_space_dimension() - space_dimension(),
                                 "PPL::Box::",
                                 "concatenate_assign(y)",
                                 "concatenation exceeds the maximum "
                                 "allowed space dimension");
  // Here `y_space_dim > 0', so that a non-trivial concatenation will occur:
  // make sure that reallocation will occur once at most.
  x.seq.reserve(x_space_dim + y_space_dim);

  // If `x' is marked empty, then it is sufficient to adjust
  // the dimension of the vector space.
  if (x.marked_empty()) {
    x.seq.insert(x.seq.end(), y_space_dim, ITV(EMPTY));
    PPL_ASSERT(x.OK());
    return;
  }

  // Here neither `x' nor `y' are marked empty: concatenate them.
  std::copy(y.seq.begin(), y.seq.end(),
            std::back_insert_iterator<Sequence>(x.seq));
  // Update the `empty_up_to_date' flag.
  if (!y.status.test_empty_up_to_date()) {
    reset_empty_up_to_date();
  }

  PPL_ASSERT(x.OK());
}

template <typename ITV>
void
Box<ITV>::difference_assign(const Box& y) {
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("difference_assign(y)", y);
  }

  Box& x = *this;
  if (x.is_empty() || y.is_empty()) {
    return;
  }
  switch (space_dim) {
  case 0:
    // If `x' is zero-dimensional, then at this point both `x' and `y'
    // are the universe box, so that their difference is empty.
    x.set_empty();
    break;

  case 1:
    x.seq[0].difference_assign(y.seq[0]);
    if (x.seq[0].is_empty()) {
      x.set_empty();
    }
    break;

  default:
    {
      dimension_type index_non_contained = space_dim;
      dimension_type number_non_contained = 0;
      for (dimension_type i = space_dim; i-- > 0; ) {
        if (!y.seq[i].contains(x.seq[i])) {
          if (++number_non_contained == 1) {
            index_non_contained = i;
          }
          else {
            break;
          }
        }
      }

      switch (number_non_contained) {
      case 0:
        // `y' covers `x': the difference is empty.
        x.set_empty();
        break;
      case 1:
        x.seq[index_non_contained]
          .difference_assign(y.seq[index_non_contained]);
        if (x.seq[index_non_contained].is_empty()) {
          x.set_empty();
        }
        break;
      default:
        // Nothing to do: the difference is `x'.
        break;
      }
    }
    break;
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
bool
Box<ITV>::simplify_using_context_assign(const Box& y) {
  Box& x = *this;
  const dimension_type num_dims = x.space_dimension();
  // Dimension-compatibility check.
  if (num_dims != y.space_dimension()) {
    x.throw_dimension_incompatible("simplify_using_context_assign(y)", y);
  }

  // Filter away the zero-dimensional case.
  if (num_dims == 0) {
    if (y.marked_empty()) {
      x.set_nonempty();
      return false;
    }
    else {
      return !x.marked_empty();
    }
  }

  // Filter away the case when `y' is empty.
  if (y.is_empty()) {
    for (dimension_type i = num_dims; i-- > 0; ) {
      x.seq[i].assign(UNIVERSE);
    }
    x.set_nonempty();
    return false;
  }

  if (x.is_empty()) {
    // Find in `y' a non-universe interval, if any.
    for (dimension_type i = 0; i < num_dims; ++i) {
      if (y.seq[i].is_universe()) {
        x.seq[i].assign(UNIVERSE);
      }
      else {
        // Set x.seq[i] so as to contradict y.seq[i], if possible.
        ITV& seq_i = x.seq[i];
        seq_i.empty_intersection_assign(y.seq[i]);
        if (seq_i.is_empty()) {
          // We were not able to assign to `seq_i' a non-empty interval:
          // reset `seq_i' to the universe interval and keep searching.
          seq_i.assign(UNIVERSE);
          continue;
        }
        // We assigned to `seq_i' a non-empty interval:
        // set the other intervals to universe and return.
        for (++i; i < num_dims; ++i) {
          x.seq[i].assign(UNIVERSE);
        }
        x.set_nonempty();
        PPL_ASSERT(x.OK());
        return false;
      }
    }
    // All intervals in `y' are universe or could not be contradicted:
    // simplification can leave the empty box `x' as is.
    PPL_ASSERT(x.OK() && x.is_empty());
    return false;
  }

  // Loop index `i' is intentionally going upwards.
  for (dimension_type i = 0; i < num_dims; ++i) {
    if (!x.seq[i].simplify_using_context_assign(y.seq[i])) {
      PPL_ASSERT(!x.seq[i].is_empty());
      // The intersection of `x' and `y' is empty due to the i-th interval:
      // reset other intervals to UNIVERSE.
      for (dimension_type j = num_dims; j-- > i; ) {
        x.seq[j].assign(UNIVERSE);
      }
      for (dimension_type j = i; j-- > 0; ) {
        x.seq[j].assign(UNIVERSE);
      }
      PPL_ASSERT(x.OK());
      return false;
    }
  }
  PPL_ASSERT(x.OK());
  return true;
}

template <typename ITV>
void
Box<ITV>::time_elapse_assign(const Box& y) {
  Box& x = *this;
  const dimension_type x_space_dim = x.space_dimension();

  // Dimension-compatibility check.
  if (x_space_dim != y.space_dimension()) {
    x.throw_dimension_incompatible("time_elapse_assign(y)", y);
  }

  // Dealing with the zero-dimensional case.
  if (x_space_dim == 0) {
    if (y.marked_empty()) {
      x.set_empty();
    }
    return;
  }

  // If either one of `x' or `y' is empty, the result is empty too.
  // Note: if possible, avoid cost of checking for emptiness.
  if (x.marked_empty() || y.marked_empty()
      || x.is_empty() || y.is_empty()) {
    x.set_empty();
    return;
  }

  for (dimension_type i = x_space_dim; i-- > 0; ) {
    ITV& x_seq_i = x.seq[i];
    const ITV& y_seq_i = y.seq[i];
    if (!x_seq_i.lower_is_boundary_infinity()) {
      if (y_seq_i.lower_is_boundary_infinity() || y_seq_i.lower() < 0) {
        x_seq_i.lower_extend();
      }
    }
    if (!x_seq_i.upper_is_boundary_infinity()) {
      if (y_seq_i.upper_is_boundary_infinity() || y_seq_i.upper() > 0) {
        x_seq_i.upper_extend();
      }
    }
  }
  PPL_ASSERT(x.OK());
}

template <typename ITV>
inline void
Box<ITV>::remove_space_dimensions(const Variables_Set& vars) {
  // The removal of no dimensions from any box is a no-op.
  // Note that this case also captures the only legal removal of
  // space dimensions from a box in a zero-dimensional space.
  if (vars.empty()) {
    PPL_ASSERT(OK());
    return;
  }

  const dimension_type old_space_dim = space_dimension();

  // Dimension-compatibility check.
  const dimension_type vsi_space_dim = vars.space_dimension();
  if (old_space_dim < vsi_space_dim) {
    throw_dimension_incompatible("remove_space_dimensions(vs)",
                                 vsi_space_dim);
  }

  const dimension_type new_space_dim = old_space_dim - vars.size();

  // If the box is empty (this must be detected), then resizing is all
  // what is needed.  If it is not empty and we are removing _all_ the
  // dimensions then, again, resizing suffices.
  if (is_empty() || new_space_dim == 0) {
    seq.resize(new_space_dim);
    PPL_ASSERT(OK());
    return;
  }

  // For each variable to be removed, we fill the corresponding interval
  // by shifting left those intervals that will not be removed.
  Variables_Set::const_iterator vsi = vars.begin();
  Variables_Set::const_iterator vsi_end = vars.end();
  dimension_type dst = *vsi;
  dimension_type src = dst + 1;
  for (++vsi; vsi != vsi_end; ++vsi) {
    const dimension_type vsi_next = *vsi;
    // All intervals in between are moved to the left.
    while (src < vsi_next) {
      swap(seq[dst++], seq[src++]);
    }
    ++src;
  }

  // Moving the remaining intervals.
  while (src < old_space_dim) {
    swap(seq[dst++], seq[src++]);
  }

  PPL_ASSERT(dst == new_space_dim);
  seq.resize(new_space_dim);

  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>::remove_higher_space_dimensions(const dimension_type new_dimension) {
  // Dimension-compatibility check: the variable having
  // maximum index is the one occurring last in the set.
  const dimension_type space_dim = space_dimension();
  if (new_dimension > space_dim) {
    throw_dimension_incompatible("remove_higher_space_dimensions(nd)",
                                 new_dimension);
  }

  // The removal of no dimensions from any box is a no-op.
  // Note that this case also captures the only legal removal of
  // dimensions from a zero-dim space box.
  if (new_dimension == space_dim) {
    PPL_ASSERT(OK());
    return;
  }

  seq.resize(new_dimension);
  PPL_ASSERT(OK());
}

template <typename ITV>
template <typename Partial_Function>
void
Box<ITV>::map_space_dimensions(const Partial_Function& pfunc) {
  const dimension_type space_dim = space_dimension();
  if (space_dim == 0) {
    return;
  }

  if (pfunc.has_empty_codomain()) {
    // All dimensions vanish: the box becomes zero_dimensional.
    remove_higher_space_dimensions(0);
    return;
  }

  const dimension_type new_space_dim = pfunc.max_in_codomain() + 1;
  // If the box is empty, then simply adjust the space dimension.
  if (is_empty()) {
    remove_higher_space_dimensions(new_space_dim);
    return;
  }

  // We create a new Box with the new space dimension.
  Box<ITV> tmp(new_space_dim);
  // Map the intervals, exchanging the indexes.
  for (dimension_type i = 0; i < space_dim; ++i) {
    dimension_type new_i;
    if (pfunc.maps(i, new_i)) {
      swap(seq[i], tmp.seq[new_i]);
    }
  }
  m_swap(tmp);
  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>::fold_space_dimensions(const Variables_Set& vars,
                                const Variable dest) {
  const dimension_type space_dim = space_dimension();
  // `dest' should be one of the dimensions of the box.
  if (dest.space_dimension() > space_dim) {
    throw_dimension_incompatible("fold_space_dimensions(vs, v)", "v", dest);
  }

  // The folding of no dimensions is a no-op.
  if (vars.empty()) {
    return;
  }

  // All variables in `vars' should be dimensions of the box.
  if (vars.space_dimension() > space_dim) {
    throw_dimension_incompatible("fold_space_dimensions(vs, v)",
                                 vars.space_dimension());
  }
  // Moreover, `dest.id()' should not occur in `vars'.
  if (vars.find(dest.id()) != vars.end()) {
    throw_invalid_argument("fold_space_dimensions(vs, v)",
                           "v should not occur in vs");
  }

  // Note: the check for emptiness is needed for correctness.
  if (!is_empty()) {
    // Join the interval corresponding to variable `dest' with the intervals
    // corresponding to the variables in `vars'.
    ITV& seq_v = seq[dest.id()];
    for (Variables_Set::const_iterator i = vars.begin(),
           vs_end = vars.end(); i != vs_end; ++i) {
      seq_v.join_assign(seq[*i]);
    }
  }
  remove_space_dimensions(vars);
}

template <typename ITV>
void
Box<ITV>::add_constraint_no_check(const Constraint& c) {
  PPL_ASSERT(c.space_dimension() <= space_dimension());

  dimension_type c_num_vars = 0;
  dimension_type c_only_var = 0;
  // Throw an exception if c is not an interval constraints.
  if (!Box_Helpers::extract_interval_constraint(c, c_num_vars, c_only_var)) {
    throw_invalid_argument("add_constraint(c)",
                           "c is not an interval constraint");
  }

  // Throw an exception if c is a nontrivial strict constraint
  // and ITV does not support open boundaries.
  if (c.is_strict_inequality() && c_num_vars != 0
      && ITV::is_always_topologically_closed()) {
    throw_invalid_argument("add_constraint(c)",
                           "c is a nontrivial strict constraint");
  }

  // Avoid doing useless work if the box is known to be empty.
  if (marked_empty()) {
    return;
  }

  const Coefficient& n = c.inhomogeneous_term();
  if (c_num_vars == 0) {
    // Dealing with a trivial constraint.
    if (n < 0
        || (c.is_equality() && n != 0)
        || (c.is_strict_inequality() && n == 0)) {
      set_empty();
    }
    return;
  }

  PPL_ASSERT(c_num_vars == 1);
  const Coefficient& d = c.coefficient(Variable(c_only_var));
  add_interval_constraint_no_check(c_only_var, c.type(), n, d);
}

template <typename ITV>
void
Box<ITV>::add_constraints_no_check(const Constraint_System& cs) {
  PPL_ASSERT(cs.space_dimension() <= space_dimension());
  // Note: even when the box is known to be empty, we need to go
  // through all the constraints to fulfill the method's contract
  // for what concerns exception throwing.
  for (Constraint_System::const_iterator i = cs.begin(),
         cs_end = cs.end(); i != cs_end; ++i) {
    add_constraint_no_check(*i);
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>::add_congruence_no_check(const Congruence& cg) {
  PPL_ASSERT(cg.space_dimension() <= space_dimension());

  // Set aside the case of proper congruences.
  if (cg.is_proper_congruence()) {
    if (cg.is_inconsistent()) {
      set_empty();
      return;
    }
    else if (cg.is_tautological()) {
      return;
    }
    else {
      throw_invalid_argument("add_congruence(cg)",
                             "cg is a nontrivial proper congruence");
    }
  }

  PPL_ASSERT(cg.is_equality());
  dimension_type cg_num_vars = 0;
  dimension_type cg_only_var = 0;
  // Throw an exception if c is not an interval congruence.
  if (!Box_Helpers::extract_interval_congruence(cg, cg_num_vars, cg_only_var)) {
    throw_invalid_argument("add_congruence(cg)",
                           "cg is not an interval congruence");
  }

  // Avoid doing useless work if the box is known to be empty.
  if (marked_empty()) {
    return;
  }

  const Coefficient& n = cg.inhomogeneous_term();
  if (cg_num_vars == 0) {
    // Dealing with a trivial equality congruence.
    if (n != 0) {
      set_empty();
    }
    return;
  }

  PPL_ASSERT(cg_num_vars == 1);
  const Coefficient& d = cg.coefficient(Variable(cg_only_var));
  add_interval_constraint_no_check(cg_only_var, Constraint::EQUALITY, n, d);
}

template <typename ITV>
void
Box<ITV>::add_congruences_no_check(const Congruence_System& cgs) {
  PPL_ASSERT(cgs.space_dimension() <= space_dimension());
  // Note: even when the box is known to be empty, we need to go
  // through all the congruences to fulfill the method's contract
  // for what concerns exception throwing.
  for (Congruence_System::const_iterator i = cgs.begin(),
         cgs_end = cgs.end(); i != cgs_end; ++i) {
    add_congruence_no_check(*i);
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>::refine_no_check(const Constraint& c) {
  PPL_ASSERT(c.space_dimension() <= space_dimension());
  PPL_ASSERT(!marked_empty());

  dimension_type c_num_vars = 0;
  dimension_type c_only_var = 0;
  // Non-interval constraints are approximated.
  if (!Box_Helpers::extract_interval_constraint(c, c_num_vars, c_only_var)) {
    propagate_constraint_no_check(c);
    return;
  }

  const Coefficient& n = c.inhomogeneous_term();
  if (c_num_vars == 0) {
    // Dealing with a trivial constraint.
    if (n < 0
        || (c.is_equality() && n != 0)
        || (c.is_strict_inequality() && n == 0)) {
      set_empty();
    }
    return;
  }

  PPL_ASSERT(c_num_vars == 1);
  const Coefficient& d = c.coefficient(Variable(c_only_var));
  add_interval_constraint_no_check(c_only_var, c.type(), n, d);
}

template <typename ITV>
void
Box<ITV>::refine_no_check(const Constraint_System& cs) {
  PPL_ASSERT(cs.space_dimension() <= space_dimension());
  for (Constraint_System::const_iterator i = cs.begin(),
         cs_end = cs.end(); !marked_empty() && i != cs_end; ++i) {
    refine_no_check(*i);
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>::refine_no_check(const Congruence& cg) {
  PPL_ASSERT(!marked_empty());

  PPL_ASSERT(cg.space_dimension() <= space_dimension());

  if (cg.is_proper_congruence()) {
    // A proper congruences is also an interval constraint
    // if and only if it is trivial.
    if (cg.is_inconsistent()) {
      set_empty();
    }
    return;
  }

  PPL_ASSERT(cg.is_equality());
  Constraint c(cg);
  refine_no_check(c);
}

template <typename ITV>
void
Box<ITV>::refine_no_check(const Congruence_System& cgs) {
  PPL_ASSERT(cgs.space_dimension() <= space_dimension());
  for (Congruence_System::const_iterator i = cgs.begin(),
         cgs_end = cgs.end(); !marked_empty() && i != cgs_end; ++i) {
    refine_no_check(*i);
  }
  PPL_ASSERT(OK());
}

#if 1 // Alternative implementations for propagate_constraint_no_check.
namespace Implementation {

namespace Boxes {

inline bool
propagate_constraint_check_result(Result r, Ternary& open) {
  r = result_relation_class(r);
  switch (r) {
  case V_GT_MINUS_INFINITY:
  case V_LT_PLUS_INFINITY:
    return true;
  case V_LT:
  case V_GT:
    open = T_YES;
    return false;
  case V_LE:
  case V_GE:
    if (open == T_NO) {
      open = T_MAYBE;
    }
    return false;
  case V_EQ:
    return false;
  default:
    PPL_UNREACHABLE;
    return true;
  }
}

} // namespace Boxes

} // namespace Implementation


template <typename ITV>
void
Box<ITV>::propagate_constraint_no_check(const Constraint& c) {
  using namespace Implementation::Boxes;

  PPL_ASSERT(c.space_dimension() <= space_dimension());

  typedef
    typename Select_Temp_Boundary_Type<typename ITV::boundary_type>::type
    Temp_Boundary_Type;

  const dimension_type c_space_dim = c.space_dimension();
  const Constraint::Type c_type = c.type();
  const Coefficient& c_inhomogeneous_term = c.inhomogeneous_term();

  // Find a space dimension having a non-zero coefficient (if any).
  const dimension_type last_k
    = c.expression().last_nonzero(1, c_space_dim + 1);
  if (last_k == c_space_dim + 1) {
    // Constraint c is trivial: check if it is inconsistent.
    if (c_inhomogeneous_term < 0
        || (c_inhomogeneous_term == 0
            && c_type != Constraint::NONSTRICT_INEQUALITY)) {
      set_empty();
    }
    return;
  }

  // Here constraint c is non-trivial.
  PPL_ASSERT(last_k <= c_space_dim);
  Temp_Boundary_Type t_bound;
  Temp_Boundary_Type t_a;
  Temp_Boundary_Type t_x;
  Ternary open;
  const Constraint::expr_type c_e = c.expression();
  for (Constraint::expr_type::const_iterator k = c_e.begin(),
         k_end = c_e.lower_bound(Variable(last_k)); k != k_end; ++k) {
    const Coefficient& a_k = *k;
    const Variable k_var = k.variable();
    const int sgn_a_k = sgn(a_k);
    if (sgn_a_k == 0) {
      continue;
    }
    Result r;
    if (sgn_a_k > 0) {
      open = (c_type == Constraint::STRICT_INEQUALITY) ? T_YES : T_NO;
      if (open == T_NO) {
        maybe_reset_fpu_inexact<Temp_Boundary_Type>();
      }
      r = assign_r(t_bound, c_inhomogeneous_term, ROUND_UP);
      if (propagate_constraint_check_result(r, open)) {
        goto maybe_refine_upper_1;
      }
      r = neg_assign_r(t_bound, t_bound, ROUND_DOWN);
      if (propagate_constraint_check_result(r, open)) {
        goto maybe_refine_upper_1;
      }
      for (Constraint::expr_type::const_iterator i = c_e.begin(),
            i_end = c_e.lower_bound(Variable(last_k)); i != i_end; ++i) {
        const Variable i_var = i.variable();
        if (i_var.id() == k_var.id()) {
          continue;
        }
        const Coefficient& a_i = *i;
        const int sgn_a_i = sgn(a_i);
        ITV& x_i = seq[i_var.id()];
        if (sgn_a_i < 0) {
          if (x_i.lower_is_boundary_infinity()) {
            goto maybe_refine_upper_1;
          }
          r = assign_r(t_a, a_i, ROUND_DOWN);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_1;
          }
          r = assign_r(t_x, x_i.lower(), ROUND_DOWN);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_1;
          }
          if (x_i.lower_is_open()) {
            open = T_YES;
          }
          r = sub_mul_assign_r(t_bound, t_a, t_x, ROUND_DOWN);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_1;
          }
        }
        else {
          PPL_ASSERT(sgn_a_i > 0);
          if (x_i.upper_is_boundary_infinity()) {
            goto maybe_refine_upper_1;
          }
          r = assign_r(t_a, a_i, ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_1;
          }
          r = assign_r(t_x, x_i.upper(), ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_1;
          }
          if (x_i.upper_is_open()) {
            open = T_YES;
          }
          r = sub_mul_assign_r(t_bound, t_a, t_x, ROUND_DOWN);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_1;
          }
        }
      }
      r = assign_r(t_a, a_k, ROUND_UP);
      if (propagate_constraint_check_result(r, open)) {
        goto maybe_refine_upper_1;
      }
      r = div_assign_r(t_bound, t_bound, t_a, ROUND_DOWN);
      if (propagate_constraint_check_result(r, open)) {
        goto maybe_refine_upper_1;
      }

      // Refine the lower bound of `seq[k]' with `t_bound'.
      if (open == T_MAYBE
          && maybe_check_fpu_inexact<Temp_Boundary_Type>() == 1) {
        open = T_YES;
      }
      {
        const Relation_Symbol rel
          = (open == T_YES) ? GREATER_THAN : GREATER_OR_EQUAL;
        seq[k_var.id()].add_constraint(i_constraint(rel, t_bound));
      }
      reset_empty_up_to_date();
    maybe_refine_upper_1:
      if (c_type != Constraint::EQUALITY) {
        continue;
      }
      open = T_NO;
      maybe_reset_fpu_inexact<Temp_Boundary_Type>();
      r = assign_r(t_bound, c_inhomogeneous_term, ROUND_DOWN);
      if (propagate_constraint_check_result(r, open)) {
        goto next_k;
      }
      r = neg_assign_r(t_bound, t_bound, ROUND_UP);
      if (propagate_constraint_check_result(r, open)) {
        goto next_k;
      }
      for (Constraint::expr_type::const_iterator i = c_e.begin(),
            i_end = c_e.lower_bound(Variable(c_space_dim)); i != i_end; ++i) {
        const Variable i_var = i.variable();
        if (i_var.id() == k_var.id()) {
          continue;
        }
        const Coefficient& a_i = *i;
        const int sgn_a_i = sgn(a_i);
        ITV& x_i = seq[i_var.id()];
        if (sgn_a_i < 0) {
          if (x_i.upper_is_boundary_infinity()) {
            goto next_k;
          }
          r = assign_r(t_a, a_i, ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
          r = assign_r(t_x, x_i.upper(), ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
          if (x_i.upper_is_open()) {
            open = T_YES;
          }
          r = sub_mul_assign_r(t_bound, t_a, t_x, ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
        }
        else {
          PPL_ASSERT(sgn_a_i > 0);
          if (x_i.lower_is_boundary_infinity()) {
            goto next_k;
          }
          r = assign_r(t_a, a_i, ROUND_DOWN);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
          r = assign_r(t_x, x_i.lower(), ROUND_DOWN);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
          if (x_i.lower_is_open()) {
            open = T_YES;
          }
          r = sub_mul_assign_r(t_bound, t_a, t_x, ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
        }
      }
      r = assign_r(t_a, a_k, ROUND_DOWN);
      if (propagate_constraint_check_result(r, open)) {
        goto next_k;
      }
      r = div_assign_r(t_bound, t_bound, t_a, ROUND_UP);
      if (propagate_constraint_check_result(r, open)) {
        goto next_k;
      }

      // Refine the upper bound of seq[k] with t_bound.
      if (open == T_MAYBE
          && maybe_check_fpu_inexact<Temp_Boundary_Type>() == 1) {
        open = T_YES;
      }
      const Relation_Symbol rel
        = (open == T_YES) ? LESS_THAN : LESS_OR_EQUAL;
      seq[k_var.id()].add_constraint(i_constraint(rel, t_bound));
      reset_empty_up_to_date();
    }
    else {
      PPL_ASSERT(sgn_a_k < 0);
      open = (c_type == Constraint::STRICT_INEQUALITY) ? T_YES : T_NO;
      if (open == T_NO) {
        maybe_reset_fpu_inexact<Temp_Boundary_Type>();
      }
      r = assign_r(t_bound, c_inhomogeneous_term, ROUND_UP);
      if (propagate_constraint_check_result(r, open)) {
        goto maybe_refine_upper_2;
      }
      r = neg_assign_r(t_bound, t_bound, ROUND_DOWN);
      if (propagate_constraint_check_result(r, open)) {
        goto maybe_refine_upper_2;
      }
      for (Constraint::expr_type::const_iterator i = c_e.begin(),
            i_end = c_e.lower_bound(Variable(c_space_dim)); i != i_end; ++i) {
        const Variable i_var = i.variable();
        if (i_var.id() == k_var.id()) {
          continue;
        }
        const Coefficient& a_i = *i;
        const int sgn_a_i = sgn(a_i);
        ITV& x_i = seq[i_var.id()];
        if (sgn_a_i < 0) {
          if (x_i.lower_is_boundary_infinity()) {
            goto maybe_refine_upper_2;
          }
          r = assign_r(t_a, a_i, ROUND_DOWN);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_2;
          }
          r = assign_r(t_x, x_i.lower(), ROUND_DOWN);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_2;
          }
          if (x_i.lower_is_open()) {
            open = T_YES;
          }
          r = sub_mul_assign_r(t_bound, t_a, t_x, ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_2;
          }
        }
        else {
          PPL_ASSERT(sgn_a_i > 0);
          if (x_i.upper_is_boundary_infinity()) {
            goto maybe_refine_upper_2;
          }
          r = assign_r(t_a, a_i, ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_2;
          }
          r = assign_r(t_x, x_i.upper(), ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_2;
          }
          if (x_i.upper_is_open()) {
            open = T_YES;
          }
          r = sub_mul_assign_r(t_bound, t_a, t_x, ROUND_DOWN);
          if (propagate_constraint_check_result(r, open)) {
            goto maybe_refine_upper_2;
          }
        }
      }
      r = assign_r(t_a, a_k, ROUND_UP);
      if (propagate_constraint_check_result(r, open)) {
        goto maybe_refine_upper_2;
      }
      r = div_assign_r(t_bound, t_bound, t_a, ROUND_UP);
      if (propagate_constraint_check_result(r, open)) {
        goto maybe_refine_upper_2;
      }
      // Refine the upper bound of seq[k] with t_bound.
      if (open == T_MAYBE
          && maybe_check_fpu_inexact<Temp_Boundary_Type>() == 1) {
        open = T_YES;
      }
      {
        const Relation_Symbol rel
          = (open == T_YES) ? LESS_THAN : LESS_OR_EQUAL;
        seq[k_var.id()].add_constraint(i_constraint(rel, t_bound));
      }
      reset_empty_up_to_date();
    maybe_refine_upper_2:
      if (c_type != Constraint::EQUALITY) {
        continue;
      }
      open = T_NO;
      maybe_reset_fpu_inexact<Temp_Boundary_Type>();
      r = assign_r(t_bound, c_inhomogeneous_term, ROUND_DOWN);
      if (propagate_constraint_check_result(r, open)) {
        goto next_k;
      }
      r = neg_assign_r(t_bound, t_bound, ROUND_UP);
      if (propagate_constraint_check_result(r, open)) {
        goto next_k;
      }
      for (Constraint::expr_type::const_iterator i = c_e.begin(),
            i_end = c_e.lower_bound(Variable(c_space_dim)); i != i_end; ++i) {
        const Variable i_var = i.variable();
        if (i_var.id() == k_var.id()) {
          continue;
        }
        const Coefficient& a_i = *i;
        const int sgn_a_i = sgn(a_i);
        ITV& x_i = seq[i_var.id()];
        if (sgn_a_i < 0) {
          if (x_i.upper_is_boundary_infinity()) {
            goto next_k;
          }
          r = assign_r(t_a, a_i, ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
          r = assign_r(t_x, x_i.upper(), ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
          if (x_i.upper_is_open()) {
            open = T_YES;
          }
          r = sub_mul_assign_r(t_bound, t_a, t_x, ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
        }
        else {
          PPL_ASSERT(sgn_a_i > 0);
          if (x_i.lower_is_boundary_infinity()) {
            goto next_k;
          }
          r = assign_r(t_a, a_i, ROUND_DOWN);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
          r = assign_r(t_x, x_i.lower(), ROUND_DOWN);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
          if (x_i.lower_is_open()) {
            open = T_YES;
          }
          r = sub_mul_assign_r(t_bound, t_a, t_x, ROUND_UP);
          if (propagate_constraint_check_result(r, open)) {
            goto next_k;
          }
        }
      }
      r = assign_r(t_a, a_k, ROUND_DOWN);
      if (propagate_constraint_check_result(r, open)) {
        goto next_k;
      }
      r = div_assign_r(t_bound, t_bound, t_a, ROUND_DOWN);
      if (propagate_constraint_check_result(r, open)) {
        goto next_k;
      }

      // Refine the lower bound of seq[k] with t_bound.
      if (open == T_MAYBE
          && maybe_check_fpu_inexact<Temp_Boundary_Type>() == 1) {
        open = T_YES;
      }
      const Relation_Symbol rel
        = (open == T_YES) ? GREATER_THAN : GREATER_OR_EQUAL;
      seq[k_var.id()].add_constraint(i_constraint(rel, t_bound));
      reset_empty_up_to_date();
    }
  next_k:
    ;
  }
}

#else // Alternative implementations for propagate_constraint_no_check.

template <typename ITV>
void
Box<ITV>::propagate_constraint_no_check(const Constraint& c) {
  PPL_ASSERT(c.space_dimension() <= space_dimension());

  dimension_type c_space_dim = c.space_dimension();
  ITV k[c_space_dim];
  ITV p[c_space_dim];
  for (Constraint::expr_type::const_iterator i = c_e.begin(),
        i_end = c_e.lower_bound(Variable(c_space_dim)); i != i_end; ++i) {
    const Variable i_var = i.variable();
    k[i_var.id()] = *i;
    ITV& p_i = p[i_var.id()];
    p_i = seq[i_var.id()];
    p_i.mul_assign(p_i, k[i_var.id()]);
  }
  const Coefficient& inhomogeneous_term = c.inhomogeneous_term();
  for (Constraint::expr_type::const_iterator i = c_e.begin(),
        i_end = c_e.lower_bound(Variable(c_space_dim)); i != i_end; ++i) {
    const Variable i_var = i.variable();
    int sgn_coefficient_i = sgn(*i);
    ITV q(inhomogeneous_term);
    for (Constraint::expr_type::const_iterator j = c_e.begin(),
          j_end = c_e.lower_bound(Variable(c_space_dim)); j != j_end; ++j) {
      const Variable j_var = j.variable();
      if (i_var == j_var) {
        continue;
      }
      q.add_assign(q, p[j_var.id()]);
    }
    q.div_assign(q, k[i_var.id()]);
    q.neg_assign(q);
    Relation_Symbol rel;
    switch (c.type()) {
    case Constraint::EQUALITY:
      rel = EQUAL;
      break;
    case Constraint::NONSTRICT_INEQUALITY:
      rel = (sgn_coefficient_i > 0) ? GREATER_OR_EQUAL : LESS_OR_EQUAL;
      break;
    case Constraint::STRICT_INEQUALITY:
      rel = (sgn_coefficient_i > 0) ? GREATER_THAN : LESS_THAN;
      break;
    }
    seq[i_var.id()].add_constraint(i_constraint(rel, q));
    // FIXME: could/should we exploit the return value of add_constraint
    //        in case it is available?
    // FIXME: should we instead be lazy and do not even bother about
    //        the possibility the interval becomes empty apart from setting
    //        empty_up_to_date = false?
    if (seq[i_var.id()].is_empty()) {
      set_empty();
      break;
    }
  }

  PPL_ASSERT(OK());
}

#endif // Alternative implementations for propagate_constraint_no_check.

template <typename ITV>
void
Box<ITV>
::propagate_constraints_no_check(const Constraint_System& cs,
                                 const dimension_type max_iterations) {
  const dimension_type space_dim = space_dimension();
  PPL_ASSERT(cs.space_dimension() <= space_dim);

  const Constraint_System::const_iterator cs_begin = cs.begin();
  const Constraint_System::const_iterator cs_end = cs.end();
  const dimension_type propagation_weight
    = Implementation::num_constraints(cs) * space_dim;

  Sequence copy;
  bool changed;
  dimension_type num_iterations = 0;
  do {
    WEIGHT_BEGIN();
    ++num_iterations;
    copy = seq;
    for (Constraint_System::const_iterator i = cs_begin; i != cs_end; ++i) {
      propagate_constraint_no_check(*i);
    }

    WEIGHT_ADD_MUL(40, propagation_weight);
    // Check if the client has requested abandoning all expensive
    // computations.  If so, the exception specified by the client
    // is thrown now.
    maybe_abandon();

    // NOTE: if max_iterations == 0 (i.e., no iteration limit is set)
    // the following test will anyway trigger on wrap around.
    if (num_iterations == max_iterations) {
      break;
    }

    changed = (copy != seq);
  } while (changed);
}

template <typename ITV>
void
Box<ITV>::affine_image(const Variable var,
                       const Linear_Expression& expr,
                       Coefficient_traits::const_reference denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("affine_image(v, e, d)", "d == 0");
  }

  // Dimension-compatibility checks.
  const dimension_type space_dim = space_dimension();
  const dimension_type expr_space_dim = expr.space_dimension();
  if (space_dim < expr_space_dim) {
    throw_dimension_incompatible("affine_image(v, e, d)", "e", expr);
  }
  // `var' should be one of the dimensions of the polyhedron.
  const dimension_type var_space_dim = var.space_dimension();
  if (space_dim < var_space_dim) {
    throw_dimension_incompatible("affine_image(v, e, d)", "v", var);
  }

  if (is_empty()) {
    return;
  }

  Tmp_Interval_Type expr_value;
  Tmp_Interval_Type temp0;
  Tmp_Interval_Type temp1;
  expr_value.assign(expr.inhomogeneous_term());
  for (Linear_Expression::const_iterator i = expr.begin(),
          i_end = expr.end(); i != i_end; ++i) {
    temp0.assign(*i);
    temp1.assign(seq[i.variable().id()]);
    temp0.mul_assign(temp0, temp1);
    expr_value.add_assign(expr_value, temp0);
  }
  if (denominator != 1) {
    temp0.assign(denominator);
    expr_value.div_assign(expr_value, temp0);
  }
  seq[var.id()].assign(expr_value);

  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>::affine_form_image(const Variable var,
                            const Linear_Form<ITV>& lf) {

  // Check that ITV has a floating point boundary type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<typename ITV::boundary_type>
            ::is_exact, "Box<ITV>::affine_form_image(Variable, Linear_Form):"
                        "ITV has not a floating point boundary type.");

  // Dimension-compatibility checks.
  const dimension_type space_dim = space_dimension();
  const dimension_type lf_space_dim = lf.space_dimension();
  if (space_dim < lf_space_dim) {
    throw_dimension_incompatible("affine_form_image(var, lf)", "lf", lf);
  }

  // `var' should be one of the dimensions of the polyhedron.
  const dimension_type var_space_dim = var.space_dimension();
  if (space_dim < var_space_dim) {
    throw_dimension_incompatible("affine_form_image(var, lf)", "var", var);
  }

  if (is_empty()) {
    return;
  }

  // Intervalization of 'lf'.
  ITV result = lf.inhomogeneous_term();
  for (dimension_type i = 0; i < lf_space_dim; ++i) {
    ITV current_addend = lf.coefficient(Variable(i));
    const ITV& curr_int = seq[i];
    current_addend *= curr_int;
    result += current_addend;
  }

  seq[var.id()].assign(result);
  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>::affine_preimage(const Variable var,
                          const Linear_Expression& expr,
                          Coefficient_traits::const_reference
                          denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("affine_preimage(v, e, d)", "d == 0");
  }

  // Dimension-compatibility checks.
  const dimension_type x_space_dim = space_dimension();
  const dimension_type expr_space_dim = expr.space_dimension();
  if (x_space_dim < expr_space_dim) {
    throw_dimension_incompatible("affine_preimage(v, e, d)", "e", expr);
  }
  // `var' should be one of the dimensions of the polyhedron.
  const dimension_type var_space_dim = var.space_dimension();
  if (x_space_dim < var_space_dim) {
    throw_dimension_incompatible("affine_preimage(v, e, d)", "v", var);
  }

  if (is_empty()) {
    return;
  }

  const Coefficient& expr_v = expr.coefficient(var);
  const bool invertible = (expr_v != 0);
  if (!invertible) {
    Tmp_Interval_Type expr_value;
    Tmp_Interval_Type temp0;
    Tmp_Interval_Type temp1;
    expr_value.assign(expr.inhomogeneous_term());
    for (Linear_Expression::const_iterator i = expr.begin(),
            i_end = expr.end(); i != i_end; ++i) {
      temp0.assign(*i);
      temp1.assign(seq[i.variable().id()]);
      temp0.mul_assign(temp0, temp1);
      expr_value.add_assign(expr_value, temp0);
    }
    if (denominator != 1) {
      temp0.assign(denominator);
      expr_value.div_assign(expr_value, temp0);
    }
    ITV& x_seq_v = seq[var.id()];
    expr_value.intersect_assign(x_seq_v);
    if (expr_value.is_empty()) {
      set_empty();
    }
    else {
      x_seq_v.assign(UNIVERSE);
    }
  }
  else {
    // The affine transformation is invertible.
    // CHECKME: for efficiency, would it be meaningful to avoid
    // the computation of inverse by partially evaluating the call
    // to affine_image?
    Linear_Expression inverse;
    inverse -= expr;
    inverse += (expr_v + denominator) * var;
    affine_image(var, inverse, expr_v);
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>
::bounded_affine_image(const Variable var,
                       const Linear_Expression& lb_expr,
                       const Linear_Expression& ub_expr,
                       Coefficient_traits::const_reference denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("bounded_affine_image(v, lb, ub, d)", "d == 0");
  }

  // Dimension-compatibility checks.
  const dimension_type space_dim = space_dimension();
  // The dimension of `lb_expr' and `ub_expr' should not be
  // greater than the dimension of `*this'.
  const dimension_type lb_space_dim = lb_expr.space_dimension();
  if (space_dim < lb_space_dim) {
    throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)",
                                 "lb", lb_expr);
  }
  const dimension_type ub_space_dim = ub_expr.space_dimension();
  if (space_dim < ub_space_dim) {
    throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)",
                                 "ub", ub_expr);
  }
    // `var' should be one of the dimensions of the box.
  const dimension_type var_space_dim = var.space_dimension();
  if (space_dim < var_space_dim) {
    throw_dimension_incompatible("affine_image(v, e, d)", "v", var);
  }
  // Any image of an empty box is empty.
  if (is_empty()) {
    return;
  }
  // Add the constraint implied by the `lb_expr' and `ub_expr'.
  if (denominator > 0) {
    refine_with_constraint(lb_expr <= ub_expr);
  }
  else {
    refine_with_constraint(lb_expr >= ub_expr);
  }

  // Check whether `var' occurs in `lb_expr' and/or `ub_expr'.
  if (lb_expr.coefficient(var) == 0) {
    // Here `var' can only occur in `ub_expr'.
    generalized_affine_image(var,
                             LESS_OR_EQUAL,
                             ub_expr,
                             denominator);
    if (denominator > 0) {
      refine_with_constraint(lb_expr <= denominator*var);
    }
    else {
      refine_with_constraint(denominator*var <= lb_expr);
    }
  }
  else if (ub_expr.coefficient(var) == 0) {
    // Here `var' can only occur in `lb_expr'.
    generalized_affine_image(var,
                             GREATER_OR_EQUAL,
                             lb_expr,
                             denominator);
    if (denominator > 0) {
      refine_with_constraint(denominator*var <= ub_expr);
    }
    else {
      refine_with_constraint(ub_expr <= denominator*var);
    }
  }
  else {
    // Here `var' occurs in both `lb_expr' and `ub_expr'.  As boxes
    // can only use the non-relational constraints, we find the
    // maximum/minimum values `ub_expr' and `lb_expr' obtain with the
    // box and use these instead of the `ub-expr' and `lb-expr'.
    PPL_DIRTY_TEMP_COEFFICIENT(max_numer);
    PPL_DIRTY_TEMP_COEFFICIENT(max_denom);
    bool max_included;
    PPL_DIRTY_TEMP_COEFFICIENT(min_numer);
    PPL_DIRTY_TEMP_COEFFICIENT(min_denom);
    bool min_included;
    ITV& seq_v = seq[var.id()];
    if (maximize(ub_expr, max_numer, max_denom, max_included)) {
      if (minimize(lb_expr, min_numer, min_denom, min_included)) {
        // The `ub_expr' has a maximum value and the `lb_expr'
        // has a minimum value for the box.
        // Set the bounds for `var' using the minimum for `lb_expr'.
        min_denom *= denominator;
        PPL_DIRTY_TEMP(mpq_class, q1);
        PPL_DIRTY_TEMP(mpq_class, q2);
        assign_r(q1.get_num(), min_numer, ROUND_NOT_NEEDED);
        assign_r(q1.get_den(), min_denom, ROUND_NOT_NEEDED);
        q1.canonicalize();
        // Now make the maximum of lb_expr the upper bound.  If the
        // maximum is not at a box point, then inequality is strict.
        max_denom *= denominator;
        assign_r(q2.get_num(), max_numer, ROUND_NOT_NEEDED);
        assign_r(q2.get_den(), max_denom, ROUND_NOT_NEEDED);
        q2.canonicalize();

        if (denominator > 0) {
          Relation_Symbol gr = min_included ? GREATER_OR_EQUAL : GREATER_THAN;
          Relation_Symbol lr = max_included ? LESS_OR_EQUAL : LESS_THAN;
          seq_v.build(i_constraint(gr, q1), i_constraint(lr, q2));
        }
        else {
          Relation_Symbol gr = max_included ? GREATER_OR_EQUAL : GREATER_THAN;
          Relation_Symbol lr = min_included ? LESS_OR_EQUAL : LESS_THAN;
          seq_v.build(i_constraint(gr, q2), i_constraint(lr, q1));
        }
      }
      else {
        // The `ub_expr' has a maximum value but the `lb_expr'
        // has no minimum value for the box.
        // Set the bounds for `var' using the maximum for `lb_expr'.
        PPL_DIRTY_TEMP(mpq_class, q);
        max_denom *= denominator;
        assign_r(q.get_num(), max_numer, ROUND_NOT_NEEDED);
        assign_r(q.get_den(), max_denom, ROUND_NOT_NEEDED);
        q.canonicalize();
        Relation_Symbol rel = (denominator > 0)
          ? (max_included ? LESS_OR_EQUAL : LESS_THAN)
          : (max_included ? GREATER_OR_EQUAL : GREATER_THAN);
        seq_v.build(i_constraint(rel, q));
      }
    }
    else if (minimize(lb_expr, min_numer, min_denom, min_included)) {
        // The `ub_expr' has no maximum value but the `lb_expr'
        // has a minimum value for the box.
        // Set the bounds for `var' using the minimum for `lb_expr'.
        min_denom *= denominator;
        PPL_DIRTY_TEMP(mpq_class, q);
        assign_r(q.get_num(), min_numer, ROUND_NOT_NEEDED);
        assign_r(q.get_den(), min_denom, ROUND_NOT_NEEDED);
        q.canonicalize();

        Relation_Symbol rel = (denominator > 0)
          ? (min_included ? GREATER_OR_EQUAL : GREATER_THAN)
          : (min_included ? LESS_OR_EQUAL : LESS_THAN);
        seq_v.build(i_constraint(rel, q));
    }
    else {
      // The `ub_expr' has no maximum value and the `lb_expr'
      // has no minimum value for the box.
      // So we set the bounds to be unbounded.
      seq_v.assign(UNIVERSE);
    }
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>
::bounded_affine_preimage(const Variable var,
                          const Linear_Expression& lb_expr,
                          const Linear_Expression& ub_expr,
                          Coefficient_traits::const_reference denominator) {
  // The denominator cannot be zero.
  const dimension_type space_dim = space_dimension();
  if (denominator == 0) {
    throw_invalid_argument("bounded_affine_preimage(v, lb, ub, d)", "d == 0");
  }

  // Dimension-compatibility checks.
  // `var' should be one of the dimensions of the polyhedron.
  const dimension_type var_space_dim = var.space_dimension();
  if (space_dim < var_space_dim) {
    throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)",
                                 "v", var);
  }
  // The dimension of `lb_expr' and `ub_expr' should not be
  // greater than the dimension of `*this'.
  const dimension_type lb_space_dim = lb_expr.space_dimension();
  if (space_dim < lb_space_dim) {
    throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)",
                                 "lb", lb_expr);
  }
  const dimension_type ub_space_dim = ub_expr.space_dimension();
  if (space_dim < ub_space_dim) {
    throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)",
                                 "ub", ub_expr);
  }

  // Any preimage of an empty polyhedron is empty.
  if (marked_empty()) {
    return;
  }

  const bool negative_denom = (denominator < 0);
  const Coefficient& lb_var_coeff = lb_expr.coefficient(var);
  const Coefficient& ub_var_coeff = ub_expr.coefficient(var);

  // If the implied constraint between `ub_expr and `lb_expr' is
  // independent of `var', then impose it now.
  if (lb_var_coeff == ub_var_coeff) {
    if (negative_denom) {
      refine_with_constraint(lb_expr >= ub_expr);
    }
    else {
      refine_with_constraint(lb_expr <= ub_expr);
    }
  }

  ITV& seq_var = seq[var.id()];
  if (!seq_var.is_universe()) {
    // We want to work with a positive denominator,
    // so the sign and its (unsigned) value are separated.
    PPL_DIRTY_TEMP_COEFFICIENT(pos_denominator);
    pos_denominator = denominator;
    if (negative_denom) {
      neg_assign(pos_denominator, pos_denominator);
    }
    // Store all the information about the upper and lower bounds
    // for `var' before making this interval unbounded.
    bool open_lower = seq_var.lower_is_open();
    bool unbounded_lower = seq_var.lower_is_boundary_infinity();
    PPL_DIRTY_TEMP(mpq_class, q_seq_var_lower);
    PPL_DIRTY_TEMP_COEFFICIENT(numer_lower);
    PPL_DIRTY_TEMP_COEFFICIENT(denom_lower);
    if (!unbounded_lower) {
      assign_r(q_seq_var_lower, seq_var.lower(), ROUND_NOT_NEEDED);
      assign_r(numer_lower, q_seq_var_lower.get_num(), ROUND_NOT_NEEDED);
      assign_r(denom_lower, q_seq_var_lower.get_den(), ROUND_NOT_NEEDED);
      if (negative_denom) {
        neg_assign(denom_lower, denom_lower);
      }
      numer_lower *= pos_denominator;
      seq_var.lower_extend();
    }
    bool open_upper = seq_var.upper_is_open();
    bool unbounded_upper = seq_var.upper_is_boundary_infinity();
    PPL_DIRTY_TEMP(mpq_class, q_seq_var_upper);
    PPL_DIRTY_TEMP_COEFFICIENT(numer_upper);
    PPL_DIRTY_TEMP_COEFFICIENT(denom_upper);
    if (!unbounded_upper) {
      assign_r(q_seq_var_upper, seq_var.upper(), ROUND_NOT_NEEDED);
      assign_r(numer_upper, q_seq_var_upper.get_num(), ROUND_NOT_NEEDED);
      assign_r(denom_upper, q_seq_var_upper.get_den(), ROUND_NOT_NEEDED);
      if (negative_denom) {
        neg_assign(denom_upper, denom_upper);
      }
      numer_upper *= pos_denominator;
      seq_var.upper_extend();
    }

    if (!unbounded_lower) {
      // `lb_expr' is revised by removing the `var' component,
      // multiplying by `-' denominator of the lower bound for `var',
      // and adding the lower bound for `var' to the inhomogeneous term.
      Linear_Expression revised_lb_expr(ub_expr);
      revised_lb_expr -= ub_var_coeff * var;
      PPL_DIRTY_TEMP_COEFFICIENT(d);
      neg_assign(d, denom_lower);
      revised_lb_expr *= d;
      revised_lb_expr += numer_lower;

      // Find the minimum value for the revised lower bound expression
      // and use this to refine the appropriate bound.
      bool included;
      PPL_DIRTY_TEMP_COEFFICIENT(denom);
      if (minimize(revised_lb_expr, numer_lower, denom, included)) {
        denom_lower *= (denom * ub_var_coeff);
        PPL_DIRTY_TEMP(mpq_class, q);
        assign_r(q.get_num(), numer_lower, ROUND_NOT_NEEDED);
        assign_r(q.get_den(), denom_lower, ROUND_NOT_NEEDED);
        q.canonicalize();
        if (!included) {
          open_lower = true;
        }
        Relation_Symbol rel;
        if ((ub_var_coeff >= 0) ? !negative_denom : negative_denom) {
          rel = open_lower ? GREATER_THAN : GREATER_OR_EQUAL;
        }
        else {
          rel = open_lower ? LESS_THAN : LESS_OR_EQUAL;
        }
        seq_var.add_constraint(i_constraint(rel, q));
        if (seq_var.is_empty()) {
          set_empty();
          return;
        }
      }
    }

    if (!unbounded_upper) {
      // `ub_expr' is revised by removing the `var' component,
      // multiplying by `-' denominator of the upper bound for `var',
      // and adding the upper bound for `var' to the inhomogeneous term.
      Linear_Expression revised_ub_expr(lb_expr);
      revised_ub_expr -= lb_var_coeff * var;
      PPL_DIRTY_TEMP_COEFFICIENT(d);
      neg_assign(d, denom_upper);
      revised_ub_expr *= d;
      revised_ub_expr += numer_upper;

      // Find the maximum value for the revised upper bound expression
      // and use this to refine the appropriate bound.
      bool included;
      PPL_DIRTY_TEMP_COEFFICIENT(denom);
      if (maximize(revised_ub_expr, numer_upper, denom, included)) {
        denom_upper *= (denom * lb_var_coeff);
        PPL_DIRTY_TEMP(mpq_class, q);
        assign_r(q.get_num(), numer_upper, ROUND_NOT_NEEDED);
        assign_r(q.get_den(), denom_upper, ROUND_NOT_NEEDED);
        q.canonicalize();
        if (!included) {
          open_upper = true;
        }
        Relation_Symbol rel;
        if ((lb_var_coeff >= 0) ? !negative_denom : negative_denom) {
          rel = open_upper ? LESS_THAN : LESS_OR_EQUAL;
        }
        else {
          rel = open_upper ? GREATER_THAN : GREATER_OR_EQUAL;
        }
        seq_var.add_constraint(i_constraint(rel, q));
        if (seq_var.is_empty()) {
          set_empty();
          return;
        }
      }
    }
  }

  // If the implied constraint between `ub_expr and `lb_expr' is
  // dependent on `var', then impose on the new box.
  if (lb_var_coeff != ub_var_coeff) {
    if (denominator > 0) {
      refine_with_constraint(lb_expr <= ub_expr);
    }
    else {
      refine_with_constraint(lb_expr >= ub_expr);
    }
  }

  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>
::generalized_affine_image(const Variable var,
                           const Relation_Symbol relsym,
                           const Linear_Expression& expr,
                           Coefficient_traits::const_reference denominator) {
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("generalized_affine_image(v, r, e, d)", "d == 0");
  }

  // Dimension-compatibility checks.
  const dimension_type space_dim = space_dimension();
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  if (space_dim < expr.space_dimension()) {
    throw_dimension_incompatible("generalized_affine_image(v, r, e, d)",
                                 "e", expr);
  }
  // `var' should be one of the dimensions of the box.
  const dimension_type var_space_dim = var.space_dimension();
  if (space_dim < var_space_dim) {
    throw_dimension_incompatible("generalized_affine_image(v, r, e, d)",
                                 "v", var);
  }

  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL) {
    throw_invalid_argument("generalized_affine_image(v, r, e, d)",
                           "r is the disequality relation symbol");
  }

  // First compute the affine image.
  affine_image(var, expr, denominator);

  if (relsym == EQUAL) {
    // The affine relation is indeed an affine function.
    return;
  }
  // Any image of an empty box is empty.
  if (is_empty()) {
    return;
  }

  ITV& seq_var = seq[var.id()];
  switch (relsym) {
  case LESS_OR_EQUAL:
    seq_var.lower_extend();
    break;
  case LESS_THAN:
    seq_var.lower_extend();
    if (!seq_var.upper_is_boundary_infinity()) {
      seq_var.remove_sup();
    }
    break;
  case GREATER_OR_EQUAL:
    seq_var.upper_extend();
    break;
  case GREATER_THAN:
    seq_var.upper_extend();
    if (!seq_var.lower_is_boundary_infinity()) {
      seq_var.remove_inf();
    }
    break;
  default:
    // The EQUAL and NOT_EQUAL cases have been already dealt with.
    PPL_UNREACHABLE;
    break;
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>
::generalized_affine_preimage(const Variable var,
                              const Relation_Symbol relsym,
                              const Linear_Expression& expr,
                              Coefficient_traits::const_reference denominator)
{
  // The denominator cannot be zero.
  if (denominator == 0) {
    throw_invalid_argument("generalized_affine_preimage(v, r, e, d)",
                           "d == 0");
  }
  // Dimension-compatibility checks.
  const dimension_type space_dim = space_dimension();
  // The dimension of `expr' should not be greater than the dimension
  // of `*this'.
  if (space_dim < expr.space_dimension()) {
    throw_dimension_incompatible("generalized_affine_preimage(v, r, e, d)",
                                 "e", expr);
  }
  // `var' should be one of the dimensions of the box.
  const dimension_type var_space_dim = var.space_dimension();
  if (space_dim < var_space_dim) {
    throw_dimension_incompatible("generalized_affine_preimage(v, r, e, d)",
                                 "v", var);
  }
  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL) {
    throw_invalid_argument("generalized_affine_preimage(v, r, e, d)",
                           "r is the disequality relation symbol");
  }

  // Check whether the affine relation is indeed an affine function.
  if (relsym == EQUAL) {
    affine_preimage(var, expr, denominator);
    return;
  }

  // Compute the reversed relation symbol to simplify later coding.
  Relation_Symbol reversed_relsym;
  switch (relsym) {
  case LESS_THAN:
    reversed_relsym = GREATER_THAN;
    break;
  case LESS_OR_EQUAL:
    reversed_relsym = GREATER_OR_EQUAL;
    break;
  case GREATER_OR_EQUAL:
    reversed_relsym = LESS_OR_EQUAL;
    break;
  case GREATER_THAN:
    reversed_relsym = LESS_THAN;
    break;
  default:
    // The EQUAL and NOT_EQUAL cases have been already dealt with.
    PPL_UNREACHABLE;
    break;
  }

  // Check whether the preimage of this affine relation can be easily
  // computed as the image of its inverse relation.
  const Coefficient& var_coefficient = expr.coefficient(var);
  if (var_coefficient != 0) {
    Linear_Expression inverse_expr
      = expr - (denominator + var_coefficient) * var;
    PPL_DIRTY_TEMP_COEFFICIENT(inverse_denominator);
    neg_assign(inverse_denominator, var_coefficient);
    Relation_Symbol inverse_relsym
      = (sgn(denominator) == sgn(inverse_denominator))
      ? relsym
      : reversed_relsym;
    generalized_affine_image(var, inverse_relsym, inverse_expr,
                             inverse_denominator);
    return;
  }

  // Here `var_coefficient == 0', so that the preimage cannot
  // be easily computed by inverting the affine relation.
  // Shrink the box by adding the constraint induced
  // by the affine relation.
  // First, compute the maximum and minimum value reached by
  // `denominator*var' on the box as we need to use non-relational
  // expressions.
  PPL_DIRTY_TEMP_COEFFICIENT(max_numer);
  PPL_DIRTY_TEMP_COEFFICIENT(max_denom);
  bool max_included;
  bool bound_above = maximize(denominator*var, max_numer, max_denom, max_included);
  PPL_DIRTY_TEMP_COEFFICIENT(min_numer);
  PPL_DIRTY_TEMP_COEFFICIENT(min_denom);
  bool min_included;
  bool bound_below = minimize(denominator*var, min_numer, min_denom, min_included);
  // Use the correct relation symbol
  const Relation_Symbol corrected_relsym
    = (denominator > 0) ? relsym : reversed_relsym;
  // Revise the expression to take into account the denominator of the
  // maximum/minimum value for `var'.
  Linear_Expression revised_expr;
  PPL_DIRTY_TEMP_COEFFICIENT(d);
  if (corrected_relsym == LESS_THAN || corrected_relsym == LESS_OR_EQUAL) {
    if (bound_below) {
      revised_expr = expr;
      revised_expr.set_inhomogeneous_term(Coefficient_zero());
      revised_expr *= d;
    }
  }
  else {
    if (bound_above) {
      revised_expr = expr;
      revised_expr.set_inhomogeneous_term(Coefficient_zero());
      revised_expr *= max_denom;
    }
  }

  switch (corrected_relsym) {
  case LESS_THAN:
    if (bound_below) {
      refine_with_constraint(min_numer < revised_expr);
    }
    break;
  case LESS_OR_EQUAL:
    if (bound_below) {
      (min_included)
        ? refine_with_constraint(min_numer <= revised_expr)
        : refine_with_constraint(min_numer < revised_expr);
    }
    break;
  case GREATER_OR_EQUAL:
    if (bound_above) {
      (max_included)
        ? refine_with_constraint(max_numer >= revised_expr)
        : refine_with_constraint(max_numer > revised_expr);
    }
    break;
  case GREATER_THAN:
    if (bound_above) {
      refine_with_constraint(max_numer > revised_expr);
    }
    break;
  default:
    // The EQUAL and NOT_EQUAL cases have been already dealt with.
    PPL_UNREACHABLE;
    break;
  }
  // If the shrunk box is empty, its preimage is empty too.
  if (is_empty()) {
    return;
  }
  ITV& seq_v = seq[var.id()];
  seq_v.assign(UNIVERSE);
  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>
::generalized_affine_image(const Linear_Expression& lhs,
                           const Relation_Symbol relsym,
                           const Linear_Expression& rhs) {
  // Dimension-compatibility checks.
  // The dimension of `lhs' should not be greater than the dimension
  // of `*this'.
  dimension_type lhs_space_dim = lhs.space_dimension();
  const dimension_type space_dim = space_dimension();
  if (space_dim < lhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_image(e1, r, e2)",
                                 "e1", lhs);
  }
  // The dimension of `rhs' should not be greater than the dimension
  // of `*this'.
  const dimension_type rhs_space_dim = rhs.space_dimension();
  if (space_dim < rhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_image(e1, r, e2)",
                                 "e2", rhs);
  }

  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL) {
    throw_invalid_argument("generalized_affine_image(e1, r, e2)",
                           "r is the disequality relation symbol");
  }

  // Any image of an empty box is empty.
  if (marked_empty()) {
    return;
  }

  // Compute the maximum and minimum value reached by the rhs on the box.
  PPL_DIRTY_TEMP_COEFFICIENT(max_numer);
  PPL_DIRTY_TEMP_COEFFICIENT(max_denom);
  bool max_included;
  bool max_rhs = maximize(rhs, max_numer, max_denom, max_included);
  PPL_DIRTY_TEMP_COEFFICIENT(min_numer);
  PPL_DIRTY_TEMP_COEFFICIENT(min_denom);
  bool min_included;
  bool min_rhs = minimize(rhs, min_numer, min_denom, min_included);

  // Check whether there is 0, 1 or more than one variable in the lhs
  // and record the variable with the highest dimension; set the box
  // intervals to be unbounded for all other dimensions with non-zero
  // coefficients in the lhs.
  bool has_var = false;
  dimension_type has_var_id = lhs.last_nonzero();

  if (has_var_id != 0) {
    has_var = true;
    --has_var_id;
    dimension_type other_var = lhs.first_nonzero(1, has_var_id + 1);
    --other_var;
    if (other_var != has_var_id) {
      // There is more than one dimension with non-zero coefficient, so
      // we cannot have any information about the dimensions in the lhs.
      ITV& seq_var = seq[has_var_id];
      seq_var.assign(UNIVERSE);
      // Since all but the highest dimension with non-zero coefficient
      // in the lhs have been set unbounded, it remains to set the
      // highest dimension in the lhs unbounded.
      ITV& seq_i = seq[other_var];
      seq_i.assign(UNIVERSE);
      PPL_ASSERT(OK());
      return;
    }
  }

  if (has_var) {
    // There is exactly one dimension with non-zero coefficient.
    ITV& seq_var = seq[has_var_id];

    // Compute the new bounds for this dimension defined by the rhs
    // expression.
    const Coefficient& inhomo = lhs.inhomogeneous_term();
    const Coefficient& coeff = lhs.coefficient(Variable(has_var_id));
    PPL_DIRTY_TEMP(mpq_class, q_max);
    PPL_DIRTY_TEMP(mpq_class, q_min);
    if (max_rhs) {
      max_numer -= inhomo * max_denom;
      max_denom *= coeff;
      assign_r(q_max.get_num(), max_numer, ROUND_NOT_NEEDED);
      assign_r(q_max.get_den(), max_denom, ROUND_NOT_NEEDED);
      q_max.canonicalize();
    }
    if (min_rhs) {
      min_numer -= inhomo * min_denom;
      min_denom *= coeff;
      assign_r(q_min.get_num(), min_numer, ROUND_NOT_NEEDED);
      assign_r(q_min.get_den(), min_denom, ROUND_NOT_NEEDED);
      q_min.canonicalize();
    }

    // The choice as to which bounds should be set depends on the sign
    // of the coefficient of the dimension `has_var_id' in the lhs.
    if (coeff > 0) {
      // The coefficient of the dimension in the lhs is positive.
      switch (relsym) {
      case LESS_OR_EQUAL:
        if (max_rhs) {
          Relation_Symbol rel = max_included ? LESS_OR_EQUAL : LESS_THAN;
          seq_var.build(i_constraint(rel, q_max));
        }
        else {
          seq_var.assign(UNIVERSE);
        }
        break;
      case LESS_THAN:
        if (max_rhs) {
          seq_var.build(i_constraint(LESS_THAN, q_max));
        }
        else {
          seq_var.assign(UNIVERSE);
        }
        break;
      case EQUAL:
        {
          I_Constraint<mpq_class> l;
          I_Constraint<mpq_class> u;
          if (max_rhs) {
            u.set(max_included ? LESS_OR_EQUAL : LESS_THAN, q_max);
          }
          if (min_rhs) {
            l.set(min_included ? GREATER_OR_EQUAL : GREATER_THAN, q_min);
          }
          seq_var.build(l, u);
          break;
        }
      case GREATER_OR_EQUAL:
        if (min_rhs) {
          Relation_Symbol rel = min_included ? GREATER_OR_EQUAL : GREATER_THAN;
          seq_var.build(i_constraint(rel, q_min));
        }
        else {
          seq_var.assign(UNIVERSE);
        }
        break;
      case GREATER_THAN:
        if (min_rhs) {
          seq_var.build(i_constraint(GREATER_THAN, q_min));
        }
        else {
          seq_var.assign(UNIVERSE);
        }
        break;
      default:
        // The NOT_EQUAL case has been already dealt with.
        PPL_UNREACHABLE;
        break;
      }
    }
    else {
      // The coefficient of the dimension in the lhs is negative.
      switch (relsym) {
      case GREATER_OR_EQUAL:
        if (min_rhs) {
          Relation_Symbol rel = min_included ? LESS_OR_EQUAL : LESS_THAN;
          seq_var.build(i_constraint(rel, q_min));
        }
        else {
          seq_var.assign(UNIVERSE);
        }
        break;
      case GREATER_THAN:
        if (min_rhs) {
          seq_var.build(i_constraint(LESS_THAN, q_min));
        }
        else {
          seq_var.assign(UNIVERSE);
        }
        break;
      case EQUAL:
        {
          I_Constraint<mpq_class> l;
          I_Constraint<mpq_class> u;
          if (max_rhs) {
            l.set(max_included ? GREATER_OR_EQUAL : GREATER_THAN, q_max);
          }
          if (min_rhs) {
            u.set(min_included ? LESS_OR_EQUAL : LESS_THAN, q_min);
          }
          seq_var.build(l, u);
          break;
        }
      case LESS_OR_EQUAL:
        if (max_rhs) {
          Relation_Symbol rel = max_included ? GREATER_OR_EQUAL : GREATER_THAN;
          seq_var.build(i_constraint(rel, q_max));
        }
        else {
          seq_var.assign(UNIVERSE);
        }
        break;
      case LESS_THAN:
        if (max_rhs) {
          seq_var.build(i_constraint(GREATER_THAN, q_max));
        }
        else {
          seq_var.assign(UNIVERSE);
        }
        break;
      default:
        // The NOT_EQUAL case has been already dealt with.
        PPL_UNREACHABLE;
        break;
      }
    }
  }

  else {
    // The lhs is a constant value, so we just need to add the
    // appropriate constraint.
    const Coefficient& inhomo = lhs.inhomogeneous_term();
    switch (relsym) {
    case LESS_THAN:
      refine_with_constraint(inhomo < rhs);
      break;
    case LESS_OR_EQUAL:
      refine_with_constraint(inhomo <= rhs);
      break;
    case EQUAL:
      refine_with_constraint(inhomo == rhs);
      break;
    case GREATER_OR_EQUAL:
      refine_with_constraint(inhomo >= rhs);
      break;
    case GREATER_THAN:
      refine_with_constraint(inhomo > rhs);
      break;
    default:
      // The NOT_EQUAL case has been already dealt with.
      PPL_UNREACHABLE;
      break;
    }
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
void
Box<ITV>::generalized_affine_preimage(const Linear_Expression& lhs,
                                      const Relation_Symbol relsym,
                                      const Linear_Expression& rhs) {
  // Dimension-compatibility checks.
  // The dimension of `lhs' should not be greater than the dimension
  // of `*this'.
  dimension_type lhs_space_dim = lhs.space_dimension();
  const dimension_type space_dim = space_dimension();
  if (space_dim < lhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_image(e1, r, e2)",
                                 "e1", lhs);
  }
  // The dimension of `rhs' should not be greater than the dimension
  // of `*this'.
  const dimension_type rhs_space_dim = rhs.space_dimension();
  if (space_dim < rhs_space_dim) {
    throw_dimension_incompatible("generalized_affine_image(e1, r, e2)",
                                 "e2", rhs);
  }

  // The relation symbol cannot be a disequality.
  if (relsym == NOT_EQUAL) {
    throw_invalid_argument("generalized_affine_image(e1, r, e2)",
                           "r is the disequality relation symbol");
  }
  // Any image of an empty box is empty.
  if (marked_empty()) {
    return;
  }
  // For any dimension occurring in the lhs, swap and change the sign
  // of this component for the rhs and lhs.  Then use these in a call
  // to generalized_affine_image/3.
  Linear_Expression revised_lhs = lhs;
  Linear_Expression revised_rhs = rhs;
  for (Linear_Expression::const_iterator i = lhs.begin(),
         i_end = lhs.end(); i != i_end; ++i) {
    const Variable var = i.variable();
    PPL_DIRTY_TEMP_COEFFICIENT(tmp);
    tmp = *i;
    tmp += rhs.coefficient(var);
    sub_mul_assign(revised_rhs, tmp, var);
    sub_mul_assign(revised_lhs, tmp, var);
  }
  generalized_affine_image(revised_lhs, relsym, revised_rhs);
  PPL_ASSERT(OK());
}

template <typename ITV>
template <typename T, typename Iterator>
typename Enable_If<Is_Same<T, Box<ITV> >::value
                   && Is_Same_Or_Derived<Interval_Base, ITV>::value,
                   void>::type
Box<ITV>::CC76_widening_assign(const T& y, Iterator first, Iterator last) {
  if (y.is_empty()) {
    return;
  }
  for (dimension_type i = seq.size(); i-- > 0; ) {
    seq[i].CC76_widening_assign(y.seq[i], first, last);
  }

  PPL_ASSERT(OK());
}

template <typename ITV>
template <typename T>
typename Enable_If<Is_Same<T, Box<ITV> >::value
                   && Is_Same_Or_Derived<Interval_Base, ITV>::value,
                   void>::type
Box<ITV>::CC76_widening_assign(const T& y, unsigned* tp) {
  static typename ITV::boundary_type stop_points[] = {
    typename ITV::boundary_type(-2),
    typename ITV::boundary_type(-1),
    typename ITV::boundary_type(0),
    typename ITV::boundary_type(1),
    typename ITV::boundary_type(2)
  };

  Box& x = *this;
  // If there are tokens available, work on a temporary copy.
  if (tp != 0 && *tp > 0) {
    Box<ITV> x_tmp(x);
    x_tmp.CC76_widening_assign(y, 0);
    // If the widening was not precise, use one of the available tokens.
    if (!x.contains(x_tmp)) {
      --(*tp);
    }
    return;
  }
  x.CC76_widening_assign(y,
                         stop_points,
                         stop_points
                         + sizeof(stop_points)/sizeof(stop_points[0]));
}

template <typename ITV>
void
Box<ITV>::get_limiting_box(const Constraint_System& cs,
                           Box& limiting_box) const {
  // Private method: the caller has to ensure the following.
  PPL_ASSERT(cs.space_dimension() <= space_dimension());

  for (Constraint_System::const_iterator cs_i = cs.begin(),
         cs_end = cs.end(); cs_i != cs_end; ++cs_i) {
    const Constraint& c = *cs_i;
    dimension_type c_num_vars = 0;
    dimension_type c_only_var = 0;
    // Constraints that are not interval constraints are ignored.
    if (!Box_Helpers::extract_interval_constraint(c, c_num_vars, c_only_var)) {
      continue;
    }
    // Trivial constraints are ignored.
    if (c_num_vars != 0) {
      // c is a non-trivial interval constraint.
      // add interval constraint to limiting box
      const Coefficient& n = c.inhomogeneous_term();
      const Coefficient& d = c.coefficient(Variable(c_only_var));
      if (interval_relation(seq[c_only_var], c.type(), n, d)
          == Poly_Con_Relation::is_included()) {
        limiting_box.add_interval_constraint_no_check(c_only_var, c.type(),
                                                      n, d);
      }
    }
  }
}

template <typename ITV>
void
Box<ITV>::limited_CC76_extrapolation_assign(const Box& y,
                                            const Constraint_System& cs,
                                            unsigned* tp) {
  Box& x = *this;
  const dimension_type space_dim = x.space_dimension();

  // Dimension-compatibility check.
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("limited_CC76_extrapolation_assign(y, cs)",
                                  y);
  }
  // `cs' must be dimension-compatible with the two boxes.
  const dimension_type cs_space_dim = cs.space_dimension();
  if (space_dim < cs_space_dim) {
    throw_constraint_incompatible("limited_CC76_extrapolation_assign(y, cs)");
  }
  // The limited CC76-extrapolation between two boxes in a
  // zero-dimensional space is also a zero-dimensional box
  if (space_dim == 0) {
    return;
  }
  // Assume `y' is contained in or equal to `*this'.
  PPL_EXPECT_HEAVY(copy_contains(*this, y));

  // If `*this' is empty, since `*this' contains `y', `y' is empty too.
  if (marked_empty()) {
    return;
  }
  // If `y' is empty, we return.
  if (y.marked_empty()) {
    return;
  }
  // Build a limiting box using all the constraints in cs
  // that are satisfied by *this.
  Box limiting_box(space_dim, UNIVERSE);
  get_limiting_box(cs, limiting_box);

  x.CC76_widening_assign(y, tp);

  // Intersect the widened box with the limiting box.
  intersection_assign(limiting_box);
}

template <typename ITV>
template <typename T>
typename Enable_If<Is_Same<T, Box<ITV> >::value
                   && Is_Same_Or_Derived<Interval_Base, ITV>::value,
                   void>::type
Box<ITV>::CC76_narrowing_assign(const T& y) {
  const dimension_type space_dim = space_dimension();

  // Dimension-compatibility check.
  if (space_dim != y.space_dimension()) {
    throw_dimension_incompatible("CC76_narrowing_assign(y)", y);
  }

  // Assume `*this' is contained in or equal to `y'.
  PPL_EXPECT_HEAVY(copy_contains(y, *this));

  // If both boxes are zero-dimensional,
  // since `y' contains `*this', we simply return `*this'.
  if (space_dim == 0) {
    return;
  }
  // If `y' is empty, since `y' contains `this', `*this' is empty too.
  if (y.is_empty()) {
    return;
  }
  // If `*this' is empty, we return.
  if (is_empty()) {
    return;
  }
  // Replace each constraint in `*this' by the corresponding constraint
  // in `y' if the corresponding inhomogeneous terms are both finite.
  for (dimension_type i = space_dim; i-- > 0; ) {
    ITV& x_i = seq[i];
    const ITV& y_i = y.seq[i];
    if (!x_i.lower_is_boundary_infinity()
        && !y_i.lower_is_boundary_infinity()
        && x_i.lower() != y_i.lower()) {
      x_i.lower() = y_i.lower();
    }
    if (!x_i.upper_is_boundary_infinity()
        && !y_i.upper_is_boundary_infinity()
        && x_i.upper() != y_i.upper()) {
      x_i.upper() = y_i.upper();
    }
  }
  PPL_ASSERT(OK());
}

template <typename ITV>
Constraint_System
Box<ITV>::constraints() const {
  const dimension_type space_dim = space_dimension();
  Constraint_System cs;
  cs.set_space_dimension(space_dim);

  if (space_dim == 0) {
    if (marked_empty()) {
      cs = Constraint_System::zero_dim_empty();
    }
    return cs;
  }

  if (marked_empty()) {
    cs.insert(Constraint::zero_dim_false());
    return cs;
  }

  for (dimension_type k = 0; k < space_dim; ++k) {
    const Variable v_k = Variable(k);
    PPL_DIRTY_TEMP_COEFFICIENT(n);
    PPL_DIRTY_TEMP_COEFFICIENT(d);
    bool closed = false;
    if (has_lower_bound(v_k, n, d, closed)) {
      if (closed) {
        cs.insert(d * v_k >= n);
      }
      else {
        cs.insert(d * v_k > n);
      }
    }
    if (has_upper_bound(v_k, n, d, closed)) {
      if (closed) {
        cs.insert(d * v_k <= n);
      }
      else {
        cs.insert(d * v_k < n);
      }
    }
  }
  return cs;
}

template <typename ITV>
Constraint_System
Box<ITV>::minimized_constraints() const {
  const dimension_type space_dim = space_dimension();
  Constraint_System cs;
  cs.set_space_dimension(space_dim);

  if (space_dim == 0) {
    if (marked_empty()) {
      cs = Constraint_System::zero_dim_empty();
    }
    return cs;
  }

  // Make sure emptiness is detected.
  if (is_empty()) {
    cs.insert(Constraint::zero_dim_false());
    return cs;
  }

  for (dimension_type k = 0; k < space_dim; ++k) {
    const Variable v_k = Variable(k);
    PPL_DIRTY_TEMP_COEFFICIENT(n);
    PPL_DIRTY_TEMP_COEFFICIENT(d);
    bool closed = false;
    if (has_lower_bound(v_k, n, d, closed)) {
      if (closed) {
        // Make sure equality constraints are detected.
        if (seq[k].is_singleton()) {
          cs.insert(d * v_k == n);
          continue;
        }
        else {
          cs.insert(d * v_k >= n);
        }
      }
      else {
        cs.insert(d * v_k > n);
      }
    }
    if (has_upper_bound(v_k, n, d, closed)) {
      if (closed) {
        cs.insert(d * v_k <= n);
      }
      else {
        cs.insert(d * v_k < n);
      }
    }
  }
  return cs;
}

template <typename ITV>
Congruence_System
Box<ITV>::congruences() const {
  const dimension_type space_dim = space_dimension();
  Congruence_System cgs(space_dim);

  if (space_dim == 0) {
    if (marked_empty()) {
      cgs = Congruence_System::zero_dim_empty();
    }
    return cgs;
  }

  // Make sure emptiness is detected.
  if (is_empty()) {
    cgs.insert(Congruence::zero_dim_false());
    return cgs;
  }

  for (dimension_type k = 0; k < space_dim; ++k) {
    const Variable v_k = Variable(k);
    PPL_DIRTY_TEMP_COEFFICIENT(n);
    PPL_DIRTY_TEMP_COEFFICIENT(d);
    bool closed = false;
    if (has_lower_bound(v_k, n, d, closed) && closed) {
      // Make sure equality congruences are detected.
      if (seq[k].is_singleton()) {
        cgs.insert((d * v_k %= n) / 0);
      }
    }
  }
  return cgs;
}

template <typename ITV>
memory_size_type
Box<ITV>::external_memory_in_bytes() const {
  memory_size_type n = seq.capacity() * sizeof(ITV);
  for (dimension_type k = seq.size(); k-- > 0; ) {
    n += seq[k].external_memory_in_bytes();
  }
  return n;
}

/*! \relates Parma_Polyhedra_Library::Box */
template <typename ITV>
std::ostream&
IO_Operators::operator<<(std::ostream& s, const Box<ITV>& box) {
  if (box.is_empty()) {
    s << "false";
  }
  else if (box.is_universe()) {
    s << "true";
  }
  else {
    for (dimension_type k = 0,
           space_dim = box.space_dimension(); k < space_dim; ) {
      s << Variable(k) << " in " << box[k];
      ++k;
      if (k < space_dim) {
        s << ", ";
      }
      else {
        break;
      }
    }
  }
  return s;
}

template <typename ITV>
void
Box<ITV>::ascii_dump(std::ostream& s) const {
  const char separator = ' ';
  status.ascii_dump(s);
  const dimension_type space_dim = space_dimension();
  s << "space_dim" << separator << space_dim;
  s << "\n";
  for (dimension_type i = 0; i < space_dim;  ++i) {
    seq[i].ascii_dump(s);
  }
}

PPL_OUTPUT_TEMPLATE_DEFINITIONS(ITV, Box<ITV>)

template <typename ITV>
bool
Box<ITV>::ascii_load(std::istream& s) {
  if (!status.ascii_load(s)) {
    return false;
  }
  std::string str;
  dimension_type space_dim;
  if (!(s >> str) || str != "space_dim") {
    return false;
  }
  if (!(s >> space_dim)) {
    return false;
  }
  seq.clear();
  ITV seq_i;
  for (dimension_type i = 0; i < space_dim;  ++i) {
    if (seq_i.ascii_load(s)) {
      seq.push_back(seq_i);
    }
    else {
      return false;
    }
  }

  // Check invariants.
  PPL_ASSERT(OK());
  return true;
}

template <typename ITV>
void
Box<ITV>::throw_dimension_incompatible(const char* method,
                                       const Box& y) const {
  std::ostringstream s;
  s << "PPL::Box::" << method << ":" << std::endl
    << "this->space_dimension() == " << this->space_dimension()
    << ", y->space_dimension() == " << y.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename ITV>
void
Box<ITV>
::throw_dimension_incompatible(const char* method,
                               dimension_type required_dim) const {
  std::ostringstream s;
  s << "PPL::Box::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", required dimension == " << required_dim << ".";
  throw std::invalid_argument(s.str());
}

template <typename ITV>
void
Box<ITV>::throw_dimension_incompatible(const char* method,
                                       const Constraint& c) const {
  std::ostringstream s;
  s << "PPL::Box::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", c->space_dimension == " << c.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename ITV>
void
Box<ITV>::throw_dimension_incompatible(const char* method,
                                       const Congruence& cg) const {
  std::ostringstream s;
  s << "PPL::Box::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", cg->space_dimension == " << cg.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename ITV>
void
Box<ITV>::throw_dimension_incompatible(const char* method,
                                       const Constraint_System& cs) const {
  std::ostringstream s;
  s << "PPL::Box::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", cs->space_dimension == " << cs.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename ITV>
void
Box<ITV>::throw_dimension_incompatible(const char* method,
                                       const Congruence_System& cgs) const {
  std::ostringstream s;
  s << "PPL::Box::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", cgs->space_dimension == " << cgs.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename ITV>
void
Box<ITV>::throw_dimension_incompatible(const char* method,
                                       const Generator& g) const {
  std::ostringstream s;
  s << "PPL::Box::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", g->space_dimension == " << g.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename ITV>
void
Box<ITV>::throw_constraint_incompatible(const char* method) {
  std::ostringstream s;
  s << "PPL::Box::" << method << ":" << std::endl
    << "the constraint is incompatible.";
  throw std::invalid_argument(s.str());
}

template <typename ITV>
void
Box<ITV>::throw_expression_too_complex(const char* method,
                                       const Linear_Expression& le) {
  using namespace IO_Operators;
  std::ostringstream s;
  s << "PPL::Box::" << method << ":" << std::endl
    << le << " is too complex.";
  throw std::invalid_argument(s.str());
}

template <typename ITV>
void
Box<ITV>::throw_dimension_incompatible(const char* method,
                                       const char* le_name,
                                       const Linear_Expression& le) const {
  std::ostringstream s;
  s << "PPL::Box::" << method << ":" << std::endl
    << "this->space_dimension() == " << space_dimension()
    << ", " << le_name << "->space_dimension() == "
    << le.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename ITV>
template <typename C>
void
Box<ITV>::throw_dimension_incompatible(const char* method,
                                       const char* lf_name,
                                       const Linear_Form<C>& lf) const {
  std::ostringstream s;
  s << "PPL::Box::" << method << ":\n"
    << "this->space_dimension() == " << space_dimension()
    << ", " << lf_name << "->space_dimension() == "
    << lf.space_dimension() << ".";
  throw std::invalid_argument(s.str());
}

template <typename ITV>
void
Box<ITV>::throw_invalid_argument(const char* method, const char* reason) {
  std::ostringstream s;
  s << "PPL::Box::" << method << ":" << std::endl
    << reason;
  throw std::invalid_argument(s.str());
}

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Box */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Specialization,
          typename Temp, typename To, typename ITV>
bool
l_m_distance_assign(Checked_Number<To, Extended_Number_Policy>& r,
                    const Box<ITV>& x, const Box<ITV>& y,
                    const Rounding_Dir dir,
                    Temp& tmp0, Temp& tmp1, Temp& tmp2) {
  const dimension_type x_space_dim = x.space_dimension();
  // Dimension-compatibility check.
  if (x_space_dim != y.space_dimension()) {
    return false;
  }
  // Zero-dim boxes are equal if and only if they are both empty or universe.
  if (x_space_dim == 0) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
    }
    else {
      assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
    }
    return true;
  }

  // The distance computation requires a check for emptiness.
  (void) x.is_empty();
  (void) y.is_empty();
  // If one of two boxes is empty, then they are equal if and only if
  // the other box is empty too.
  if (x.marked_empty() || y.marked_empty()) {
    if (x.marked_empty() == y.marked_empty()) {
      assign_r(r, 0, ROUND_NOT_NEEDED);
      return true;
    }
    else {
      goto pinf;
    }
  }

  assign_r(tmp0, 0, ROUND_NOT_NEEDED);
  for (dimension_type i = x_space_dim; i-- > 0; ) {
    const ITV& x_i = x.seq[i];
    const ITV& y_i = y.seq[i];
    // Dealing with the lower bounds.
    if (x_i.lower_is_boundary_infinity()) {
      if (!y_i.lower_is_boundary_infinity()) {
        goto pinf;
      }
    }
    else if (y_i.lower_is_boundary_infinity()) {
      goto pinf;
    }
    else {
      const Temp* tmp1p;
      const Temp* tmp2p;
      if (x_i.lower() > y_i.lower()) {
        maybe_assign(tmp1p, tmp1, x_i.lower(), dir);
        maybe_assign(tmp2p, tmp2, y_i.lower(), inverse(dir));
      }
      else {
        maybe_assign(tmp1p, tmp1, y_i.lower(), dir);
        maybe_assign(tmp2p, tmp2, x_i.lower(), inverse(dir));
      }
      sub_assign_r(tmp1, *tmp1p, *tmp2p, dir);
      PPL_ASSERT(sgn(tmp1) >= 0);
      Specialization::combine(tmp0, tmp1, dir);
    }
    // Dealing with the lower bounds.
    if (x_i.upper_is_boundary_infinity()) {
      if (y_i.upper_is_boundary_infinity()) {
        continue;
      }
      else {
        goto pinf;
      }
    }
    else if (y_i.upper_is_boundary_infinity()) {
      goto pinf;
    }
    else {
      const Temp* tmp1p;
      const Temp* tmp2p;
      if (x_i.upper() > y_i.upper()) {
        maybe_assign(tmp1p, tmp1, x_i.upper(), dir);
        maybe_assign(tmp2p, tmp2, y_i.upper(), inverse(dir));
      }
      else {
        maybe_assign(tmp1p, tmp1, y_i.upper(), dir);
        maybe_assign(tmp2p, tmp2, x_i.upper(), inverse(dir));
      }
      sub_assign_r(tmp1, *tmp1p, *tmp2p, dir);
      PPL_ASSERT(sgn(tmp1) >= 0);
      Specialization::combine(tmp0, tmp1, dir);
    }
  }
  Specialization::finalize(tmp0, dir);
  assign_r(r, tmp0, dir);
  return true;

 pinf:
  assign_r(r, PLUS_INFINITY, ROUND_NOT_NEEDED);
  return true;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Box_defs.hh line 2287. */

/* Automatically generated from PPL source file ../src/Linear_Form_templates.hh line 30. */
#include <stdexcept>
#include <iostream>
#include <cmath>

namespace Parma_Polyhedra_Library {

template <typename C>
Linear_Form<C>::Linear_Form(const Variable v)
  : vec() {
  const dimension_type space_dim = v.space_dimension();
  if (space_dim > max_space_dimension()) {
    throw std::length_error("Linear_Form<C>::"
                            "Linear_Form(v):\n"
                            "v exceeds the maximum allowed "
                            "space dimension.");
  }
  vec.reserve(compute_capacity(space_dim+1, vec_type().max_size()));
  vec.resize(space_dim+1, zero);
  vec[v.space_dimension()] = C(typename C::boundary_type(1));
}

template <typename C>
Linear_Form<C>::Linear_Form(const Variable v, const Variable w)
  : vec() {
  const dimension_type v_space_dim = v.space_dimension();
  const dimension_type w_space_dim = w.space_dimension();
  const dimension_type space_dim = std::max(v_space_dim, w_space_dim);
  if (space_dim > max_space_dimension()) {
    throw std::length_error("Linear_Form<C>::"
                            "Linear_Form(v, w):\n"
                            "v or w exceed the maximum allowed "
                            "space dimension.");
  }
  vec.reserve(compute_capacity(space_dim+1, vec_type().max_size()));
  vec.resize(space_dim+1, zero);
  if (v_space_dim != w_space_dim) {
    vec[v_space_dim] = C(typename C::boundary_type(1));
    vec[w_space_dim] = C(typename C::boundary_type(-1));
  }
}

template <typename C>
Linear_Form<C>::Linear_Form(const Linear_Expression& e)
  : vec() {
  const dimension_type space_dim = e.space_dimension();
  if (space_dim > max_space_dimension()) {
    throw std::length_error("Linear_Form<C>::"
                            "Linear_Form(e):\n"
                            "e exceeds the maximum allowed "
                            "space dimension.");
  }
  vec.reserve(compute_capacity(space_dim+1, vec_type().max_size()));
  vec.resize(space_dim+1);
  for (dimension_type i = space_dim; i-- > 0; )
    vec[i+1] = e.coefficient(Variable(i));
  vec[0] = e.inhomogeneous_term();
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator+(const Linear_Form<C>& f1, const Linear_Form<C>& f2) {
  dimension_type f1_size = f1.size();
  dimension_type f2_size = f2.size();
  dimension_type min_size;
  dimension_type max_size;
  const Linear_Form<C>* p_e_max;
  if (f1_size > f2_size) {
    min_size = f2_size;
    max_size = f1_size;
    p_e_max = &f1;
  }
  else {
    min_size = f1_size;
    max_size = f2_size;
    p_e_max = &f2;
  }

  Linear_Form<C> r(max_size, false);
  dimension_type i = max_size;
  while (i > min_size) {
    --i;
    r[i] = p_e_max->vec[i];
  }
  while (i > 0) {
    --i;
    r[i] = f1[i];
    r[i] += f2[i];
  }
  return r;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator+(const Variable v, const Linear_Form<C>& f) {
  const dimension_type v_space_dim = v.space_dimension();
  if (v_space_dim > Linear_Form<C>::max_space_dimension()) {
    throw std::length_error("Linear_Form "
                            "operator+(v, f):\n"
                            "v exceeds the maximum allowed "
                            "space dimension.");
  }
  Linear_Form<C> r(f);
  if (v_space_dim > f.space_dimension()) {
    r.extend(v_space_dim+1);
  }
  r[v_space_dim] += C(typename C::boundary_type(1));
  return r;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator+(const C& n, const Linear_Form<C>& f) {
  Linear_Form<C> r(f);
  r[0] += n;
  return r;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator-(const Linear_Form<C>& f) {
  Linear_Form<C> r(f);
  for (dimension_type i = f.size(); i-- > 0; ) {
    r[i].neg_assign(r[i]);
  }
  return r;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator-(const Linear_Form<C>& f1, const Linear_Form<C>& f2) {
  dimension_type f1_size = f1.size();
  dimension_type f2_size = f2.size();
  if (f1_size > f2_size) {
    Linear_Form<C> r(f1_size, false);
    dimension_type i = f1_size;
    while (i > f2_size) {
      --i;
      r[i] = f1[i];
    }
    while (i > 0) {
      --i;
      r[i] = f1[i];
      r[i] -= f2[i];
    }
    return r;
  }
  else {
    Linear_Form<C> r(f2_size, false);
    dimension_type i = f2_size;
    while (i > f1_size) {
      --i;
      r[i].neg_assign(f2[i]);
    }
    while (i > 0) {
      --i;
      r[i] = f1[i];
      r[i] -= f2[i];
    }
    return r;
  }
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator-(const Variable v, const Linear_Form<C>& f) {
  const dimension_type v_space_dim = v.space_dimension();
  if (v_space_dim > Linear_Form<C>::max_space_dimension()) {
    throw std::length_error("Linear_Form "
                            "operator-(v, e):\n"
                            "v exceeds the maximum allowed "
                            "space dimension.");
  }
  Linear_Form<C> r(f);
  if (v_space_dim > f.space_dimension()) {
    r.extend(v_space_dim+1);
  }
  for (dimension_type i = f.size(); i-- > 0; ) {
    r[i].neg_assign(r[i]);
  }
  r[v_space_dim] += C(typename C::boundary_type(1));
  return r;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator-(const Linear_Form<C>& f, const Variable v) {
  const dimension_type v_space_dim = v.space_dimension();
  if (v_space_dim > Linear_Form<C>::max_space_dimension()) {
    throw std::length_error("Linear_Form "
                            "operator-(e, v):\n"
                            "v exceeds the maximum allowed "
                            "space dimension.");
  }
  Linear_Form<C> r(f);
  if (v_space_dim > f.space_dimension()) {
    r.extend(v_space_dim+1);
  }
  r[v_space_dim] -= C(typename C::boundary_type(1));
  return r;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator-(const C& n, const Linear_Form<C>& f) {
  Linear_Form<C> r(f);
  for (dimension_type i = f.size(); i-- > 0; ) {
    r[i].neg_assign(r[i]);
  }
  r[0] += n;
  return r;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>
operator*(const C& n, const Linear_Form<C>& f) {
  Linear_Form<C> r(f);
  for (dimension_type i = f.size(); i-- > 0; ) {
    r[i] *= n;
  }
  return r;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>&
operator+=(Linear_Form<C>& f1, const Linear_Form<C>& f2) {
  dimension_type f1_size = f1.size();
  dimension_type f2_size = f2.size();
  if (f1_size < f2_size) {
    f1.extend(f2_size);
  }
  for (dimension_type i = f2_size; i-- > 0; ) {
    f1[i] += f2[i];
  }
  return f1;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>&
operator+=(Linear_Form<C>& f, const Variable v) {
  const dimension_type v_space_dim = v.space_dimension();
  if (v_space_dim > Linear_Form<C>::max_space_dimension()) {
    throw std::length_error("Linear_Form<C>& "
                            "operator+=(e, v):\n"
                            "v exceeds the maximum allowed space dimension.");
  }
  if (v_space_dim > f.space_dimension()) {
    f.extend(v_space_dim+1);
  }
  f[v_space_dim] += C(typename C::boundary_type(1));
  return f;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>&
operator-=(Linear_Form<C>& f1, const Linear_Form<C>& f2) {
  dimension_type f1_size = f1.size();
  dimension_type f2_size = f2.size();
  if (f1_size < f2_size) {
    f1.extend(f2_size);
  }
  for (dimension_type i = f2_size; i-- > 0; ) {
    f1[i] -= f2[i];
  }
  return f1;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>&
operator-=(Linear_Form<C>& f, const Variable v) {
  const dimension_type v_space_dim = v.space_dimension();
  if (v_space_dim > Linear_Form<C>::max_space_dimension()) {
    throw std::length_error("Linear_Form<C>& "
                            "operator-=(e, v):\n"
                            "v exceeds the maximum allowed space dimension.");
  }
  if (v_space_dim > f.space_dimension()) {
    f.extend(v_space_dim+1);
  }
  f[v_space_dim] -= C(typename C::boundary_type(1));
  return f;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>&
operator*=(Linear_Form<C>& f, const C& n) {
  dimension_type f_size = f.size();
  for (dimension_type i = f_size; i-- > 0; ) {
    f[i] *= n;
  }
  return f;
}

/*! \relates Linear_Form */
template <typename C>
Linear_Form<C>&
operator/=(Linear_Form<C>& f, const C& n) {
  dimension_type f_size = f.size();
  for (dimension_type i = f_size; i-- > 0; ) {
    f[i] /= n;
  }
  return f;
}

/*! \relates Linear_Form */
template <typename C>
inline bool
operator==(const Linear_Form<C>& x, const Linear_Form<C>& y) {
  const dimension_type x_size = x.size();
  const dimension_type y_size = y.size();
  if (x_size >= y_size) {
    for (dimension_type i = y_size; i-- > 0; ) {
      if (x[i] != y[i]) {
        return false;
      }
    }
    for (dimension_type i = x_size; --i >= y_size; ) {
      if (x[i] != x.zero) {
        return false;
      }
    }
  }
  else {
    for (dimension_type i = x_size; i-- > 0; ) {
      if (x[i] != y[i]) {
        return false;
      }
    }
    for (dimension_type i = y_size; --i >= x_size; ) {
      if (y[i] != x.zero) {
        return false;
      }
    }

  }

  return true;
}

template <typename C>
void
Linear_Form<C>::negate() {
  for (dimension_type i = vec.size(); i-- > 0; ) {
    vec[i].neg_assign(vec[i]);
  }
  return;
}

template <typename C>
inline memory_size_type
Linear_Form<C>::external_memory_in_bytes() const {
  memory_size_type n = 0;
  for (dimension_type i = size(); i-- > 0; ) {
    n += vec[i].external_memory_in_bytes();
  }
  n += vec.capacity()*sizeof(C);
  return n;
}

template <typename C>
bool
Linear_Form<C>::OK() const {
  for (dimension_type i = size(); i-- > 0; ) {
    if (!vec[i].OK()) {
      return false;
    }
  }
  return true;
}

// Floating point analysis related methods.
template <typename C>
void
Linear_Form<C>::relative_error(
                const Floating_Point_Format analyzed_format,
                Linear_Form& result) const {
  typedef typename C::boundary_type analyzer_format;

  // Get the necessary information on the analyzed's format.
  unsigned int f_base;
  unsigned int f_mantissa_bits;
  switch (analyzed_format) {
    case IEEE754_HALF:
      f_base = float_ieee754_half::BASE;
      f_mantissa_bits = float_ieee754_half::MANTISSA_BITS;
      break;
    case IEEE754_SINGLE:
      f_base = float_ieee754_single::BASE;
      f_mantissa_bits = float_ieee754_single::MANTISSA_BITS;
      break;
    case IEEE754_DOUBLE:
      f_base = float_ieee754_double::BASE;
      f_mantissa_bits = float_ieee754_double::MANTISSA_BITS;
      break;
    case IBM_SINGLE:
      f_base = float_ibm_single::BASE;
      f_mantissa_bits = float_ibm_single::MANTISSA_BITS;
      break;
    case IEEE754_QUAD:
      f_base = float_ieee754_quad::BASE;
      f_mantissa_bits = float_ieee754_quad::MANTISSA_BITS;
      break;
    case INTEL_DOUBLE_EXTENDED:
      f_base = float_intel_double_extended::BASE;
      f_mantissa_bits = float_intel_double_extended::MANTISSA_BITS;
      break;
    default:
      PPL_UNREACHABLE;
      break;
  }

  C error_propagator;
  // We assume that f_base is a power of 2.
  unsigned int u_power = msb_position(f_base) * f_mantissa_bits;
  int neg_power = -static_cast<int>(u_power);
  analyzer_format lb = static_cast<analyzer_format>(ldexp(1.0, neg_power));

  error_propagator.build(i_constraint(GREATER_OR_EQUAL, -lb),
                         i_constraint(LESS_OR_EQUAL, lb));

  // Handle the inhomogeneous term.
  const C* current_term = &inhomogeneous_term();
  assert(current_term->is_bounded());

  C current_multiplier(std::max(std::abs(current_term->lower()),
                                std::abs(current_term->upper())));
  Linear_Form current_result_term(current_multiplier);
  current_result_term *= error_propagator;
  result = Linear_Form(current_result_term);

  // Handle the other terms.
  dimension_type dimension = space_dimension();
  for (dimension_type i = 0; i < dimension; ++i) {
    current_term = &coefficient(Variable(i));
    assert(current_term->is_bounded());
    current_multiplier = C(std::max(std::abs(current_term->lower()),
                                    std::abs(current_term->upper())));
    current_result_term = Linear_Form(Variable(i));
    current_result_term *= current_multiplier;
    current_result_term *= error_propagator;
    result += current_result_term;
  }

  return;
}

template <typename C>
template <typename Target>
bool
Linear_Form<C>::intervalize(const FP_Oracle<Target,C>& oracle,
                            C& result) const {
  result = C(inhomogeneous_term());
  dimension_type dimension = space_dimension();
  for (dimension_type i = 0; i < dimension; ++i) {
    C current_addend = coefficient(Variable(i));
    C curr_int;
    if (!oracle.get_interval(i, curr_int)) {
      return false;
    }
    current_addend *= curr_int;
    result += current_addend;
  }

  return true;
}

/*! \relates Parma_Polyhedra_Library::Linear_Form */
template <typename C>
std::ostream&
IO_Operators::operator<<(std::ostream& s, const Linear_Form<C>& f) {
  const dimension_type num_variables = f.space_dimension();
  bool first = true;
  for (dimension_type v = 0; v < num_variables; ++v) {
    const C& fv = f[v+1];
    if (fv != typename C::boundary_type(0)) {
      if (first) {
        if (fv == typename C::boundary_type(-1)) {
          s << "-";
        }
        else if (fv != typename C::boundary_type(1)) {
          s << fv << "*";
        }
        first = false;
      }
      else {
        if (fv == typename C::boundary_type(-1)) {
          s << " - ";
        }
        else {
          s << " + ";
          if (fv != typename C::boundary_type(1)) {
            s << fv << "*";
          }
        }
      }
      s << Variable(v);
    }
  }
  // Inhomogeneous term.
  const C& it = f[0];
  if (it != 0) {
    if (!first) {
        s << " + ";
    }
    else {
      first = false;
    }
    s << it;
  }

  if (first) {
    // The null linear form.
    s << Linear_Form<C>::zero;
  }
  return s;
}

PPL_OUTPUT_TEMPLATE_DEFINITIONS(C, Linear_Form<C>)

template <typename C>
C Linear_Form<C>::zero(typename C::boundary_type(0));

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/linearize.hh line 1. */
/* Linearization function implementation.
*/


/* Automatically generated from PPL source file ../src/Concrete_Expression_defs.hh line 1. */
/* Concrete_Expression class declaration.
*/


/* Automatically generated from PPL source file ../src/Concrete_Expression_defs.hh line 30. */

namespace Parma_Polyhedra_Library {

//! The type of a concrete expression.
class Concrete_Expression_Type {
public:
  /*! \brief
    Returns the bounded integer type corresponding to \p width,
    \p representation and \p overflow.
  */
  static Concrete_Expression_Type
  bounded_integer(Bounded_Integer_Type_Width width,
                  Bounded_Integer_Type_Representation representation,
                  Bounded_Integer_Type_Overflow overflow);

  /*! \brief
    Returns the floating point type corresponding to \p format.
  */
  static Concrete_Expression_Type
  floating_point(Floating_Point_Format format);

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is a bounded
    integer type.
  */
  bool is_bounded_integer() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is a floating
    point type.
  */
  bool is_floating_point() const;

  /*! \brief
    Returns the width in bits of the bounded integer type encoded by
    \p *this.

    The behavior is undefined if \p *this does not encode a bounded
    integer type.
  */
  Bounded_Integer_Type_Width bounded_integer_type_width() const;

  /*! \brief
    Returns the representation of the bounded integer type encoded by
    \p *this.

    The behavior is undefined if \p *this does not encode a bounded
    integer type.
  */
  Bounded_Integer_Type_Representation
  bounded_integer_type_representation() const;

  /*! \brief
    Returns the overflow behavior of the bounded integer type encoded by
    \p *this.

    The behavior is undefined if \p *this does not encode a bounded
    integer type.
  */
  Bounded_Integer_Type_Overflow
  bounded_integer_type_overflow() const;

  /*! \brief
    Returns the format of the floating point type encoded by \p *this.

    The behavior is undefined if \p *this does not encode a floating
    point type.
  */
  Floating_Point_Format floating_point_format() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

private:
  //! A 32-bit word encoding the type.
  struct Implementation {
    bool bounded_integer:1;
    unsigned int bounded_integer_type_width:23;
    unsigned int bounded_integer_type_representation:2;
    unsigned int bounded_integer_type_overflow:2;
    unsigned int floating_point_format:4;
  };

  //! Constructor from \p implementation.
  Concrete_Expression_Type(Implementation implementation);

  //! The encoding of \p *this.
  Implementation impl;
};

//! Base class for all concrete expressions.
template <typename Target>
class Concrete_Expression_Common {
public:
  //! Returns the type of \* this.
  Concrete_Expression_Type type() const;

  //! Returns the kind of \* this.
  Concrete_Expression_Kind kind() const;

  //! Tests if \p *this has the same kind as <CODE>Derived\<Target\></CODE>.
  template <template <typename T> class Derived>
  bool is() const;

  /*! \brief
    Returns a pointer to \p *this converted to type
    <CODE>Derived\<Target\>*</CODE>.
  */
  template <template <typename T> class Derived>
  Derived<Target>* as();

  /*! \brief
    Returns a pointer to \p *this converted to type
    <CODE>const Derived\<Target\>*</CODE>.
  */
  template <template <typename T> class Derived>
  const Derived<Target>* as() const;

};

//! Base class for binary operator applied to two concrete expressions.
template <typename Target>
class Binary_Operator_Common {
public:
  //! Returns a constant identifying the operator of \p *this.
  Concrete_Expression_BOP binary_operator() const;

  //! Returns the left-hand side of \p *this.
  const Concrete_Expression<Target>* left_hand_side() const;

  //! Returns the right-hand side of \p *this.
  const Concrete_Expression<Target>* right_hand_side() const;
};

//! Base class for unary operator applied to one concrete expression.
template <typename Target>
class Unary_Operator_Common {
public:
  //! Returns a constant identifying the operator of \p *this.
  Concrete_Expression_UOP unary_operator() const;

  //! Returns the argument \p *this.
  const Concrete_Expression<Target>* argument() const;
};

//! Base class for cast operator concrete expressions.
template <typename Target>
class Cast_Operator_Common {
  //! Returns the casted expression.
  const Concrete_Expression<Target>* argument() const;
};

//! Base class for integer constant concrete expressions.
template <typename Target>
class Integer_Constant_Common {
};

//! Base class for floating-point constant concrete expression.
template <typename Target>
class Floating_Point_Constant_Common {
};

//! Base class for references to some approximable.
template <typename Target>
class Approximable_Reference_Common {
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Concrete_Expression_inlines.hh line 1. */
/* Concrete_Expression class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline
Concrete_Expression_Type
::Concrete_Expression_Type(Implementation implementation)
  : impl(implementation) {
}

inline Concrete_Expression_Type
Concrete_Expression_Type
::bounded_integer(const Bounded_Integer_Type_Width width,
                  const Bounded_Integer_Type_Representation representation,
                  const Bounded_Integer_Type_Overflow overflow) {
  Implementation impl;
  impl.bounded_integer = true;
  impl.bounded_integer_type_width = width;
  impl.bounded_integer_type_representation = representation;
  impl.bounded_integer_type_overflow = overflow;
  // Arbitrary choice to ensure determinism.
  impl.floating_point_format = IEEE754_HALF;
  return Concrete_Expression_Type(impl);
}

inline Concrete_Expression_Type
Concrete_Expression_Type
::floating_point(const Floating_Point_Format format) {
  Implementation impl;
  impl.bounded_integer = false;
  impl.floating_point_format = format;
  // Arbitrary choices to ensure determinism.
  impl.bounded_integer_type_width = BITS_128;
  impl.bounded_integer_type_representation =  SIGNED_2_COMPLEMENT;
  impl.bounded_integer_type_overflow = OVERFLOW_IMPOSSIBLE;
  return Concrete_Expression_Type(impl);
}

inline bool
Concrete_Expression_Type::is_bounded_integer() const {
  return impl.bounded_integer;
}

inline bool
Concrete_Expression_Type::is_floating_point() const {
  return !impl.bounded_integer;
}

inline Bounded_Integer_Type_Width
Concrete_Expression_Type::bounded_integer_type_width() const {
  const unsigned int u = impl.bounded_integer_type_width;
  return static_cast<Bounded_Integer_Type_Width>(u);
}

inline Bounded_Integer_Type_Representation
Concrete_Expression_Type::bounded_integer_type_representation() const {
  const unsigned int u = impl.bounded_integer_type_representation;
  return static_cast<Bounded_Integer_Type_Representation>(u);
}

inline Bounded_Integer_Type_Overflow
Concrete_Expression_Type::bounded_integer_type_overflow() const {
  const unsigned int u = impl.bounded_integer_type_overflow;
  return static_cast<Bounded_Integer_Type_Overflow>(u);
}

inline Floating_Point_Format
Concrete_Expression_Type::floating_point_format() const {
  const unsigned int u = impl.floating_point_format;
  return static_cast<Floating_Point_Format>(u);
}

template <typename Target>
template <template <typename T> class Derived>
inline bool
Concrete_Expression_Common<Target>::is() const {
  return static_cast<const Concrete_Expression<Target>*>(this)->kind() ==
         Derived<Target>::KIND;
}

template <typename Target>
template <template <typename T> class Derived>
inline Derived<Target>*
Concrete_Expression_Common<Target>::as() {
  PPL_ASSERT(is<Derived>());
  return static_cast<Derived<Target>*>(this);
}

template <typename Target>
template <template <typename T> class Derived>
inline const Derived<Target>*
Concrete_Expression_Common<Target>::as() const {
  PPL_ASSERT(is<Derived>());
  return static_cast<const Derived<Target>*>(this);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Concrete_Expression_defs.hh line 200. */

/* Automatically generated from PPL source file ../src/linearize.hh line 31. */
#include <map>

namespace Parma_Polyhedra_Library {

/*! \brief \relates Parma_Polyhedra_Library::Concrete_Expression
  Helper function used by <CODE>linearize</CODE> to linearize a
  sum of floating point expressions.

  Makes \p result become the linearization of \p *this in the given
  composite abstract store.

  \tparam Target
  A type template parameter specifying the instantiation of
  Concrete_Expression to be used.

  \tparam FP_Interval_Type
  A type template parameter for the intervals used in the abstract domain.
  The interval bounds should have a floating point type.

  \return
  <CODE>true</CODE> if the linearization succeeded,
  <CODE>false</CODE> otherwise.

  \param bop_expr
  The binary operator concrete expression to linearize.
  Its binary operator type must be <CODE>ADD</CODE>.

  \param oracle
  The FP_Oracle to be queried.

  \param lf_store
  The linear form abstract store.

  \param result
  The modified linear form.

  \par Linearization of sum floating-point expressions

  Let \f$i + \sum_{v \in \cV}i_{v}v \f$ and
  \f$i' + \sum_{v \in \cV}i'_{v}v \f$
  be two linear forms and \f$\aslf\f$ a sound abstract operator on linear
  forms such that:

  \f[
  \left(i + \sum_{v \in \cV}i_{v}v \right)
  \aslf
  \left(i' + \sum_{v \in \cV}i'_{v}v \right)
  =
  \left(i \asifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \asifp i'_{v} \right)v.
  \f]

  Given an expression \f$e_{1} \oplus e_{2}\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval linear form
  \f$\linexprenv{e_{1} \oplus e_{2}}{\rho^{\#}}{\rho^{\#}_l}\f$
  as follows:
  \f[
  \linexprenv{e_{1} \oplus e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  =
  \linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \aslf
  \linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \aslf
  \varepsilon_{\mathbf{f}}\left(\linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \right)
  \aslf
  \varepsilon_{\mathbf{f}}\left(\linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \right)
  \aslf
  mf_{\mathbf{f}}[-1, 1]
  \f]
  where \f$\varepsilon_{\mathbf{f}}(l)\f$ is the relative error
  associated to \f$l\f$ (see method <CODE>relative_error</CODE> of
  class Linear_Form) and \f$mf_{\mathbf{f}}\f$ is a rounding
  error computed by function <CODE>compute_absolute_error</CODE>.
*/
template <typename Target, typename FP_Interval_Type>
static bool
add_linearize(const Binary_Operator<Target>& bop_expr,
              const FP_Oracle<Target,FP_Interval_Type>& oracle,
              const std::map<dimension_type, Linear_Form<FP_Interval_Type> >&
              lf_store,
              Linear_Form<FP_Interval_Type>& result) {
  PPL_ASSERT(bop_expr.binary_operator() == Binary_Operator<Target>::ADD);

  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;

  if (!linearize(*(bop_expr.left_hand_side()), oracle, lf_store, result)) {
    return false;
  }

  Floating_Point_Format analyzed_format =
    bop_expr.type().floating_point_format();
  FP_Linear_Form rel_error;
  result.relative_error(analyzed_format, rel_error);
  result += rel_error;
  FP_Linear_Form linearized_second_operand;
  if (!linearize(*(bop_expr.right_hand_side()), oracle, lf_store,
                 linearized_second_operand)) {
    return false;
  }

  result += linearized_second_operand;
  linearized_second_operand.relative_error(analyzed_format, rel_error);
  result += rel_error;
  FP_Interval_Type absolute_error =
                   compute_absolute_error<FP_Interval_Type>(analyzed_format);
  result += absolute_error;
  return !result.overflows();
}

/*! \brief \relates Parma_Polyhedra_Library::Concrete_Expression
  Helper function used by <CODE>linearize</CODE> to linearize a
  difference of floating point expressions.

  Makes \p result become the linearization of \p *this in the given
  composite abstract store.

  \tparam Target
  A type template parameter specifying the instantiation of
  Concrete_Expression to be used.

  \tparam FP_Interval_Type
  A type template parameter for the intervals used in the abstract domain.
  The interval bounds should have a floating point type.

  \return
  <CODE>true</CODE> if the linearization succeeded,
  <CODE>false</CODE> otherwise.

  \param bop_expr
  The binary operator concrete expression to linearize.
  Its binary operator type must be <CODE>SUB</CODE>.

  \param oracle
  The FP_Oracle to be queried.

  \param lf_store
  The linear form abstract store.

  \param result
  The modified linear form.

  \par Linearization of difference floating-point expressions

  Let \f$i + \sum_{v \in \cV}i_{v}v \f$ and
  \f$i' + \sum_{v \in \cV}i'_{v}v \f$
  be two linear forms, \f$\aslf\f$ and \f$\adlf\f$ two sound abstract
  operators on linear form such that:
  \f[
  \left(i + \sum_{v \in \cV}i_{v}v\right)
  \aslf
  \left(i' + \sum_{v \in \cV}i'_{v}v\right)
  =
  \left(i \asifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \asifp i'_{v}\right)v,
  \f]
  \f[
  \left(i + \sum_{v \in \cV}i_{v}v\right)
  \adlf
  \left(i' + \sum_{v \in \cV}i'_{v}v\right)
  =
  \left(i \adifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \adifp i'_{v}\right)v.
  \f]
  Given an expression \f$e_{1} \ominus e_{2}\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$,  we construct the interval linear form
  \f$\linexprenv{e_{1} \ominus e_{2}}{\rho^{\#}}{\rho^{\#}_l}\f$
  on \f$\cV\f$ as follows:
  \f[
  \linexprenv{e_{1} \ominus e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  =
  \linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \adlf
  \linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \aslf
  \varepsilon_{\mathbf{f}}\left(\linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \right)
  \aslf
  \varepsilon_{\mathbf{f}}\left(\linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \right)
  \aslf
  mf_{\mathbf{f}}[-1, 1]
  \f]
  where \f$\varepsilon_{\mathbf{f}}(l)\f$ is the relative error
  associated to \f$l\f$ (see method <CODE>relative_error</CODE> of
  class Linear_Form) and \f$mf_{\mathbf{f}}\f$ is a rounding
  error computed by function <CODE>compute_absolute_error</CODE>.
*/
template <typename Target, typename FP_Interval_Type>
static bool
sub_linearize(const Binary_Operator<Target>& bop_expr,
              const FP_Oracle<Target,FP_Interval_Type>& oracle,
              const std::map<dimension_type, Linear_Form<FP_Interval_Type> >& lf_store,
              Linear_Form<FP_Interval_Type>& result) {
  PPL_ASSERT(bop_expr.binary_operator() == Binary_Operator<Target>::SUB);

  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;

  if (!linearize(*(bop_expr.left_hand_side()), oracle, lf_store, result)) {
    return false;
  }

  Floating_Point_Format analyzed_format =
    bop_expr.type().floating_point_format();
  FP_Linear_Form rel_error;
  result.relative_error(analyzed_format, rel_error);
  result += rel_error;
  FP_Linear_Form linearized_second_operand;
  if (!linearize(*(bop_expr.right_hand_side()), oracle, lf_store,
                 linearized_second_operand)) {
    return false;
  }

  result -= linearized_second_operand;
  linearized_second_operand.relative_error(analyzed_format, rel_error);
  result += rel_error;
  FP_Interval_Type absolute_error =
                   compute_absolute_error<FP_Interval_Type>(analyzed_format);
  result += absolute_error;
  return !result.overflows();
}

/*! \brief \relates Parma_Polyhedra_Library::Concrete_Expression
  Helper function used by <CODE>linearize</CODE> to linearize a
  product of floating point expressions.

  Makes \p result become the linearization of \p *this in the given
  composite abstract store.

  \tparam Target
  A type template parameter specifying the instantiation of
  Concrete_Expression to be used.

  \tparam FP_Interval_Type
  A type template parameter for the intervals used in the abstract domain.
  The interval bounds should have a floating point type.

  \return
  <CODE>true</CODE> if the linearization succeeded,
  <CODE>false</CODE> otherwise.

  \param bop_expr
  The binary operator concrete expression to linearize.
  Its binary operator type must be <CODE>MUL</CODE>.

  \param oracle
  The FP_Oracle to be queried.

  \param lf_store
  The linear form abstract store.

  \param result
  The modified linear form.

  \par Linearization of multiplication floating-point expressions

  Let \f$i + \sum_{v \in \cV}i_{v}v \f$ and
  \f$i' + \sum_{v \in \cV}i'_{v}v \f$
  be two linear forms, \f$\aslf\f$ and \f$\amlf\f$ two sound abstract
  operators on linear forms such that:
  \f[
  \left(i + \sum_{v \in \cV}i_{v}v\right)
  \aslf
  \left(i' + \sum_{v \in \cV}i'_{v}v\right)
  =
  \left(i \asifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \asifp i'_{v}\right)v,
  \f]
  \f[
  i
  \amlf
  \left(i' + \sum_{v \in \cV}i'_{v}v\right)
  =
  \left(i \amifp i'\right)
  + \sum_{v \in \cV}\left(i \amifp i'_{v}\right)v.
  \f]
  Given an expression \f$[a, b] \otimes e_{2}\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval linear form
  \f$\linexprenv{[a, b] \otimes e_{2}}{\rho^{\#}}{\rho^{\#}_l}\f$
  as follows:
  \f[
  \linexprenv{[a, b] \otimes e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  =
  \left([a, b]
  \amlf
  \linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}\right)
  \aslf
  \left([a, b]
  \amlf
  \varepsilon_{\mathbf{f}}\left(\linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \right)\right)
  \aslf
  mf_{\mathbf{f}}[-1, 1].
  \f].

  Given an expression \f$e_{1} \otimes [a, b]\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval linear form
  \f$\linexprenv{e_{1} \otimes [a, b]}{\rho^{\#}}{\rho^{\#}_l}\f$
  as follows:
  \f[
  \linexprenv{e_{1} \otimes [a, b]}{\rho^{\#}}{\rho^{\#}_l}
  =
  \linexprenv{[a, b] \otimes e_{1}}{\rho^{\#}}{\rho^{\#}_l}.
  \f]

  Given an expression \f$e_{1} \otimes e_{2}\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval linear form
  \f$\linexprenv{e_{1} \otimes e_{2}}{\rho^{\#}}{\rho^{\#}_l}\f$
  as follows:
  \f[
  \linexprenv{e_{1} \otimes e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  =
  \linexprenv{\iota\left(\linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \right)\rho^{\#}
  \otimes e_{2}}{\rho^{\#}}{\rho^{\#}_l},
  \f]
  where \f$\varepsilon_{\mathbf{f}}(l)\f$ is the relative error
  associated to \f$l\f$ (see method <CODE>relative_error</CODE> of
  class Linear_Form), \f$\iota(l)\rho^{\#}\f$ is the intervalization
  of \f$l\f$ (see method <CODE>intervalize</CODE> of class Linear_Form),
  and \f$mf_{\mathbf{f}}\f$ is a rounding error computed by function
  <CODE>compute_absolute_error</CODE>.

  Even though we intervalize the first operand in the above example, the
  actual implementation utilizes an heuristics for choosing which of the two
  operands must be intervalized in order to obtain the most precise result.
*/
template <typename Target, typename FP_Interval_Type>
static bool
mul_linearize(const Binary_Operator<Target>& bop_expr,
              const FP_Oracle<Target,FP_Interval_Type>& oracle,
              const std::map<dimension_type, Linear_Form<FP_Interval_Type> >& lf_store,
              Linear_Form<FP_Interval_Type>& result) {
  PPL_ASSERT(bop_expr.binary_operator() == Binary_Operator<Target>::MUL);

  typedef typename FP_Interval_Type::boundary_type analyzer_format;
  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;

  /*
    FIXME: We currently adopt the "Interval-Size Local" strategy in order to
    decide which of the two linear forms must be intervalized, as described
    in Section 6.2.4 ("Multiplication Strategies") of Antoine Mine's Ph.D.
    thesis "Weakly Relational Numerical Abstract Domains".
    In this Section are also described other multiplication strategies, such
    as All-Cases, Relative-Size Local, Simplification-Driven Global and
    Homogeneity Global.
  */

  // Here we choose which of the two linear forms must be intervalized.

  // true if we intervalize the first form, false if we intervalize the second.
  bool intervalize_first;
  FP_Linear_Form linearized_first_operand;
  if (!linearize(*(bop_expr.left_hand_side()), oracle, lf_store,
                 linearized_first_operand)) {
    return false;
  }
  FP_Interval_Type intervalized_first_operand;
  if (!linearized_first_operand.intervalize(oracle,
                                            intervalized_first_operand)) {
    return false;
  }
  FP_Linear_Form linearized_second_operand;
  if (!linearize(*(bop_expr.right_hand_side()), oracle, lf_store,
                 linearized_second_operand)) {
    return false;
  }
  FP_Interval_Type intervalized_second_operand;
  if (!linearized_second_operand.intervalize(oracle,
                                             intervalized_second_operand)) {
    return false;
  }

  // FIXME: we are not sure that what we do here is policy-proof.
  if (intervalized_first_operand.is_bounded()) {
    if (intervalized_second_operand.is_bounded()) {
      analyzer_format first_interval_size
        = intervalized_first_operand.upper()
        - intervalized_first_operand.lower();
      analyzer_format second_interval_size
        = intervalized_second_operand.upper()
        - intervalized_second_operand.lower();
      if (first_interval_size <= second_interval_size) {
        intervalize_first = true;
      }
      else {
        intervalize_first = false;
      }
    }
    else {
      intervalize_first = true;
    }
  }
  else {
    if (intervalized_second_operand.is_bounded()) {
      intervalize_first = false;
    }
    else {
      return false;
    }
  }

  // Here we do the actual computation.
  // For optimizing, we store the relative error directly into result.
  Floating_Point_Format analyzed_format =
    bop_expr.type().floating_point_format();
  if (intervalize_first) {
    linearized_second_operand.relative_error(analyzed_format, result);
    linearized_second_operand *= intervalized_first_operand;
    result *= intervalized_first_operand;
    result += linearized_second_operand;
  }
  else {
    linearized_first_operand.relative_error(analyzed_format, result);
    linearized_first_operand *= intervalized_second_operand;
    result *= intervalized_second_operand;
    result += linearized_first_operand;
  }

  FP_Interval_Type absolute_error =
                   compute_absolute_error<FP_Interval_Type>(analyzed_format);
  result += absolute_error;
  return !result.overflows();
}

/*! \brief \relates Parma_Polyhedra_Library::Concrete_Expression
  Helper function used by <CODE>linearize</CODE> to linearize a
  division of floating point expressions.

  Makes \p result become the linearization of \p *this in the given
  composite abstract store.

  \tparam Target
  A type template parameter specifying the instantiation of
  Concrete_Expression to be used.

  \tparam FP_Interval_Type
  A type template parameter for the intervals used in the abstract domain.
  The interval bounds should have a floating point type.

  \return
  <CODE>true</CODE> if the linearization succeeded,
  <CODE>false</CODE> otherwise.

  \param bop_expr
  The binary operator concrete expression to linearize.
  Its binary operator type must be <CODE>DIV</CODE>.

  \param oracle
  The FP_Oracle to be queried.

  \param lf_store
  The linear form abstract store.

  \param result
  The modified linear form.

  \par Linearization of division floating-point expressions

  Let \f$i + \sum_{v \in \cV}i_{v}v \f$ and
  \f$i' + \sum_{v \in \cV}i'_{v}v \f$
  be two linear forms, \f$\aslf\f$ and \f$\adivlf\f$ two sound abstract
  operator on linear forms such that:
  \f[
  \left(i + \sum_{v \in \cV}i_{v}v\right)
  \aslf
  \left(i' + \sum_{v \in \cV}i'_{v}v\right)
  =
  \left(i \asifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \asifp i'_{v}\right)v,
  \f]
  \f[
  \left(i + \sum_{v \in \cV}i_{v}v\right)
  \adivlf
  i'
  =
  \left(i \adivifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \adivifp i'\right)v.
  \f]
  Given an expression \f$e_{1} \oslash [a, b]\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$,
  we construct the interval linear form
  \f$
  \linexprenv{e_{1} \oslash [a, b]}{\rho^{\#}}{\rho^{\#}_l}
  \f$
  as follows:
  \f[
  \linexprenv{e_{1} \oslash [a, b]}{\rho^{\#}}{\rho^{\#}_l}
  =
  \left(\linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \adivlf
  [a, b]\right)
  \aslf
  \left(\varepsilon_{\mathbf{f}}\left(
  \linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \right)
  \adivlf
  [a, b]\right)
  \aslf
  mf_{\mathbf{f}}[-1, 1],
  \f]
  given an expression \f$e_{1} \oslash e_{2}\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval linear form
  \f$\linexprenv{e_{1} \oslash e_{2}}{\rho^{\#}}{\rho^{\#}_l}\f$
  as follows:
  \f[
  \linexprenv{e_{1} \oslash e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  =
  \linexprenv{e_{1} \oslash \iota\left(
  \linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \right)\rho^{\#}}{\rho^{\#}}{\rho^{\#}_l},
  \f]
  where \f$\varepsilon_{\mathbf{f}}(l)\f$ is the relative error
  associated to \f$l\f$ (see method <CODE>relative_error</CODE> of
  class Linear_Form), \f$\iota(l)\rho^{\#}\f$ is the intervalization
  of \f$l\f$ (see method <CODE>intervalize</CODE> of class Linear_Form),
  and \f$mf_{\mathbf{f}}\f$ is a rounding error computed by function
  <CODE>compute_absolute_error</CODE>.
*/
template <typename Target, typename FP_Interval_Type>
static bool
div_linearize(const Binary_Operator<Target>& bop_expr,
              const FP_Oracle<Target,FP_Interval_Type>& oracle,
              const std::map<dimension_type, Linear_Form<FP_Interval_Type> >& lf_store,
              Linear_Form<FP_Interval_Type>& result) {
  PPL_ASSERT(bop_expr.binary_operator() == Binary_Operator<Target>::DIV);

  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;

  FP_Linear_Form linearized_second_operand;
  if (!linearize(*(bop_expr.right_hand_side()), oracle, lf_store,
                 linearized_second_operand)) {
    return false;
  }
  FP_Interval_Type intervalized_second_operand;
  if (!linearized_second_operand.intervalize(oracle,
                                             intervalized_second_operand)) {
    return false;
  }
  // Check if we may divide by zero.
  if ((intervalized_second_operand.lower_is_boundary_infinity()
       || intervalized_second_operand.lower() <= 0) &&
      (intervalized_second_operand.upper_is_boundary_infinity()
       || intervalized_second_operand.upper() >= 0)) {
    return false;
  }
  if (!linearize(*(bop_expr.left_hand_side()), oracle, lf_store, result)) {
    return false;
  }

  Floating_Point_Format analyzed_format =
    bop_expr.type().floating_point_format();
  FP_Linear_Form rel_error;
  result.relative_error(analyzed_format, rel_error);
  result /= intervalized_second_operand;
  rel_error /= intervalized_second_operand;
  result += rel_error;
  FP_Interval_Type absolute_error =
                   compute_absolute_error<FP_Interval_Type>(analyzed_format);
  result += absolute_error;
  return !result.overflows();
}

/*! \brief \relates Parma_Polyhedra_Library::Concrete_Expression
  Helper function used by <CODE>linearize</CODE> to linearize a cast
  floating point expression.

  Makes \p result become the linearization of \p *this in the given
  composite abstract store.

  \tparam Target
  A type template parameter specifying the instantiation of
  Concrete_Expression to be used.

  \tparam FP_Interval_Type
  A type template parameter for the intervals used in the abstract domain.
  The interval bounds should have a floating point type.

  \return
  <CODE>true</CODE> if the linearization succeeded,
  <CODE>false</CODE> otherwise.

  \param cast_expr
  The cast operator concrete expression to linearize.

  \param oracle
  The FP_Oracle to be queried.

  \param lf_store
  The linear form abstract store.

  \param result
  The modified linear form.
*/
template <typename Target, typename FP_Interval_Type>
static bool
cast_linearize(const Cast_Operator<Target>& cast_expr,
               const FP_Oracle<Target,FP_Interval_Type>& oracle,
               const std::map<dimension_type, Linear_Form<FP_Interval_Type> >& lf_store,
               Linear_Form<FP_Interval_Type>& result) {
  typedef typename FP_Interval_Type::boundary_type analyzer_format;
  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;

  Floating_Point_Format analyzed_format =
    cast_expr.type().floating_point_format();
  const Concrete_Expression<Target>* cast_arg = cast_expr.argument();
  if (cast_arg->type().is_floating_point()) {
    if (!linearize(*cast_arg, oracle, lf_store, result)) {
      return false;
    }
    if (!is_less_precise_than(analyzed_format,
                              cast_arg->type().floating_point_format())
        || result == FP_Linear_Form(FP_Interval_Type(0))
        || result == FP_Linear_Form(FP_Interval_Type(1))) {
      /*
        FIXME: find a general way to check if the casted constant
        is exactly representable in the less precise format.
      */
      /*
        We are casting to a more precise format or casting
        a definitely safe value: do not add errors.
      */
      return true;
    }
  }
  else {
    FP_Interval_Type expr_value;
    if (!oracle.get_integer_expr_value(*cast_arg, expr_value))
      return false;
    result = FP_Linear_Form(expr_value);
    if (is_less_precise_than(Float<analyzer_format>::Binary::floating_point_format, analyzed_format)
        || result == FP_Linear_Form(FP_Interval_Type(0))
        || result == FP_Linear_Form(FP_Interval_Type(1))) {
      /*
        FIXME: find a general way to check if the casted constant
        is exactly representable in the less precise format.
      */
      /*
        We are casting to a more precise format or casting
        a definitely safe value: do not add errors.
      */
      return true;
    }
  }

  FP_Linear_Form rel_error;
  result.relative_error(analyzed_format, rel_error);
  result += rel_error;
  FP_Interval_Type absolute_error =
                   compute_absolute_error<FP_Interval_Type>(analyzed_format);
  result += absolute_error;
  return !result.overflows();
}

//! Linearizes a floating point expression.
/*! \relates Parma_Polyhedra_Library::Concrete_Expression
  Makes \p result become a linear form that correctly approximates the
  value of \p expr in the given composite abstract store.

  \tparam Target
  A type template parameter specifying the instantiation of
  Concrete_Expression to be used.

  \tparam FP_Interval_Type
  A type template parameter for the intervals used in the abstract domain.
  The interval bounds should have a floating point type.

  \return
  <CODE>true</CODE> if the linearization succeeded,
  <CODE>false</CODE> otherwise.

  \param expr
  The concrete expression to linearize.

  \param oracle
  The FP_Oracle to be queried.

  \param lf_store
  The linear form abstract store.

  \param result
  Becomes the linearized expression.

  Formally, if \p expr represents the expression \f$e\f$ and
  \p lf_store represents the linear form abstract store \f$\rho^{\#}_l\f$,
  then \p result will become \f$\linexprenv{e}{\rho^{\#}}{\rho^{\#}_l}\f$
  if the linearization succeeds.
*/
template <typename Target, typename FP_Interval_Type>
bool
linearize(const Concrete_Expression<Target>& expr,
          const FP_Oracle<Target,FP_Interval_Type>& oracle,
          const std::map<dimension_type, Linear_Form<FP_Interval_Type> >& lf_store,
          Linear_Form<FP_Interval_Type>& result) {
  typedef typename FP_Interval_Type::boundary_type analyzer_format;
  typedef Linear_Form<FP_Interval_Type> FP_Linear_Form;
  typedef std::map<dimension_type, FP_Linear_Form>
    FP_Linear_Form_Abstract_Store;

  PPL_ASSERT(expr.type().is_floating_point());
  // Check that analyzer_format is a floating point type.
  PPL_COMPILE_TIME_CHECK(!std::numeric_limits<analyzer_format>::is_exact,
      "linearize<Target, FP_Interval_Type>:"
      " FP_Interval_Type is not the type of an interval with floating point boundaries.");

  switch(expr.kind()) {
  case Integer_Constant<Target>::KIND:
    PPL_UNREACHABLE;
    break;
  case Floating_Point_Constant<Target>::KIND:
  {
    const Floating_Point_Constant<Target>* fpc_expr =
      expr.template as<Floating_Point_Constant>();
    FP_Interval_Type constant_value;
    if (!oracle.get_fp_constant_value(*fpc_expr, constant_value)) {
      return false;
    }
    result = FP_Linear_Form(constant_value);
    return true;
  }
  case Unary_Operator<Target>::KIND:
  {
    const Unary_Operator<Target>* uop_expr =
      expr.template as<Unary_Operator>();
    switch (uop_expr->unary_operator()) {
    case Unary_Operator<Target>::UPLUS:
      return linearize(*(uop_expr->argument()), oracle, lf_store, result);
    case Unary_Operator<Target>::UMINUS:
      if (!linearize(*(uop_expr->argument()), oracle, lf_store, result)) {
        return false;
      }

      result.negate();
      return true;
    case Unary_Operator<Target>::BNOT:
      throw std::runtime_error("PPL internal error: unimplemented");
      break;
    default:
      PPL_UNREACHABLE;
      break;
    }
    break;
  }
  case Binary_Operator<Target>::KIND:
  {
    const Binary_Operator<Target>* bop_expr =
      expr.template as<Binary_Operator>();
    switch (bop_expr->binary_operator()) {
    case Binary_Operator<Target>::ADD:
      return add_linearize(*bop_expr, oracle, lf_store, result);
    case Binary_Operator<Target>::SUB:
      return sub_linearize(*bop_expr, oracle, lf_store, result);
    case Binary_Operator<Target>::MUL:
      return mul_linearize(*bop_expr, oracle, lf_store, result);
    case Binary_Operator<Target>::DIV:
      return div_linearize(*bop_expr, oracle, lf_store, result);
    case Binary_Operator<Target>::REM:
    case Binary_Operator<Target>::BAND:
    case Binary_Operator<Target>::BOR:
    case Binary_Operator<Target>::BXOR:
    case Binary_Operator<Target>::LSHIFT:
    case Binary_Operator<Target>::RSHIFT:
      // FIXME: can we do better?
      return false;
    default:
      PPL_UNREACHABLE;
      return false;
    }
    break;
  }
  case Approximable_Reference<Target>::KIND:
  {
    const Approximable_Reference<Target>* ref_expr =
      expr.template as<Approximable_Reference>();
    std::set<dimension_type> associated_dimensions;
    if (!oracle.get_associated_dimensions(*ref_expr, associated_dimensions)
        || associated_dimensions.empty()) {
      /*
        We were unable to find any associated space dimension:
        linearization fails.
      */
      return false;
    }

    if (associated_dimensions.size() == 1) {
      /* If a linear form associated to the only referenced
         space dimension exists in lf_store, return that form.
         Otherwise, return the simplest linear form. */
      dimension_type variable_index = *associated_dimensions.begin();
      PPL_ASSERT(variable_index != not_a_dimension());

      typename FP_Linear_Form_Abstract_Store::const_iterator
        variable_value = lf_store.find(variable_index);
      if (variable_value == lf_store.end()) {
        result = FP_Linear_Form(Variable(variable_index));
        return true;
      }

      result = FP_Linear_Form(variable_value->second);
      /* FIXME: do we really need to contemplate the possibility
         that an unbounded linear form was saved into lf_store? */
      return !result.overflows();
    }

    /*
      Here associated_dimensions.size() > 1. Try to return the LUB
      of all intervals associated to each space dimension.
    */
    PPL_ASSERT(associated_dimensions.size() > 1);
    std::set<dimension_type>::const_iterator i
      = associated_dimensions.begin();
    std::set<dimension_type>::const_iterator i_end
      = associated_dimensions.end();
    FP_Interval_Type lub(EMPTY);
    for (; i != i_end; ++i) {
      FP_Interval_Type curr_int;
      PPL_ASSERT(*i != not_a_dimension());
      if (!oracle.get_interval(*i, curr_int)) {
        return false;
      }

      lub.join_assign(curr_int);
    }

    result = FP_Linear_Form(lub);
    return !result.overflows();
  }
  case Cast_Operator<Target>::KIND:
  {
    const Cast_Operator<Target>* cast_expr =
      expr.template as<Cast_Operator>();
    return cast_linearize(*cast_expr, oracle, lf_store, result);
  }
  default:
    PPL_UNREACHABLE;
    break;
  }

  PPL_UNREACHABLE;
  return false;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/PIP_Tree_defs.hh line 1. */
/* PIP_Tree_Node class declaration.
*/


/* Automatically generated from PPL source file ../src/PIP_Tree_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class PIP_Tree_Node;
class PIP_Solution_Node;
class PIP_Decision_Node;

typedef const PIP_Tree_Node* PIP_Tree;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/PIP_Problem_defs.hh line 1. */
/* PIP_Problem class declaration.
*/


/* Automatically generated from PPL source file ../src/PIP_Problem_defs.hh line 36. */
#include <vector>
#include <deque>
#include <iosfwd>

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::PIP_Problem */
std::ostream&
operator<<(std::ostream& s, const PIP_Problem& pip);

} // namespace IO_Operators

//! Swaps \p x with \p y.
/*! \relates PIP_Problem */
void swap(PIP_Problem& x, PIP_Problem& y);

} // namespace Parma_Polyhedra_Library

//! A Parametric Integer (linear) Programming problem.
/*! \ingroup PPL_CXX_interface
  An object of this class encodes a parametric integer (linear)
  programming problem. The PIP problem is specified by providing:
   - the dimension of the vector space;
   - the subset of those dimensions of the vector space that are
     interpreted as integer parameters (the other space dimensions
     are interpreted as non-parameter integer variables);
   - a finite set of linear equality and (strict or non-strict)
     inequality constraints involving variables and/or parameters;
     these constraints are used to define:
       - the <EM>feasible region</EM>, if they involve one or more
         problem variable (and maybe some parameters);
       - the <EM>initial context</EM>, if they only involve the
         parameters;
   - optionally, the so-called <EM>big parameter</EM>,
     i.e., a problem parameter to be considered arbitrarily big.

  Note that all problem variables and problem parameters are assumed
  to take non-negative integer values, so that there is no need
  to specify non-negativity constraints.

  The class provides support for the (incremental) solution of the
  PIP problem based on variations of the revised simplex method and
  on Gomory cut generation techniques.

  The solution for a PIP problem is the lexicographic minimum of the
  integer points of the feasible region, expressed in terms of the
  parameters. As the problem to be solved only involves non-negative
  variables and parameters, the problem will always be either unfeasible
  or optimizable.

  As the feasibility and the solution value of a PIP problem depend on the
  values of the parameters, the solution is a binary decision tree,
  dividing the context parameter set into subsets.
  The tree nodes are of two kinds:
   - \e Decision nodes.
     These are internal tree nodes encoding one or more linear tests
     on the parameters; if all the tests are satisfied, then the solution
     is the node's \e true child; otherwise, the solution is the node's
     \e false child;
   - \e Solution nodes.
     These are leaf nodes in the tree, encoding the solution of the problem
     in the current context subset, where each variable is defined in terms
     of a linear expression of the parameters.
     Solution nodes also optionally embed a set of parameter constraints:
     if all these constraints are satisfied, the solution is described by
     the node, otherwise the problem has no solution.

  It may happen that a decision node has no \e false child. This means
  that there is no solution if at least one of the corresponding
  constraints is not satisfied. Decision nodes having two or more linear
  tests on the parameters cannot have a \e false child. Decision nodes
  always have a \e true child.

  Both kinds of tree nodes may also contain the definition of extra
  parameters which are artificially introduced by the solver to enforce
  an integral solution. Such artificial parameters are defined by
  the integer division of a linear expression on the parameters
  by an integer coefficient.

  By exploiting the incremental nature of the solver, it is possible
  to reuse part of the computational work already done when solving
  variants of a given PIP_Problem: currently, incremental resolution
  supports the addition of space dimensions, the addition of parameters
  and the addition of constraints.

  \par Example problem
  An example PIP problem can be defined the following:
  \code
  3*j >= -2*i+8
  j <= 4*i - 4
  i <= n
  j <= m
  \endcode
  where \c i and \c j are the problem variables
  and \c n and \c m are the problem parameters.
  This problem can be optimized; the resulting solution tree may be
  represented as follows:
  \verbatim
  if 7*n >= 10 then
    if 7*m >= 12 then
      {i = 2 ; j = 2}
    else
      Parameter P = (m) div 2
      if 2*n + 3*m >= 8 then
        {i = -m - P + 4 ; j = m}
      else
        _|_
  else
    _|_
  \endverbatim
  The solution tree starts with a decision node depending on the
  context constraint <code>7*n >= 10</code>.
  If this constraint is satisfied by the values assigned to the
  problem parameters, then the (textually first) \c then branch is taken,
  reaching the \e true child of the root node (which in this case
  is another decision node); otherwise, the (textually last) \c else
  branch is taken, for which there is no corresponding \e false child.
  \par
  The \f$\perp\f$ notation, also called \e bottom, denotes the
  lexicographic minimum of an empty set of solutions,
  here meaning the corresponding subproblem is unfeasible.
  \par
  Notice that a tree node may introduce new (non-problem) parameters,
  as is the case for parameter \c P in the (textually first) \c else
  branch above. These \e artificial parameters are only meaningful
  inside the subtree where they are defined and are used to define
  the parametric values of the problem variables in solution nodes
  (e.g., the <CODE>{i,j}</CODE> vector in the textually third \c then branch).

  \par Context restriction
  The above solution is correct in an unrestricted initial context,
  meaning all possible values are allowed for the parameters. If we
  restrict the context with the following parameter inequalities:
  \code
  m >= n
  n >= 5
  \endcode
  then the resulting optimizing tree will be a simple solution node:
  \verbatim
  {i = 2 ; j = 2}
  \endverbatim

  \par Creating the PIP_Problem object
  The PIP_Problem object corresponding to the above example can be
  created as follows:
  \code
  Variable i(0);
  Variable j(1);
  Variable n(2);
  Variable m(3);
  Variables_Set params(n, m);
  Constraint_System cs;
  cs.insert(3*j >= -2*i+8);
  cs.insert(j <= 4*i - 4);
  cs.insert(j <= m);
  cs.insert(i <= n);
  PIP_Problem pip(cs.space_dimension(), cs.begin(), cs.end(), params);
  \endcode
  If you want to restrict the initial context, simply add the parameter
  constraints the same way as for normal constraints.
  \code
  cs.insert(m >= n);
  cs.insert(n >= 5);
  \endcode

  \par Solving the problem
  Once the PIP_Problem object has been created, you can start the
  resolution of the problem by calling the solve() method:
  \code
  PIP_Problem_Status status = pip.solve();
  \endcode
  where the returned \c status indicates if the problem has been optimized
  or if it is unfeasible for any possible configuration of the parameter
  values. The resolution process is also started if an attempt is made
  to get its solution, as follows:
  \code
  const PIP_Tree_Node* node = pip.solution();
  \endcode
  In this case, an unfeasible problem will result in an empty solution
  tree, i.e., assigning a null pointer to \c node.

  \par Printing the solution tree
  A previously computed solution tree may be printed as follows:
  \code
  pip.print_solution(std::cout);
  \endcode
  This will produce the following output (note: variables and parameters
  are printed according to the default output function; see
  <code>Variable::set_output_function</code>):
  \verbatim
  if 7*C >= 10 then
    if 7*D >= 12 then
      {2 ; 2}
    else
      Parameter E = (D) div 2
      if 2*C + 3*D >= 8 then
        {-D - E + 4 ; D}
      else
        _|_
  else
    _|_
  \endverbatim

  \par Spanning the solution tree
  A parameter assignment for a PIP problem binds each of the problem
  parameters to a non-negative integer value. After fixing a parameter
  assignment, the ``spanning'' of the PIP problem solution tree refers
  to the process whereby the solution tree is navigated, starting from
  the root node: the value of artificial parameters is computed according
  to the parameter assignment and the node's constraints are evaluated,
  thereby descending in either the true or the false subtree of decision
  nodes and eventually reaching a solution node or a bottom node.
  If a solution node is found, each of the problem variables is provided
  with a parametric expression, which can be evaluated to a fixed value
  using the given parameter assignment and the computed values for
  artificial parameters.
  \par
  The coding of the spanning process can be done as follows.
  First, the root of the PIP solution tree is retrieved:
  \code
  const PIP_Tree_Node* node = pip.solution();
  \endcode
  If \c node represents an unfeasible solution (i.e., \f$\perp\f$),
  its value will be \c 0. For a non-null tree node, the virtual methods
  \c PIP_Tree_Node::as_decision() and \c PIP_Tree_Node::as_solution()
  can be used to check whether the node is a decision or a solution node:
  \code
  const PIP_Solution_Node* sol = node->as_solution();
  if (sol != 0) {
    // The node is a solution node
    ...
  }
  else {
    // The node is a decision node
    const PIP_Decision_Node* dec = node->as_decision();
    ...
  }
  \endcode
  \par
  The true (resp., false) child node of a Decision Node may be accessed by
  using method \c PIP_Decision_Node::child_node(bool), passing \c true
  (resp., \c false) as the input argument.

  \par Artificial parameters
  A PIP_Tree_Node::Artificial_Parameter object represents the result
  of the integer division of a Linear_Expression (on the other
  parameters, including the previously-defined artificials)
  by an integer denominator (a Coefficient object).
  The dimensions of the artificial parameters (if any) in a tree node
  have consecutive indices starting from <code>dim+1</code>, where the value
  of \c dim is computed as follows:
   - for the tree root node, \c dim is the space dimension of the PIP_Problem;
   - for any other node of the tree, it is recursively obtained by adding
     the value of \c dim computed for the parent node to the number of
     artificial parameters defined in the parent node.
  \par
  Since the numbering of dimensions for artificial parameters follows
  the rule above, the addition of new problem variables and/or new problem
  parameters to an already solved PIP_Problem object (as done when
  incrementally solving a problem) will result in the systematic
  renumbering of all the existing artificial parameters.

  \par Node constraints
  All kind of tree nodes can contain context constraints.
  Decision nodes always contain at least one of them.
  The node's local constraint system can be obtained using method
  PIP_Tree_Node::constraints.
  These constraints only involve parameters, including both the problem
  parameters and the artificial parameters that have been defined
  in nodes occurring on the path from the root node to the current node.
  The meaning of these constraints is as follows:
   - On a decision node, if all tests in the constraints are true, then the
     solution is the \e true child; otherwise it is the \e false child.
   - On a solution node, if the (possibly empty) system of constraints
     evaluates to true for a given parameter assignment, then the solution
     is described by the node; otherwise the solution is \f$\perp\f$
     (i.e., the problem is unfeasible for that parameter assignment).

  \par Getting the optimal values for the variables
  After spanning the solution tree using the given parameter assignment,
  if a solution node has been reached, then it is possible to retrieve
  the parametric expression for each of the problem variables using
  method PIP_Solution_Node::parametric_values. The retrieved expression
  will be defined in terms of all the parameters (problem parameters
  and artificial parameters defined along the path).

  \par Solving maximization problems
  You can solve a lexicographic maximization problem by reformulating its
  constraints using variable substitution. Proceed the following steps:
   - Create a big parameter (see PIP_Problem::set_big_parameter_dimension),
     which we will call \f$M\f$.
   - Reformulate each of the maximization problem constraints by
     substituting each \f$x_i\f$ variable with an expression of the form
     \f$M-x'_i\f$, where the \f$x'_i\f$ variables are positive variables to
     be minimized.
   - Solve the lexicographic minimum for the \f$x'\f$ variable vector.
   - In the solution expressions, the values of the \f$x'\f$ variables will
     be expressed in the form: \f$x'_i = M-x_i\f$. To get back the value of
     the expression of each \f$x_i\f$ variable, just apply the
     formula: \f$x_i = M-x'_i\f$.
  \par
  Note that if the resulting expression of one of the \f$x'_i\f$ variables
  is not in the \f$x'_i = M-x_i\f$ form, this means that the
  sign-unrestricted problem is unbounded.
  \par
  You can choose to maximize only a subset of the variables while minimizing
  the other variables. In that case, just apply the variable substitution
  method on the variables you want to be maximized. The variable
  optimization priority will still be in lexicographic order.

  \par
  \b Example: consider you want to find the lexicographic maximum of the
  \f$(x,y)\f$ vector, under the constraints:
    \f[\left\{\begin{array}{l}
      y \geq 2x - 4\\
      y \leq -x + p
    \end{array}\right.\f]
  \par
  where \f$p\f$ is a parameter.
  \par
  After variable substitution, the constraints become:
    \f[\left\{\begin{array}{l}
      M - y \geq 2M - 2x - 4\\
      M - y \leq -M + x + p
    \end{array}\right.\f]
  \par
  The code for creating the corresponding problem object is the following:
  \code
  Variable x(0);
  Variable y(1);
  Variable p(2);
  Variable M(3);
  Variables_Set params(p, M);
  Constraint_System cs;
  cs.insert(M - y >= 2*M - 2*x - 4);
  cs.insert(M - y <= -M + x + p);
  PIP_Problem pip(cs.space_dimension(), cs.begin(), cs.end(), params);
  pip.set_big_parameter_dimension(3);     // M is the big parameter
  \endcode
  Solving the problem provides the following solution:
  \verbatim
  Parameter E = (C + 1) div 3
  {D - E - 1 ; -C + D + E + 1}
  \endverbatim
  Under the notations above, the solution is:
  \f[ \left\{\begin{array}{l}
    x' = M - \left\lfloor\frac{p+1}{3}\right\rfloor - 1 \\
    y' = M - p + \left\lfloor\frac{p+1}{3}\right\rfloor + 1
  \end{array}\right.
  \f]
  \par
  Performing substitution again provides us with the values of the original
  variables:
  \f[ \left\{\begin{array}{l}
    x = \left\lfloor\frac{p+1}{3}\right\rfloor + 1 \\
    y = p - \left\lfloor\frac{p+1}{3}\right\rfloor - 1
  \end{array}\right.
  \f]

  \par Allowing variables to be arbitrarily signed
  You can deal with arbitrarily signed variables by reformulating the
  constraints using variable substitution. Proceed the following steps:
   - Create a big parameter (see PIP_Problem::set_big_parameter_dimension),
     which we will call \f$M\f$.
   - Reformulate each of the maximization problem constraints by
     substituting each \f$x_i\f$ variable with an expression of the form
     \f$x'_i-M\f$, where the \f$x'_i\f$ variables are positive.
   - Solve the lexicographic minimum for the \f$x'\f$ variable vector.
   - The solution expression can be read in the form:
   - In the solution expressions, the values of the \f$x'\f$ variables will
     be expressed in the form: \f$x'_i = x_i+M\f$. To get back the value of
     the expression of each signed \f$x_i\f$ variable, just apply the
     formula: \f$x_i = x'_i-M\f$.
  \par
  Note that if the resulting expression of one of the \f$x'_i\f$ variables
  is not in the \f$x'_i = x_i+M\f$ form, this means that the
  sign-unrestricted problem is unbounded.
  \par
  You can choose to define only a subset of the variables to be
  sign-unrestricted. In that case, just apply the variable substitution
  method on the variables you want to be sign-unrestricted.

  \par
  \b Example: consider you want to find the lexicographic minimum of the
  \f$(x,y)\f$ vector, where the \f$x\f$ and \f$y\f$ variables are
  sign-unrestricted, under the constraints:
    \f[\left\{\begin{array}{l}
      y \geq -2x - 4\\
      2y \leq x + 2p
    \end{array}\right.\f]
  \par
  where \f$p\f$ is a parameter.
  \par
  After variable substitution, the constraints become:
    \f[\left\{\begin{array}{l}
      y' - M \geq -2x' + 2M - 4\\
      2y' - 2M \leq x' - M + 2p
    \end{array}\right.\f]
  \par
  The code for creating the corresponding problem object is the following:
  \code
  Variable x(0);
  Variable y(1);
  Variable p(2);
  Variable M(3);
  Variables_Set params(p, M);
  Constraint_System cs;
  cs.insert(y - M >= -2*x + 2*M - 4);
  cs.insert(2*y - 2*M <= x - M + 2*p);
  PIP_Problem pip(cs.space_dimension(), cs.begin(), cs.end(), params);
  pip.set_big_parameter_dimension(3);     // M is the big parameter
  \endcode
  \par
  Solving the problem provides the following solution:
  \verbatim
  Parameter E = (2*C + 3) div 5
  {D - E - 1 ; D + 2*E - 2}
  \endverbatim
  Under the notations above, the solution is:
  \f[ \left\{\begin{array}{l}
    x' = M - \left\lfloor\frac{2p+3}{5}\right\rfloor - 1 \\
    y' = M + 2\left\lfloor\frac{2p+3}{5}\right\rfloor - 2
  \end{array}\right.
  \f]
  \par
  Performing substitution again provides us with the values of the original
  variables:
  \f[ \left\{\begin{array}{l}
    x = -\left\lfloor\frac{2p+3}{5}\right\rfloor - 1 \\
    y = 2\left\lfloor\frac{2p+3}{5}\right\rfloor - 2
  \end{array}\right.
  \f]

  \par Allowing parameters to be arbitrarily signed
  You can consider a parameter \f$p\f$ arbitrarily signed by replacing
  \f$p\f$ with \f$p^+-p^-\f$, where both \f$p^+\f$ and \f$p^-\f$ are
  positive parameters. To represent a set of arbitrarily signed parameters,
  replace each parameter \f$p_i\f$ with \f$p^+_i-p^-\f$, where \f$-p^-\f$ is
  the minimum negative value of all parameters.

  \par Minimizing a linear cost function
  Lexicographic solving can be used to find the parametric minimum of a
  linear cost function.
  \par
  Suppose the variables are named \f$x_1, x_2, \dots, x_n\f$, and the
  parameters \f$p_1, p_2, \dots, p_m\f$. You can minimize a linear cost
  function \f$f(x_2, \dots, x_n, p_1, \dots, p_m)\f$ by simply adding the
  constraint \f$x_1 \geq f(x_2, \dots, x_n, p_1, \dots, p_m)\f$ to the
  constraint system. As lexicographic minimization ensures \f$x_1\f$ is
  minimized in priority, and because \f$x_1\f$ is forced by a constraint to
  be superior or equal to the cost function, optimal solutions of the
  problem necessarily ensure that the solution value of \f$x_1\f$ is the
  optimal value of the cost function.
*/
class Parma_Polyhedra_Library::PIP_Problem {
public:
  //! Builds a trivial PIP problem.
  /*!
    A trivial PIP problem requires to compute the lexicographic minimum
    on a vector space under no constraints and with no parameters:
    due to the implicit non-negativity constraints, the origin of the
    vector space is an optimal solution.

    \param dim
    The dimension of the vector space enclosing \p *this
    (optional argument with default value \f$0\f$).

    \exception std::length_error
    Thrown if \p dim exceeds <CODE>max_space_dimension()</CODE>.
  */
  explicit PIP_Problem(dimension_type dim = 0);

  /*! \brief
    Builds a PIP problem having space dimension \p dim
    from the sequence of constraints in the range
    \f$[\mathrm{first}, \mathrm{last})\f$;
    those dimensions whose indices occur in \p p_vars are
    interpreted as parameters.

    \param dim
    The dimension of the vector space (variables and parameters) enclosing
    \p *this.

    \param first
    An input iterator to the start of the sequence of constraints.

    \param last
    A past-the-end input iterator to the sequence of constraints.

    \param p_vars
    The set of variables' indexes that are interpreted as parameters.

    \exception std::length_error
    Thrown if \p dim exceeds <CODE>max_space_dimension()</CODE>.

    \exception std::invalid_argument
    Thrown if the space dimension of a constraint in the sequence
    (resp., the parameter variables) is strictly greater than \p dim.
  */
  template <typename In>
  PIP_Problem(dimension_type dim, In first, In last,
              const Variables_Set& p_vars);

  //! Ordinary copy-constructor.
  PIP_Problem(const PIP_Problem& y);

  //! Destructor.
  ~PIP_Problem();

  //! Assignment operator.
  PIP_Problem& operator=(const PIP_Problem& y);

  //! Returns the maximum space dimension a PIP_Problem can handle.
  static dimension_type max_space_dimension();

  //! Returns the space dimension of the PIP problem.
  dimension_type space_dimension() const;

  /*! \brief
    Returns a set containing all the variables' indexes representing
    the parameters of the PIP problem.
  */
  const Variables_Set& parameter_space_dimensions() const;

private:
  //! A type alias for a sequence of constraints.
  typedef std::vector<Constraint> Constraint_Sequence;

public:
  /*! \brief
    A type alias for the read-only iterator on the constraints
    defining the feasible region.
  */
  typedef Constraint_Sequence::const_iterator const_iterator;

  /*! \brief
    Returns a read-only iterator to the first constraint defining
    the feasible region.
  */
  const_iterator constraints_begin() const;

  /*! \brief
    Returns a past-the-end read-only iterator to the sequence of
    constraints defining the feasible region.
  */
  const_iterator constraints_end() const;

  //! Resets \p *this to be equal to the trivial PIP problem.
  /*!
    The space dimension is reset to \f$0\f$.
  */
  void clear();

  /*! \brief
    Adds <CODE>m_vars + m_params</CODE> new space dimensions
    and embeds the old PIP problem in the new vector space.

    \param m_vars
    The number of space dimensions to add that are interpreted as
    PIP problem variables (i.e., non parameters). These are added
    \e before adding the \p m_params parameters.

    \param m_params
    The number of space dimensions to add that are interpreted as
    PIP problem parameters. These are added \e after having added the
    \p m_vars problem variables.

    \exception std::length_error
    Thrown if adding <CODE>m_vars + m_params</CODE> new space
    dimensions would cause the vector space to exceed dimension
    <CODE>max_space_dimension()</CODE>.

    The new space dimensions will be those having the highest indexes
    in the new PIP problem; they are initially unconstrained.
  */
  void add_space_dimensions_and_embed(dimension_type m_vars,
                                      dimension_type m_params);

  /*! \brief
    Sets the space dimensions whose indexes which are in set \p p_vars
    to be parameter space dimensions.

    \exception std::invalid_argument
    Thrown if some index in \p p_vars does not correspond to
    a space dimension in \p *this.
  */
  void add_to_parameter_space_dimensions(const Variables_Set& p_vars);

  /*! \brief
    Adds a copy of constraint \p c to the PIP problem.

    \exception std::invalid_argument
    Thrown if the space dimension of \p c is strictly greater than
    the space dimension of \p *this.
  */
  void add_constraint(const Constraint& c);

  /*! \brief
    Adds a copy of the constraints in \p cs to the PIP problem.

    \exception std::invalid_argument
    Thrown if the space dimension of constraint system \p cs is strictly
    greater than the space dimension of \p *this.
  */
  void add_constraints(const Constraint_System& cs);

  //! Checks satisfiability of \p *this.
  /*!
    \return
    \c true if and only if the PIP problem is satisfiable.
  */
  bool is_satisfiable() const;

  //! Optimizes the PIP problem.
  /*!
    \return
    A PIP_Problem_Status flag indicating the outcome of the optimization
    attempt (unfeasible or optimized problem).
  */
  PIP_Problem_Status solve() const;

  //! Returns a feasible solution for \p *this, if it exists.
  /*!
    A null pointer is returned for an unfeasible PIP problem.
  */
  PIP_Tree solution() const;

  //! Returns an optimizing solution for \p *this, if it exists.
  /*!
    A null pointer is returned for an unfeasible PIP problem.
  */
  PIP_Tree optimizing_solution() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  //! Prints on \p s the solution computed for \p *this.
  /*!
    \param s
    The output stream.

    \param indent
    An indentation parameter (default value 0).

    \exception std::logic_error
    Thrown if trying to print the solution when the PIP problem
    still has to be solved.
  */
  void print_solution(std::ostream& s, int indent = 0) const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Swaps \p *this with \p y.
  void m_swap(PIP_Problem& y);

  //! Possible names for PIP_Problem control parameters.
  enum Control_Parameter_Name {
    //! Cutting strategy
    CUTTING_STRATEGY,
    //! Pivot row strategy
    PIVOT_ROW_STRATEGY,
#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
    //! Number of different enumeration values.
#endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
    CONTROL_PARAMETER_NAME_SIZE
  };

  //! Possible values for PIP_Problem control parameters.
  enum Control_Parameter_Value {
    //! Choose the first non-integer row.
    CUTTING_STRATEGY_FIRST,
    //! Choose row which generates the deepest cut.
    CUTTING_STRATEGY_DEEPEST,
    //! Always generate all possible cuts.
    CUTTING_STRATEGY_ALL,

    //! Choose the first row with negative parameter sign.
    PIVOT_ROW_STRATEGY_FIRST,
    //! Choose a row that generates a lexicographically maximal pivot column.
    PIVOT_ROW_STRATEGY_MAX_COLUMN,

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
    //! Number of different enumeration values.
#endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
    CONTROL_PARAMETER_VALUE_SIZE
  };

  //! Returns the value of control parameter \p name.
  Control_Parameter_Value
  get_control_parameter(Control_Parameter_Name name) const;

  //! Sets control parameter \p value.
  void set_control_parameter(Control_Parameter_Value value);

  //! Sets the dimension for the big parameter to \p big_dim.
  void set_big_parameter_dimension(dimension_type big_dim);

  /*! \brief
    Returns the space dimension for the big parameter.

    If a big parameter was not set, returns \c not_a_dimension().
  */
  dimension_type get_big_parameter_dimension() const;

private:
  //! Initializes the control parameters with default values.
  void control_parameters_init();

  //! Copies the control parameters from problem object \p y.
  void control_parameters_copy(const PIP_Problem& y);

  //! The dimension of the vector space.
  dimension_type external_space_dim;

  /*! \brief
    The space dimension of the current (partial) solution of the
    PIP problem; it may be smaller than \p external_space_dim.
  */
  dimension_type internal_space_dim;

  //! An enumerated type describing the internal status of the PIP problem.
  enum Status {
    //! The PIP problem is unsatisfiable.
    UNSATISFIABLE,
    //! The PIP problem is optimized; the solution tree has been computed.
    OPTIMIZED,
    /*! \brief
      The feasible region of the PIP problem has been changed by adding
      new variables, parameters or constraints; a feasible solution for
      the old feasible region is still available.
    */
    PARTIALLY_SATISFIABLE
  };

  //! The internal state of the MIP problem.
  Status status;

  //! The current solution decision tree
  PIP_Tree_Node* current_solution;

  //! The sequence of constraints describing the feasible region.
  Constraint_Sequence input_cs;

  //! The first index of `input_cs' containing a pending constraint.
  dimension_type first_pending_constraint;

  /*! \brief
    A set containing all the indices of space dimensions that are
    interpreted as problem parameters.
  */
  Variables_Set parameters;

#if PPL_USE_SPARSE_MATRIX
  typedef Sparse_Row Row;
#else
  typedef Dense_Row Row;
#endif

  /*! \brief
    The initial context

    Contains problem constraints on parameters only
  */
  Matrix<Row> initial_context;

  //! The control parameters for the problem object.
  Control_Parameter_Value
  control_parameters[CONTROL_PARAMETER_NAME_SIZE];

  /*! \brief
    The dimension for the big parameter, or \c not_a_dimension()
    if not set.
  */
  dimension_type big_parameter_dimension;

  friend class PIP_Solution_Node;
};

/* Automatically generated from PPL source file ../src/PIP_Problem_inlines.hh line 1. */
/* PIP_Problem class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline dimension_type
PIP_Problem::space_dimension() const {
  return external_space_dim;
}

inline dimension_type
PIP_Problem::max_space_dimension() {
  return Constraint::max_space_dimension();
}

inline PIP_Problem::const_iterator
PIP_Problem::constraints_begin() const {
  return input_cs.begin();
}

inline PIP_Problem::const_iterator
PIP_Problem::constraints_end() const {
  return input_cs.end();
}

inline const Variables_Set&
PIP_Problem::parameter_space_dimensions() const {
  return parameters;
}

inline void
PIP_Problem::m_swap(PIP_Problem& y) {
  using std::swap;
  swap(external_space_dim, y.external_space_dim);
  swap(internal_space_dim, y.internal_space_dim);
  swap(status, y.status);
  swap(current_solution, y.current_solution);
  swap(input_cs, y.input_cs);
  swap(first_pending_constraint, y.first_pending_constraint);
  swap(parameters, y.parameters);
  swap(initial_context, y.initial_context);
  for (dimension_type i = CONTROL_PARAMETER_NAME_SIZE; i-- > 0; ) {
    swap(control_parameters[i], y.control_parameters[i]);
  }
  swap(big_parameter_dimension, y.big_parameter_dimension);
}

inline PIP_Problem&
PIP_Problem::operator=(const PIP_Problem& y) {
  PIP_Problem tmp(y);
  m_swap(tmp);
  return *this;
}

inline PIP_Problem::Control_Parameter_Value
PIP_Problem::get_control_parameter(Control_Parameter_Name name) const {
  PPL_ASSERT(name >= 0 && name < CONTROL_PARAMETER_NAME_SIZE);
  return control_parameters[name];
}

inline dimension_type
PIP_Problem::get_big_parameter_dimension() const {
  return big_parameter_dimension;
}

/*! \relates PIP_Problem */
inline void
swap(PIP_Problem& x, PIP_Problem& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/PIP_Problem_templates.hh line 1. */
/* PIP_Problem class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/PIP_Problem_templates.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename In>
PIP_Problem::PIP_Problem(dimension_type dim,
                         In first, In last,
                         const Variables_Set& p_vars)
  : external_space_dim(dim),
    internal_space_dim(0),
    status(PARTIALLY_SATISFIABLE),
    current_solution(0),
    input_cs(),
    first_pending_constraint(0),
    parameters(p_vars),
    initial_context(),
    big_parameter_dimension(not_a_dimension()) {
  // Check that integer Variables_Set does not exceed the space dimension
  // of the problem.
  if (p_vars.space_dimension() > external_space_dim) {
    std::ostringstream s;
    s << "PPL::PIP_Problem::PIP_Problem(dim, first, last, p_vars):\n"
      << "dim == " << external_space_dim
      << " and p_vars.space_dimension() == "
      << p_vars.space_dimension()
      << " are dimension incompatible.";
    throw std::invalid_argument(s.str());
  }

  // Check for space dimension overflow.
  if (dim > max_space_dimension()) {
    throw std::length_error("PPL::PIP_Problem::"
                            "PIP_Problem(dim, first, last, p_vars):\n"
                            "dim exceeds the maximum allowed "
                            "space dimension.");
  }
  // Check the constraints.
  for (In i = first; i != last; ++i) {
    if (i->space_dimension() > dim) {
      std::ostringstream s;
      s << "PPL::PIP_Problem::"
        << "PIP_Problem(dim, first, last, p_vars):\n"
        << "range [first, last) contains a constraint having space "
        << "dimension == " << i->space_dimension()
        << " that exceeds this->space_dimension == " << dim << ".";
      throw std::invalid_argument(s.str());
    }
    input_cs.push_back(*i);
  }
  control_parameters_init();
  PPL_ASSERT(OK());
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/PIP_Problem_defs.hh line 832. */

/* Automatically generated from PPL source file ../src/PIP_Tree_defs.hh line 36. */

/* Automatically generated from PPL source file ../src/PIP_Tree_defs.hh line 40. */

namespace Parma_Polyhedra_Library {

//! A node of the PIP solution tree.
/*!
  This is the base class for the nodes of the binary trees representing
  the solutions of PIP problems. From this one, two classes are derived:
    - PIP_Decision_Node, for the internal nodes of the tree;
    - PIP_Solution_Node, for the leaves of the tree.
*/
class PIP_Tree_Node {
protected:
  //! Constructor: builds a node owned by \p *owner.
  explicit PIP_Tree_Node(const PIP_Problem* owner);

  //! Copy constructor.
  PIP_Tree_Node(const PIP_Tree_Node& y);

  //! Returns a pointer to the PIP_Problem owning object.
  const PIP_Problem* get_owner() const;

  //! Sets the pointer to the PIP_Problem owning object.
  virtual void set_owner(const PIP_Problem* owner) = 0;

  /*! \brief
    Returns \c true if and only if all the nodes in the subtree
    rooted in \p *this are owned by \p *owner.
  */
  virtual bool check_ownership(const PIP_Problem* owner) const = 0;

public:
#if PPL_USE_SPARSE_MATRIX
  typedef Sparse_Row Row;
#else
  typedef Dense_Row Row;
#endif

  //! Returns a pointer to a dynamically-allocated copy of \p *this.
  virtual PIP_Tree_Node* clone() const = 0;

  //! Destructor.
  virtual ~PIP_Tree_Node();

  //! Returns \c true if and only if \p *this is well formed.
  virtual bool OK() const = 0;

  //! Returns \p this if \p *this is a solution node, 0 otherwise.
  virtual const PIP_Solution_Node* as_solution() const = 0;

  //! Returns \p this if \p *this is a decision node, 0 otherwise.
  virtual const PIP_Decision_Node* as_decision() const = 0;

  /*! \brief
    Returns the system of parameter constraints controlling \p *this.

    The indices in the constraints are the same as the original variables and
    parameters. Coefficients in indices corresponding to variables always are
    zero.
  */
  const Constraint_System& constraints() const;

  class Artificial_Parameter;

  //! A type alias for a sequence of Artificial_Parameter's.
  typedef std::vector<Artificial_Parameter> Artificial_Parameter_Sequence;

  //! Returns a const_iterator to the beginning of local artificial parameters.
  Artificial_Parameter_Sequence::const_iterator art_parameter_begin() const;

  //! Returns a const_iterator to the end of local artificial parameters.
  Artificial_Parameter_Sequence::const_iterator art_parameter_end() const;

  //! Returns the number of local artificial parameters.
  dimension_type art_parameter_count() const;

  //! Prints on \p s the tree rooted in \p *this.
  /*!
    \param s
    The output stream.

    \param indent
    The amount of indentation.
  */
  void print(std::ostream& s, int indent = 0) const;

  //! Dumps to \p s an ASCII representation of \p *this.
  void ascii_dump(std::ostream& s) const;

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  virtual memory_size_type total_memory_in_bytes() const = 0;
  //! Returns the size in bytes of the memory managed by \p *this.
  virtual memory_size_type external_memory_in_bytes() const = 0;

protected:
  //! A type alias for a sequence of constraints.
  typedef std::vector<Constraint> Constraint_Sequence;

  // Only PIP_Problem and PIP_Decision_Node are allowed to use the
  // constructor and methods.
  friend class PIP_Problem;
  friend class PIP_Decision_Node;
  friend class PIP_Solution_Node;

  //! A pointer to the PIP_Problem object owning this node.
  const PIP_Problem* owner_;

  //! A pointer to the parent of \p *this, null if \p *this is the root.
  const PIP_Decision_Node* parent_;

  //! The local system of parameter constraints.
  Constraint_System constraints_;

  //! The local sequence of expressions for local artificial parameters.
  Artificial_Parameter_Sequence artificial_parameters;

  //! Returns a pointer to this node's parent.
  const PIP_Decision_Node* parent() const;

  //! Set this node's parent to \p *p.
  void set_parent(const PIP_Decision_Node* p);

  /*! \brief
    Populates the parametric simplex tableau using external data.

    \param pip
    The PIP_Problem object containing this node.

    \param external_space_dim
    The number of all problem variables and problem parameters
    (excluding artificial parameters).

    \param first_pending_constraint
    The first element in \p input_cs to be added to the tableau,
    which already contains the previous elements.

    \param input_cs
    All the constraints of the PIP problem.

    \param parameters
    The set of indices of the problem parameters.
  */
  virtual void update_tableau(const PIP_Problem& pip,
                              dimension_type external_space_dim,
                              dimension_type first_pending_constraint,
                              const Constraint_Sequence& input_cs,
                              const Variables_Set& parameters) = 0;

  /*! \brief
    Executes a parametric simplex on the tableau, under specified context.

    \return
    The root of the PIP tree solution, or 0 if unfeasible.

    \param pip
    The PIP_Problem object containing this node.

    \param check_feasible_context
    Whether the resolution process should (re-)check feasibility of
    context (since the initial context may have been modified).

    \param context
    The context, being a set of constraints on the parameters.

    \param params
    The local parameter set, including parent's artificial parameters.

    \param space_dim
    The space dimension of parent, including artificial parameters.

    \param indent_level
    The indentation level (for debugging output only).
  */
  virtual PIP_Tree_Node* solve(const PIP_Problem& pip,
                               bool check_feasible_context,
                               const Matrix<Row>& context,
                               const Variables_Set& params,
                               dimension_type space_dim,
                               int indent_level) = 0;

  //! Inserts a new parametric constraint in internal row format.
  void add_constraint(const Row& row, const Variables_Set& parameters);

  //! Merges parent's artificial parameters into \p *this.
  void parent_merge();

  //! Prints on \p s the tree rooted in \p *this.
  /*!
    \param s
    The output stream.

    \param indent
    The amount of indentation.

    \param pip_dim_is_param
    A vector of Boolean flags telling which PIP problem dimensions are
    problem parameters. The size of the vector is equal to the PIP
    problem internal space dimension (i.e., no artificial parameters).

    \param first_art_dim
    The first space dimension corresponding to an artificial parameter
    that was created in this node (if any).
  */
  virtual void print_tree(std::ostream& s,
                          int indent,
                          const std::vector<bool>& pip_dim_is_param,
                          dimension_type first_art_dim) const = 0;

  //! A helper function used when printing PIP trees.
  static void
  indent_and_print(std::ostream& s, int indent, const char* str);

  /*! \brief
    Checks whether a context matrix is satisfiable.

    The satisfiability check is implemented by the revised dual simplex
    algorithm on the context matrix. The algorithm ensures the feasible
    solution is integer by applying a cut generation method when
    intermediate non-integer solutions are found.
  */
  static bool compatibility_check(Matrix<Row>& s);

  /*! \brief
    Helper method: checks for satisfiability of the restricted context
    obtained by adding \p row to \p context.
  */
  static bool compatibility_check(const Matrix<Row>& context, const Row& row);

}; // class PIP_Tree_Node


/*! \brief
  Artificial parameters in PIP solution trees.

  These parameters are built from a linear expression combining other
  parameters (constant term included) divided by a positive integer
  denominator. Coefficients at variables indices corresponding to
  PIP problem variables are always zero.
*/
class PIP_Tree_Node::Artificial_Parameter
  : public Linear_Expression {
public:
  //! Default constructor: builds a zero artificial parameter.
  Artificial_Parameter();

  //! Constructor.
  /*!
    Builds artificial parameter \f$\frac{\mathtt{expr}}{\mathtt{d}}\f$.

    \param expr
    The expression that, after normalization, will form the numerator of
    the artificial parameter.

    \param d
    The integer constant that, after normalization, will form the
    denominator of the artificial parameter.

    \exception std::invalid_argument
    Thrown if \p d is zero.

    Normalization will ensure that the denominator is positive.
  */
  Artificial_Parameter(const Linear_Expression& expr,
                       Coefficient_traits::const_reference d);

  //! Copy constructor.
  Artificial_Parameter(const Artificial_Parameter& y);

  //! Returns the normalized (i.e., positive) denominator.
  Coefficient_traits::const_reference denominator() const;

  //! Swaps \p *this with \p y.
  void m_swap(Artificial_Parameter& y);

  //! Returns \c true if and only if \p *this and \p y are equal.
  /*!
    Note that two artificial parameters having different space dimensions
    are considered to be different.
  */
  bool operator==(const Artificial_Parameter& y) const;
  //! Returns \c true if and only if \p *this and \p y are different.
  bool operator!=(const Artificial_Parameter& y) const;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;
  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  //! Returns \c true if and only if the parameter is well-formed.
  bool OK() const;

private:
  //! The normalized (i.e., positive) denominator.
  Coefficient denom;
}; // class PIP_Tree_Node::Artificial_Parameter


//! Swaps \p x with \p y.
/*! \relates PIP_Tree_Node::Artificial_Parameter */
void
swap(PIP_Tree_Node::Artificial_Parameter& x,
     PIP_Tree_Node::Artificial_Parameter& y);


//! A tree node representing part of the space of solutions.
class PIP_Solution_Node : public PIP_Tree_Node {
public:

  //! Constructor: builds a solution node owned by \p *owner.
  explicit PIP_Solution_Node(const PIP_Problem* owner);

  //! Returns a pointer to a dynamically-allocated copy of \p *this.
  virtual PIP_Tree_Node* clone() const;

  //! Destructor.
  virtual ~PIP_Solution_Node();

  //! Returns \c true if and only if \p *this is well formed.
  virtual bool OK() const;

  //! Returns \p this.
  virtual const PIP_Solution_Node* as_solution() const;

  //! Returns 0, since \p this is not a decision node.
  virtual const PIP_Decision_Node* as_decision() const;

  /*! \brief
    Returns a parametric expression for the values of problem variable \p var.

    The returned linear expression may involve problem parameters
    as well as artificial parameters.

    \param var
    The problem variable which is queried about.

    \exception std::invalid_argument
    Thrown if \p var is dimension-incompatible with the PIP_Problem
    owning this solution node, or if \p var is a problem parameter.
  */
  const Linear_Expression& parametric_values(Variable var) const;

  //! Dumps to \p os an ASCII representation of \p *this.
  void ascii_dump(std::ostream& os) const;

  /*! \brief
    Loads from \p is an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& is);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  virtual memory_size_type total_memory_in_bytes() const;
  //! Returns the size in bytes of the memory managed by \p *this.
  virtual memory_size_type external_memory_in_bytes() const;

private:
  //! The type for parametric simplex tableau.
  struct Tableau {
    //! The matrix of simplex coefficients.
    Matrix<Row> s;
    //! The matrix of parameter coefficients.
    Matrix<Row> t;
    //! A common denominator for all matrix elements
    Coefficient denom;

    //! Default constructor.
    Tableau();
    //! Copy constructor.
    Tableau(const Tableau& y);
    //! Destructor.
    ~Tableau();

    //! Tests whether the matrix is integer, i.e., the denominator is 1.
    bool is_integer() const;

    //! Multiplies all coefficients and denominator with ratio.
    void scale(Coefficient_traits::const_reference ratio);

    //! Normalizes the modulo of coefficients so that they are mutually prime.
    /*!
      Computes the Greatest Common Divisor (GCD) among the elements of
      the matrices and normalizes them and the denominator by the GCD itself.
    */
    void normalize();

    /*! \brief
      Compares two pivot row and column pairs before pivoting.

      The algorithm searches the first (ie, leftmost) column \f$k\f$ in
      parameter matrix for which the \f$c=s_{*j}\frac{t_{ik}}{s_{ij}}\f$
      and \f$c'=s_{*j'}\frac{t_{i'k}}{s_{i'j'}}\f$ columns are different,
      where \f$s_{*j}\f$ denotes the \f$j\f$<sup>th</sup> column from the
      \f$s\f$ matrix and \f$s_{*j'}\f$ is the \f$j'\f$<sup>th</sup> column
      of \f$s\f$.

      \f$c\f$ is the computed column that would be subtracted to column
      \f$k\f$ in parameter matrix if pivoting is done using the \f$(i,j)\f$
      row and column pair.
      \f$c'\f$ is the computed column that would be subtracted to column
      \f$k\f$ in parameter matrix if pivoting is done using the
      \f$(i',j')\f$ row and column pair.

      The test is true if the computed \f$-c\f$ column is lexicographically
      bigger than the \f$-c'\f$ column. Due to the column ordering in the
      parameter matrix of the tableau, leftmost search will enforce solution
      increase with respect to the following priority order:
       - the constant term
       - the coefficients for the original parameters
       - the coefficients for the oldest artificial parameters.

      \return
      \c true if pivot row and column pair \f$(i,j)\f$ is more
      suitable for pivoting than the \f$(i',j')\f$ pair

      \param mapping
      The PIP_Solution_Node::mapping vector for the tableau.

      \param basis
      The PIP_Solution_Node::basis vector for the tableau.

      \param row_0
      The row number for the first pivot row and column pair to be compared.

      \param col_0
      The column number for the first pivot row and column pair to be
      compared.

      \param row_1
      The row number for the second pivot row and column pair to be compared.

      \param col_1
      The column number for the second pivot row and column pair to be
      compared.
    */
    bool is_better_pivot(const std::vector<dimension_type>& mapping,
                         const std::vector<bool>& basis,
                         const dimension_type row_0,
                         const dimension_type col_0,
                         const dimension_type row_1,
                         const dimension_type col_1) const;

    //! Returns the value of the denominator.
    Coefficient_traits::const_reference denominator() const;

    //! Dumps to \p os an ASCII representation of \p *this.
    void ascii_dump(std::ostream& os) const;

    /*! \brief
      Loads from \p is an ASCII representation (as produced by
      ascii_dump(std::ostream&) const) and sets \p *this accordingly.
      Returns \c true if successful, \c false otherwise.
    */
    bool ascii_load(std::istream& is);

    //! Returns the size in bytes of the memory managed by \p *this.
    /*!
      \note
      No need for a \c total_memory_in_bytes() method, since
      class Tableau is a private inner class of PIP_Solution_Node.
    */
    memory_size_type external_memory_in_bytes() const;

    //! Returns \c true if and only if \p *this is well formed.
    bool OK() const;
  }; // struct Tableau

  //! The parametric simplex tableau.
  Tableau tableau;

  /*! \brief
    A boolean vector for identifying the basic variables.

    Variable identifiers are numbered from 0 to <CODE>n+m-1</CODE>, where \p n
    is the number of columns in the simplex tableau corresponding to variables,
    and \p m is the number of rows.

    Indices from 0 to <CODE>n-1</CODE> correspond to the original variables.

    Indices from \p n to <CODE>n+m-1</CODE> correspond to the slack variables
    associated to the internal constraints, which do not strictly correspond
    to original constraints, since these may have been transformed to fit the
    standard form of the dual simplex.

    The value for <CODE>basis[i]</CODE> is:
     - \b true if variable \p i is basic,
     - \b false if variable \p i is nonbasic.
  */
  std::vector<bool> basis;

  /*! \brief
    A mapping between the tableau rows/columns and the original variables.

    The value of <CODE>mapping[i]</CODE> depends of the value of <CODE>basis[i]</CODE>.

     - If <CODE>basis[i]</CODE> is \b true, <CODE>mapping[i]</CODE> encodes the column
       index of variable \p i in the \p s matrix of the tableau.
     - If <CODE>basis[i]</CODE> is \b false, <CODE>mapping[i]</CODE> encodes the row
       index of variable \p i in the tableau.
  */
  std::vector<dimension_type> mapping;

  /*! \brief
    The variable identifiers associated to the rows of the simplex tableau.
  */
  std::vector<dimension_type> var_row;

  /*! \brief
    The variable identifiers associated to the columns of the simplex tableau.
  */
  std::vector<dimension_type> var_column;

  /*! \brief
    The variable number of the special inequality used for modeling
    equality constraints.

    The subset of equality constraints in a specific problem can be expressed
    as: \f$f_i(x,p) = 0 ; 1 \leq i \leq n\f$. As the dual simplex standard form
    requires constraints to be inequalities, the following constraints can be
    modeled as follows:

     - \f$f_i(x,p) \geq 0 ; 1 \leq i \leq n\f$

     - \f$\sum\limits_{i=1}^n f_i(x,p) \leq 0\f$

    The \p special_equality_row value stores the variable number of the
    specific constraint which is used to model the latter sum of
    constraints. If no such constraint exists, the value is set to \p 0.
  */
  dimension_type special_equality_row;

  /*! \brief
    The column index in the parametric part of the simplex tableau
    corresponding to the big parameter; \c not_a_dimension() if not set.
  */
  dimension_type big_dimension;

  //! The possible values for the sign of a parametric linear expression.
  enum Row_Sign {
    //! Not computed yet (default).
    UNKNOWN,
    //! All row coefficients are zero.
    ZERO,
    //! All nonzero row coefficients are positive.
    POSITIVE,
    //! All nonzero row coefficients are negative.
    NEGATIVE,
    //! The row contains both positive and negative coefficients.
    MIXED
  };

  //! A cache for computed sign values of constraint parametric RHS.
  std::vector<Row_Sign> sign;

  //! Parametric values for the solution.
  std::vector<Linear_Expression> solution;

  //! An indicator for solution validity.
  bool solution_valid;

  //! Returns the sign of row \p x.
  static Row_Sign row_sign(const Row& x,
                           dimension_type big_dimension);

protected:
  //! Copy constructor.
  PIP_Solution_Node(const PIP_Solution_Node& y);

  //! A tag type to select the alternative copy constructor.
  struct No_Constraints {};

  //! Alternative copy constructor.
  /*!
    This constructor differs from the default copy constructor in that
    it will not copy the constraint system, nor the artificial parameters.
  */
  PIP_Solution_Node(const PIP_Solution_Node& y, No_Constraints);

  // PIP_Problem::ascii load() method needs access set_owner().
  friend bool PIP_Problem::ascii_load(std::istream& s);

  //! Sets the pointer to the PIP_Problem owning object.
  virtual void set_owner(const PIP_Problem* owner);

  /*! \brief
    Returns \c true if and only if all the nodes in the subtree
    rooted in \p *this is owned by \p *pip.
  */
  virtual bool check_ownership(const PIP_Problem* owner) const;

  //! Implements pure virtual method PIP_Tree_Node::update_tableau.
  virtual void update_tableau(const PIP_Problem& pip,
                              dimension_type external_space_dim,
                              dimension_type first_pending_constraint,
                              const Constraint_Sequence& input_cs,
                              const Variables_Set& parameters);

  /*! \brief
    Update the solution values.

    \param pip_dim_is_param
    A vector of Boolean flags telling which PIP problem dimensions are
    problem parameters. The size of the vector is equal to the PIP
    problem internal space dimension (i.e., no artificial parameters).
  */
  void update_solution(const std::vector<bool>& pip_dim_is_param) const;

  //! Helper method.
  void update_solution() const;

  //! Implements pure virtual method PIP_Tree_Node::solve.
  virtual PIP_Tree_Node* solve(const PIP_Problem& pip,
                               bool check_feasible_context,
                               const Matrix<Row>& context,
                               const Variables_Set& params,
                               dimension_type space_dim,
                               int indent_level);

  /*! \brief
    Generate a Gomory cut using non-integer tableau row \p index.

    \param index
    Row index in simplex tableau from which the cut is generated.

    \param parameters
    A std::set of the current parameter dimensions (including artificials);
    to be updated if a new artificial parameter is to be created.

    \param context
    A set of linear inequalities on the parameters, in matrix form; to be
    updated if a new artificial parameter is to be created.

    \param space_dimension
    The current space dimension, including variables and all parameters; to
    be updated if an extra parameter is to be created.

    \param indent_level
    The indentation level (for debugging output only).
  */
  void generate_cut(dimension_type index, Variables_Set& parameters,
                    Matrix<Row>& context, dimension_type& space_dimension,
                    int indent_level);

  //! Prints on \p s the tree rooted in \p *this.
  virtual void print_tree(std::ostream& s, int indent,
                          const std::vector<bool>& pip_dim_is_param,
                          dimension_type first_art_dim) const;

}; // class PIP_Solution_Node


//! A tree node representing a decision in the space of solutions.
class PIP_Decision_Node : public PIP_Tree_Node {
public:
  //! Returns a pointer to a dynamically-allocated copy of \p *this.
  virtual PIP_Tree_Node* clone() const;

  //! Destructor.
  virtual ~PIP_Decision_Node();

  //! Returns \c true if and only if \p *this is well formed.
  virtual bool OK() const;

  //! Returns \p this.
  virtual const PIP_Decision_Node* as_decision() const;

  //! Returns 0, since \p this is not a solution node.
  virtual const PIP_Solution_Node* as_solution() const;

  //! Returns a const pointer to the \p b (true or false) branch of \p *this.
  const PIP_Tree_Node* child_node(bool b) const;

  //! Returns a pointer to the \p b (true or false) branch of \p *this.
  PIP_Tree_Node* child_node(bool b);

  //! Dumps to \p s an ASCII representation of \p *this.
  void ascii_dump(std::ostream& s) const;

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  virtual memory_size_type total_memory_in_bytes() const;
  //! Returns the size in bytes of the memory managed by \p *this.
  virtual memory_size_type external_memory_in_bytes() const;

private:
  // PIP_Solution_Node is allowed to use the constructor and methods.
  friend class PIP_Solution_Node;

  // PIP_Problem ascii load method needs access to private constructors.
  friend bool PIP_Problem::ascii_load(std::istream& s);

  //! Pointer to the "false" child of \p *this.
  PIP_Tree_Node* false_child;

  //! Pointer to the "true" child of \p *this.
  PIP_Tree_Node* true_child;

  /*! \brief
    Builds a decision node having \p fcp and \p tcp as child.

    The decision node will encode the structure
    "if \c cs then \p tcp else \p fcp",
    where the system of constraints \c cs is initially empty.

    \param owner
    Pointer to the owning PIP_Problem object; it may be null if and
    only if both children are null.

    \param fcp
    Pointer to "false" child; it may be null.

    \param tcp
    Pointer to "true" child; it may be null.

    \note
    If any of \p fcp or \p tcp is not null, then \p owner is required
    to be not null and equal to the owner of its non-null children;
    otherwise the behavior is undefined.
  */
  explicit PIP_Decision_Node(const PIP_Problem* owner,
                             PIP_Tree_Node* fcp,
                             PIP_Tree_Node* tcp);

  //! Sets the pointer to the PIP_Problem owning object.
  virtual void set_owner(const PIP_Problem* owner);

  /*! \brief
    Returns \c true if and only if all the nodes in the subtree
    rooted in \p *this is owned by \p *pip.
  */
  virtual bool check_ownership(const PIP_Problem* owner) const;

protected:
  //! Copy constructor.
  PIP_Decision_Node(const PIP_Decision_Node& y);

  //! Implements pure virtual method PIP_Tree_Node::update_tableau.
  virtual void update_tableau(const PIP_Problem& pip,
                              dimension_type external_space_dim,
                              dimension_type first_pending_constraint,
                              const Constraint_Sequence& input_cs,
                              const Variables_Set& parameters);

  //! Implements pure virtual method PIP_Tree_Node::solve.
  virtual PIP_Tree_Node* solve(const PIP_Problem& pip,
                               bool check_feasible_context,
                               const Matrix<Row>& context,
                               const Variables_Set& params,
                               dimension_type space_dim,
                               int indent_level);

  //! Prints on \p s the tree rooted in \p *this.
  virtual void print_tree(std::ostream& s, int indent,
                          const std::vector<bool>& pip_dim_is_param,
                          dimension_type first_art_dim) const;

}; // class PIP_Decision_Node

namespace IO_Operators {

//! Output operator: prints the solution tree rooted in \p x.
/*! \relates Parma_Polyhedra_Library::PIP_Tree_Node */
std::ostream& operator<<(std::ostream& os, const PIP_Tree_Node& x);

//! Output operator.
/*! \relates Parma_Polyhedra_Library::PIP_Tree_Node::Artificial_Parameter */
std::ostream& operator<<(std::ostream& os,
                         const PIP_Tree_Node::Artificial_Parameter& x);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/PIP_Tree_inlines.hh line 1. */
/* PIP_Tree related class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

inline
PIP_Solution_Node::Tableau::Tableau()
  : s(), t(), denom(1) {
  PPL_ASSERT(OK());
}

inline
PIP_Solution_Node::Tableau::Tableau(const Tableau& y)
  : s(y.s), t(y.t), denom(y.denom) {
  PPL_ASSERT(OK());
}

inline
PIP_Solution_Node::Tableau::~Tableau() {
}

inline bool
PIP_Solution_Node::Tableau::is_integer() const {
  return denom == 1;
}

inline Coefficient_traits::const_reference
PIP_Solution_Node::Tableau::denominator() const {
  return denom;
}

inline void
PIP_Tree_Node::set_parent(const PIP_Decision_Node* p) {
  parent_ = p;
}

inline const PIP_Decision_Node*
PIP_Tree_Node::parent() const {
  return parent_;
}

inline const PIP_Problem*
PIP_Tree_Node::get_owner() const {
  return owner_;
}

inline const Constraint_System&
PIP_Tree_Node::constraints() const {
  return constraints_;
}

inline PIP_Tree_Node::Artificial_Parameter_Sequence::const_iterator
PIP_Tree_Node::art_parameter_begin() const {
  return artificial_parameters.begin();
}

inline PIP_Tree_Node::Artificial_Parameter_Sequence::const_iterator
PIP_Tree_Node::art_parameter_end() const {
  return artificial_parameters.end();
}

inline dimension_type
PIP_Tree_Node::art_parameter_count() const {
  return artificial_parameters.size();
}

inline
const PIP_Tree_Node*
PIP_Decision_Node::child_node(bool b) const {
  return b ? true_child : false_child;
}

inline
PIP_Tree_Node*
PIP_Decision_Node::child_node(bool b) {
  return b ? true_child : false_child;
}

inline
PIP_Tree_Node::Artificial_Parameter::Artificial_Parameter()
  : Linear_Expression(), denom(1) {
  PPL_ASSERT(OK());
}

inline
PIP_Tree_Node::Artificial_Parameter
::Artificial_Parameter(const Artificial_Parameter& y)
  : Linear_Expression(y), denom(y.denom) {
  PPL_ASSERT(OK());
}

inline Coefficient_traits::const_reference
PIP_Tree_Node::Artificial_Parameter::denominator() const {
  return denom;
}

inline void
PIP_Tree_Node::Artificial_Parameter::m_swap(Artificial_Parameter& y) {
  Linear_Expression::m_swap(y);
  using std::swap;
  swap(denom, y.denom);
}

/*! \relates PIP_Tree_Node::Artificial_Parameter */
inline void
swap(PIP_Tree_Node::Artificial_Parameter& x,
     PIP_Tree_Node::Artificial_Parameter& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/PIP_Tree_defs.hh line 835. */

/* Automatically generated from PPL source file ../src/BHRZ03_Certificate_defs.hh line 1. */
/* BHRZ03_Certificate class declaration.
*/


/* Automatically generated from PPL source file ../src/BHRZ03_Certificate_defs.hh line 30. */
#include <vector>

//! The convergence certificate for the BHRZ03 widening operator.
/*! \ingroup PPL_CXX_interface
  Convergence certificates are used to instantiate the BHZ03 framework
  so as to define widening operators for the finite powerset domain.

  \note
  Each convergence certificate has to be used together with a
  compatible widening operator. In particular, BHRZ03_Certificate
  can certify the convergence of both the BHRZ03 and the H79 widenings.
*/
class Parma_Polyhedra_Library::BHRZ03_Certificate {
public:
  //! Default constructor.
  BHRZ03_Certificate();

  //! Constructor: computes the certificate for \p ph.
  BHRZ03_Certificate(const Polyhedron& ph);

  //! Copy constructor.
  BHRZ03_Certificate(const BHRZ03_Certificate& y);

  //! Destructor.
  ~BHRZ03_Certificate();

  //! The comparison function for certificates.
  /*!
    \return
    \f$-1\f$, \f$0\f$ or \f$1\f$ depending on whether \p *this
    is smaller than, equal to, or greater than \p y, respectively.

    Compares \p *this with \p y, using a total ordering which is a
    refinement of the limited growth ordering relation for the
    BHRZ03 widening.
  */
  int compare(const BHRZ03_Certificate& y) const;

  //! Compares \p *this with the certificate for polyhedron \p ph.
  int compare(const Polyhedron& ph) const;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Returns <CODE>true</CODE> if and only if the certificate for
    polyhedron \p ph is strictly smaller than \p *this.
  */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  bool is_stabilizing(const Polyhedron& ph) const;

  //! A total ordering on BHRZ03 certificates.
  /*! \ingroup PPL_CXX_interface
    This binary predicate defines a total ordering on BHRZ03 certificates
    which is used when storing information about sets of polyhedra.
  */
  struct Compare {
    //! Returns <CODE>true</CODE> if and only if \p x comes before \p y.
    bool operator()(const BHRZ03_Certificate& x,
                    const BHRZ03_Certificate& y) const;
  };

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! Check if gathered information is meaningful.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  bool OK() const;

private:
  //! Affine dimension of the polyhedron.
  dimension_type affine_dim;
  //! Dimension of the lineality space of the polyhedron.
  dimension_type lin_space_dim;
  //! Cardinality of a non-redundant constraint system for the polyhedron.
  dimension_type num_constraints;
  /*! \brief
    Number of non-redundant points in a generator system
    for the polyhedron.
  */
  dimension_type num_points;
  /*! \brief
    A vector containing, for each index `0 <= i < space_dim',
    the number of non-redundant rays in a generator system of the
    polyhedron having exactly `i' null coordinates.
  */
  std::vector<dimension_type> num_rays_null_coord;
};

/* Automatically generated from PPL source file ../src/BHRZ03_Certificate_inlines.hh line 1. */
/* BHRZ03_Certificate class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/BHRZ03_Certificate_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

inline
BHRZ03_Certificate::BHRZ03_Certificate()
  : affine_dim(0), lin_space_dim(0), num_constraints(0), num_points(1),
    num_rays_null_coord() {
  // This is the certificate for a zero-dim universe polyhedron.
  PPL_ASSERT(OK());
}

inline
BHRZ03_Certificate::BHRZ03_Certificate(const BHRZ03_Certificate& y)
  : affine_dim(y.affine_dim), lin_space_dim(y.lin_space_dim),
    num_constraints(y.num_constraints), num_points(y.num_points),
    num_rays_null_coord(y.num_rays_null_coord) {
}

inline
BHRZ03_Certificate::~BHRZ03_Certificate() {
}

inline bool
BHRZ03_Certificate::is_stabilizing(const Polyhedron& ph) const {
  return compare(ph) == 1;
}

inline bool
BHRZ03_Certificate::Compare::operator()(const BHRZ03_Certificate& x,
                                        const BHRZ03_Certificate& y) const {
  // For an efficient evaluation of the multiset ordering based
  // on this LGO relation, we want larger elements to come first.
  return x.compare(y) == 1;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/BHRZ03_Certificate_defs.hh line 116. */

/* Automatically generated from PPL source file ../src/H79_Certificate_defs.hh line 1. */
/* H79_Certificate class declaration.
*/


/* Automatically generated from PPL source file ../src/H79_Certificate_defs.hh line 30. */
#include <vector>

//! A convergence certificate for the H79 widening operator.
/*! \ingroup PPL_CXX_interface
  Convergence certificates are used to instantiate the BHZ03 framework
  so as to define widening operators for the finite powerset domain.
  \note
  The convergence of the H79 widening can also be certified by
  BHRZ03_Certificate.
*/
class Parma_Polyhedra_Library::H79_Certificate {
public:
  //! Default constructor.
  H79_Certificate();

  //! Constructor: computes the certificate for \p ph.
  template <typename PH>
  H79_Certificate(const PH& ph);

  //! Constructor: computes the certificate for \p ph.
  H79_Certificate(const Polyhedron& ph);

  //! Copy constructor.
  H79_Certificate(const H79_Certificate& y);

  //! Destructor.
  ~H79_Certificate();

  //! The comparison function for certificates.
  /*!
    \return
    \f$-1\f$, \f$0\f$ or \f$1\f$ depending on whether \p *this
    is smaller than, equal to, or greater than \p y, respectively.

    Compares \p *this with \p y, using a total ordering which is a
    refinement of the limited growth ordering relation for the
    H79 widening.
  */
  int compare(const H79_Certificate& y) const;

  //! Compares \p *this with the certificate for polyhedron \p ph.
  template <typename PH>
  int compare(const PH& ph) const;

  //! Compares \p *this with the certificate for polyhedron \p ph.
  int compare(const Polyhedron& ph) const;

  //! A total ordering on H79 certificates.
  /*! \ingroup PPL_CXX_interface
    This binary predicate defines a total ordering on H79 certificates
    which is used when storing information about sets of polyhedra.
  */
  struct Compare {
    //! Returns <CODE>true</CODE> if and only if \p x comes before \p y.
    bool operator()(const H79_Certificate& x,
                    const H79_Certificate& y) const;
  };

private:
  //! Affine dimension of the polyhedron.
  dimension_type affine_dim;
  //! Cardinality of a non-redundant constraint system for the polyhedron.
  dimension_type num_constraints;
};

/* Automatically generated from PPL source file ../src/H79_Certificate_inlines.hh line 1. */
/* H79_Certificate class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/H79_Certificate_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

inline
H79_Certificate::H79_Certificate()
  : affine_dim(0), num_constraints(0) {
  // This is the certificate for a zero-dim universe polyhedron.
}

inline
H79_Certificate::H79_Certificate(const H79_Certificate& y)
  : affine_dim(y.affine_dim), num_constraints(y.num_constraints) {
}

inline
H79_Certificate::~H79_Certificate() {
}

inline bool
H79_Certificate::Compare::operator()(const H79_Certificate& x,
                                     const H79_Certificate& y) const {
  // For an efficient evaluation of the multiset ordering based
  // on this LGO relation, we want larger elements to come first.
  return x.compare(y) == 1;
}

template <typename PH>
inline
H79_Certificate::H79_Certificate(const PH& ph)
  : affine_dim(0), num_constraints(0) {
  H79_Certificate cert(Polyhedron(NECESSARILY_CLOSED, ph.constraints()));
  affine_dim = cert.affine_dim;
  num_constraints = cert.num_constraints;
}

template <typename PH>
inline int
H79_Certificate::compare(const PH& ph) const {
  return this->compare(Polyhedron(NECESSARILY_CLOSED, ph.constraints()));
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/H79_Certificate_defs.hh line 96. */

/* Automatically generated from PPL source file ../src/Grid_Certificate_defs.hh line 1. */
/* Grid_Certificate class declaration.
*/


/* Automatically generated from PPL source file ../src/Grid_Certificate_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/Grid_Certificate_defs.hh line 31. */
#include <vector>

//! The convergence certificate for the Grid widening operator.
/*! \ingroup PPL_CXX_interface
  Convergence certificates are used to instantiate the BHZ03 framework
  so as to define widening operators for the finite powerset domain.

  \note
  Each convergence certificate has to be used together with a
  compatible widening operator. In particular, Grid_Certificate can
  certify the Grid widening.
*/
class Parma_Polyhedra_Library::Grid_Certificate {
public:
  //! Default constructor.
  Grid_Certificate();

  //! Constructor: computes the certificate for \p gr.
  Grid_Certificate(const Grid& gr);

  //! Copy constructor.
  Grid_Certificate(const Grid_Certificate& y);

  //! Destructor.
  ~Grid_Certificate();

  //! The comparison function for certificates.
  /*!
    \return
    \f$-1\f$, \f$0\f$ or \f$1\f$ depending on whether \p *this
    is smaller than, equal to, or greater than \p y, respectively.
  */
  int compare(const Grid_Certificate& y) const;

  //! Compares \p *this with the certificate for grid \p gr.
  int compare(const Grid& gr) const;

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Returns <CODE>true</CODE> if and only if the certificate for grid
    \p gr is strictly smaller than \p *this.
  */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  bool is_stabilizing(const Grid& gr) const;

  //! A total ordering on Grid certificates.
  /*!
    This binary predicate defines a total ordering on Grid certificates
    which is used when storing information about sets of grids.
  */
  struct Compare {
    //! Returns <CODE>true</CODE> if and only if \p x comes before \p y.
    bool operator()(const Grid_Certificate& x,
                    const Grid_Certificate& y) const;
  };

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! Check if gathered information is meaningful.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  bool OK() const;

private:
  //! Number of a equalities in a minimized congruence system for the
  //! grid.
  dimension_type num_equalities;
  //! Number of a proper congruences in a minimized congruence system
  //! for the grid.
  dimension_type num_proper_congruences;
};

/* Automatically generated from PPL source file ../src/Grid_Certificate_inlines.hh line 1. */
/* Grid_Certificate class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Grid_Certificate_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

inline
Grid_Certificate::Grid_Certificate()
  : num_equalities(0), num_proper_congruences(0) {
  // This is the certificate for a zero-dim universe grid.
  PPL_ASSERT(OK());
}

inline
Grid_Certificate::Grid_Certificate(const Grid_Certificate& y)
  : num_equalities(y.num_equalities),
    num_proper_congruences(y.num_proper_congruences) {
}

inline
Grid_Certificate::~Grid_Certificate() {
}

inline bool
Grid_Certificate::is_stabilizing(const Grid& gr) const {
  return compare(gr) == 1;
}

inline bool
Grid_Certificate::Compare::operator()(const Grid_Certificate& x,
                                      const Grid_Certificate& y) const {
  // For an efficient evaluation of the multiset ordering based
  // on this LGO relation, we want larger elements to come first.
  return x.compare(y) == 1;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Grid_Certificate_defs.hh line 102. */

/* Automatically generated from PPL source file ../src/Partial_Function_defs.hh line 1. */
/* Partial_Function class declaration.
*/


/* Automatically generated from PPL source file ../src/Partial_Function_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Partial_Function;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Partial_Function_defs.hh line 29. */
#include <vector>
#ifndef NDEBUG
#include <set>
#endif
#include <iosfwd>

namespace Parma_Polyhedra_Library {

class Partial_Function {
public:
  /*! \brief
    Default constructor: builds a function with empty codomain
    (i.e., always undefined).
  */
  Partial_Function();

  /*! \brief
    Returns \c true if and only if the represented partial function
    has an empty codomain (i.e., it is always undefined).
  */
  bool has_empty_codomain() const;

  /*! \brief
    If the codomain is \e not empty, returns the maximum value in it.

    \exception std::runtime_error
    Thrown if called when \p *this has an empty codomain.
  */
  dimension_type max_in_codomain() const;

  /*! \brief
    If \p *this maps \p i to a value \c k, assigns \c k to \p j and
    returns \c true; otherwise, \p j is unchanged and \c false is returned.
  */
  bool maps(dimension_type i, dimension_type& j) const;

  void print(std::ostream& s) const;

  /*! \brief
    Modifies \p *this so that \p i is mapped to \p j.

    \exception std::runtime_error
    Thrown if \p *this is already mapping \p j.
  */
  void insert(dimension_type i, dimension_type j);

private:
  std::vector<dimension_type> vec;
  dimension_type max;
#ifndef NDEBUG
  std::set<dimension_type> codomain;
#endif
}; // class Partial_Function

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Partial_Function_inlines.hh line 1. */
/* Partial_Function class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Partial_Function_inlines.hh line 28. */
#include <stdexcept>

namespace Parma_Polyhedra_Library {

inline
Partial_Function::Partial_Function()
  : max(0) {
}

inline bool
Partial_Function::has_empty_codomain() const {
  PPL_ASSERT(vec.empty() == codomain.empty());
  return vec.empty();
}

inline dimension_type
Partial_Function::max_in_codomain() const {
  if (has_empty_codomain()) {
    throw std::runtime_error("Partial_Function::max_in_codomain() called"
                             " when has_empty_codomain()");
  }
  PPL_ASSERT(codomain.begin() != codomain.end()
             && max == *codomain.rbegin());
  return max;
}

inline void
Partial_Function::insert(dimension_type i, dimension_type j) {
#ifndef NDEBUG
  // The partial function has to be an injective map.
  std::pair<std::set<dimension_type>::iterator, bool> s = codomain.insert(j);
  PPL_ASSERT(s.second);
#endif // #ifndef NDEBUG

  // Expand `vec' if needed.
  const dimension_type sz = vec.size();
  if (i >= sz) {
    vec.insert(vec.end(), i - sz + 1, not_a_dimension());
  }

  // We cannot remap the same index to another one.
  PPL_ASSERT(i < vec.size() && vec[i] == not_a_dimension());
  vec[i] = j;

  // Maybe update `max'.
  if (j > max) {
    max = j;
  }
  PPL_ASSERT(codomain.begin() != codomain.end()
             && max == *codomain.rbegin());
}

inline bool
Partial_Function::maps(dimension_type i, dimension_type& j) const {
  if (i >= vec.size()) {
    return false;
  }
  const dimension_type vec_i = vec[i];
  if (vec_i == not_a_dimension()) {
    return false;
  }
  j = vec_i;
  return true;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Partial_Function_defs.hh line 86. */

/* Automatically generated from PPL source file ../src/Widening_Function_defs.hh line 1. */
/* Widening_Function class declaration.
*/


/* Automatically generated from PPL source file ../src/Widening_Function_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename PSET>
class Widening_Function;

template <typename PSET, typename CSYS>
class Limited_Widening_Function;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Widening_Function_defs.hh line 29. */

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Wraps a widening method into a function object.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename PSET>
class Parma_Polyhedra_Library::Widening_Function {
public:
  //! The (parametric) type of a widening method.
  typedef void (PSET::* Widening_Method)(const PSET&, unsigned*);

  //! Explicit unary constructor.
  explicit
  Widening_Function(Widening_Method wm);

  //! Function-application operator.
  /*!
    Computes <CODE>(x.*wm)(y, tp)</CODE>, where \p wm is the widening
    method stored at construction time.
  */
  void operator()(PSET& x, const PSET& y, unsigned* tp = 0) const;

private:
  //! The widening method.
  Widening_Method w_method;
};


#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Wraps a limited widening method into a function object.
/*! \ingroup PPL_CXX_interface */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename PSET, typename CSYS>
class Parma_Polyhedra_Library::Limited_Widening_Function {
public:
  //! The (parametric) type of a limited widening method.
  typedef void (PSET::* Limited_Widening_Method)(const PSET&,
                                                 const CSYS&,
                                                 unsigned*);

  //! Constructor.
  /*!
    \param lwm
    The limited widening method.

    \param cs
    The constraint system limiting the widening.
  */
  Limited_Widening_Function(Limited_Widening_Method lwm,
                            const CSYS& cs);

  //! Function-application operator.
  /*!
    Computes <CODE>(x.*lwm)(y, cs, tp)</CODE>, where \p lwm and \p cs
    are the limited widening method and the constraint system stored
    at construction time.
  */
  void operator()(PSET& x, const PSET& y, unsigned* tp = 0) const;

private:
  //! The limited widening method.
  Limited_Widening_Method lw_method;
  //! A constant reference to the constraint system limiting the widening.
  const CSYS& limiting_cs;
};

namespace Parma_Polyhedra_Library {

//! Wraps a widening method into a function object.
/*!
  \relates Pointset_Powerset

  \param wm
  The widening method.
*/
template <typename PSET>
Widening_Function<PSET>
widen_fun_ref(void (PSET::* wm)(const PSET&, unsigned*));

//! Wraps a limited widening method into a function object.
/*!
  \relates Pointset_Powerset

  \param lwm
  The limited widening method.

  \param cs
  The constraint system limiting the widening.
*/
template <typename PSET, typename CSYS>
Limited_Widening_Function<PSET, CSYS>
widen_fun_ref(void (PSET::* lwm)(const PSET&, const CSYS&, unsigned*),
              const CSYS& cs);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Widening_Function_inlines.hh line 1. */
/* Widening_Function class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Widening_Function_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename PSET>
Widening_Function<PSET>::Widening_Function(Widening_Method wm)
  : w_method(wm) {
}

template <typename PSET>
inline void
Widening_Function<PSET>::
operator()(PSET& x, const PSET& y, unsigned* tp) const {
  (x.*w_method)(y, tp);
}

template <typename PSET, typename CSYS>
Limited_Widening_Function<PSET, CSYS>::
Limited_Widening_Function(Limited_Widening_Method lwm,
                          const CSYS& cs)
  : lw_method(lwm), limiting_cs(cs) {
}

template <typename PSET, typename CSYS>
inline void
Limited_Widening_Function<PSET, CSYS>::
operator()(PSET& x, const PSET& y, unsigned* tp) const {
  (x.*lw_method)(y, limiting_cs, tp);
}

/*! \relates Pointset_Powerset */
template <typename PSET>
inline Widening_Function<PSET>
widen_fun_ref(void (PSET::* wm)(const PSET&, unsigned*)) {
  return Widening_Function<PSET>(wm);
}

/*! \relates Pointset_Powerset */
template <typename PSET, typename CSYS>
inline Limited_Widening_Function<PSET, CSYS>
widen_fun_ref(void (PSET::* lwm)(const PSET&, const CSYS&, unsigned*),
              const CSYS& cs) {
  return Limited_Widening_Function<PSET, CSYS>(lwm, cs);
}

} // namespace Parma_Polyhedra_Library


/* Automatically generated from PPL source file ../src/Widening_Function_defs.hh line 126. */

/* Automatically generated from PPL source file ../src/max_space_dimension.hh line 1. */
/* Definition of functions yielding maximal space dimensions.
*/


/* Automatically generated from PPL source file ../src/NNC_Polyhedron_defs.hh line 1. */
/* NNC_Polyhedron class declaration.
*/


/* Automatically generated from PPL source file ../src/NNC_Polyhedron_defs.hh line 31. */

//! A not necessarily closed convex polyhedron.
/*! \ingroup PPL_CXX_interface
    An object of the class NNC_Polyhedron represents a
    <EM>not necessarily closed</EM> (NNC) convex polyhedron
    in the vector space \f$\Rset^n\f$.

    \note
    Since NNC polyhedra are a generalization of closed polyhedra,
    any object of the class C_Polyhedron can be (explicitly) converted
    into an object of the class NNC_Polyhedron.
    The reason for defining two different classes is that objects of
    the class C_Polyhedron are characterized by a more efficient
    implementation, requiring less time and memory resources.
*/
class Parma_Polyhedra_Library::NNC_Polyhedron : public Polyhedron {
public:
  //! Builds either the universe or the empty NNC polyhedron.
  /*!
    \param num_dimensions
    The number of dimensions of the vector space enclosing the NNC polyhedron;

    \param kind
    Specifies whether a universe or an empty NNC polyhedron should be built.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space dimension.

    Both parameters are optional:
    by default, a 0-dimension space universe NNC polyhedron is built.
  */
  explicit NNC_Polyhedron(dimension_type num_dimensions = 0,
                          Degenerate_Element kind = UNIVERSE);

  //! Builds an NNC polyhedron from a system of constraints.
  /*!
    The polyhedron inherits the space dimension of the constraint system.

    \param cs
    The system of constraints defining the polyhedron.
  */
  explicit NNC_Polyhedron(const Constraint_System& cs);

  //! Builds an NNC polyhedron recycling a system of constraints.
  /*!
    The polyhedron inherits the space dimension of the constraint system.

    \param cs
    The system of constraints defining the polyhedron.  It is not
    declared <CODE>const</CODE> because its data-structures may be
    recycled to build the polyhedron.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.
  */
  NNC_Polyhedron(Constraint_System& cs, Recycle_Input dummy);

  //! Builds an NNC polyhedron from a system of generators.
  /*!
    The polyhedron inherits the space dimension of the generator system.

    \param gs
    The system of generators defining the polyhedron.

    \exception std::invalid_argument
    Thrown if the system of generators is not empty but has no points.
  */
  explicit NNC_Polyhedron(const Generator_System& gs);

  //! Builds an NNC polyhedron recycling a system of generators.
  /*!
    The polyhedron inherits the space dimension of the generator system.

    \param gs
    The system of generators defining the polyhedron.  It is not
    declared <CODE>const</CODE> because its data-structures may be
    recycled to build the polyhedron.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.

    \exception std::invalid_argument
    Thrown if the system of generators is not empty but has no points.
  */
  NNC_Polyhedron(Generator_System& gs, Recycle_Input dummy);

  //! Builds an NNC polyhedron from a system of congruences.
  /*!
    The polyhedron inherits the space dimension of the congruence system.

    \param cgs
    The system of congruences defining the polyhedron.  It is not
    declared <CODE>const</CODE> because its data-structures may be
    recycled to build the polyhedron.
  */
  explicit NNC_Polyhedron(const Congruence_System& cgs);

  //! Builds an NNC polyhedron recycling a system of congruences.
  /*!
    The polyhedron inherits the space dimension of the congruence
    system.

    \param cgs
    The system of congruences defining the polyhedron.  It is not
    declared <CODE>const</CODE> because its data-structures may be
    recycled to build the polyhedron.

    \param dummy
    A dummy tag to syntactically differentiate this one
    from the other constructors.
  */
  NNC_Polyhedron(Congruence_System& cgs, Recycle_Input dummy);

  //! Builds an NNC polyhedron from the C polyhedron \p y.
  /*!
    \param y
    The C polyhedron to be used;

    \param complexity
    This argument is ignored.
  */
  explicit NNC_Polyhedron(const C_Polyhedron& y,
                          Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds an NNC polyhedron out of a box.
  /*!
    The polyhedron inherits the space dimension of the box
    and is the most precise that includes the box.

    \param box
    The box representing the polyhedron to be built;

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.

    \exception std::length_error
    Thrown if the space dimension of \p box exceeds the maximum allowed
    space dimension.
  */
  template <typename Interval>
  explicit NNC_Polyhedron(const Box<Interval>& box,
                          Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds an NNC polyhedron out of a grid.
  /*!
    The polyhedron inherits the space dimension of the grid
    and is the most precise that includes the grid.

    \param grid
    The grid used to build the polyhedron.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.
  */
  explicit NNC_Polyhedron(const Grid& grid,
                          Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a NNC polyhedron out of a BD shape.
  /*!
    The polyhedron inherits the space dimension of the BD shape
    and is the most precise that includes the BD shape.

    \param bd
    The BD shape used to build the polyhedron.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.
  */
  template <typename U>
  explicit NNC_Polyhedron(const BD_Shape<U>& bd,
                          Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a NNC polyhedron out of an octagonal shape.
  /*!
    The polyhedron inherits the space dimension of the octagonal shape
    and is the most precise that includes the octagonal shape.

    \param os
    The octagonal shape used to build the polyhedron.

    \param complexity
    This argument is ignored as the algorithm used has
    polynomial complexity.
  */
  template <typename U>
  explicit NNC_Polyhedron(const Octagonal_Shape<U>& os,
                          Complexity_Class complexity = ANY_COMPLEXITY);

  //! Ordinary copy constructor.
  NNC_Polyhedron(const NNC_Polyhedron& y,
                 Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    The assignment operator.
    (\p *this and \p y can be dimension-incompatible.)
  */
  NNC_Polyhedron& operator=(const NNC_Polyhedron& y);

  //! Assigns to \p *this the C polyhedron \p y.
  NNC_Polyhedron& operator=(const C_Polyhedron& y);

  //! Destructor.
  ~NNC_Polyhedron();

  /*! \brief
    If the poly-hull of \p *this and \p y is exact it is assigned
    to \p *this and <CODE>true</CODE> is returned,
    otherwise <CODE>false</CODE> is returned.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool poly_hull_assign_if_exact(const NNC_Polyhedron& y);

  //! Same as poly_hull_assign_if_exact(y).
  bool upper_bound_assign_if_exact(const NNC_Polyhedron& y);

  /*! \brief
    Assigns to \p *this (the best approximation of) the result of
    computing the
    \ref Positive_Time_Elapse_Operator "positive time-elapse"
    between \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void positive_time_elapse_assign(const Polyhedron& y);
};

/* Automatically generated from PPL source file ../src/NNC_Polyhedron_inlines.hh line 1. */
/* NNC_Polyhedron class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/NNC_Polyhedron_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

inline
NNC_Polyhedron::~NNC_Polyhedron() {
}

inline
NNC_Polyhedron::NNC_Polyhedron(dimension_type num_dimensions,
                               Degenerate_Element kind)
  : Polyhedron(NOT_NECESSARILY_CLOSED,
               check_space_dimension_overflow(num_dimensions,
                                              NOT_NECESSARILY_CLOSED,
                                              "NNC_Polyhedron(n, k)",
                                              "n exceeds the maximum "
                                              "allowed space dimension"),
               kind) {
}

inline
NNC_Polyhedron::NNC_Polyhedron(const Constraint_System& cs)
  : Polyhedron(NOT_NECESSARILY_CLOSED,
               check_obj_space_dimension_overflow(cs, NOT_NECESSARILY_CLOSED,
                                                  "NNC_Polyhedron(cs)",
                                                  "the space dimension of cs "
                                                  "exceeds the maximum allowed "
                                                  "space dimension")) {
}

inline
NNC_Polyhedron::NNC_Polyhedron(Constraint_System& cs, Recycle_Input)
  : Polyhedron(NOT_NECESSARILY_CLOSED,
               check_obj_space_dimension_overflow(cs, NOT_NECESSARILY_CLOSED,
                                                  "NNC_Polyhedron(cs, recycle)",
                                                  "the space dimension of cs "
                                                  "exceeds the maximum allowed "
                                                  "space dimension"),
               Recycle_Input()) {
}

inline
NNC_Polyhedron::NNC_Polyhedron(const Generator_System& gs)
  : Polyhedron(NOT_NECESSARILY_CLOSED,
               check_obj_space_dimension_overflow(gs, NOT_NECESSARILY_CLOSED,
                                                  "NNC_Polyhedron(gs)",
                                                  "the space dimension of gs "
                                                  "exceeds the maximum allowed "
                                                  "space dimension")) {
}

inline
NNC_Polyhedron::NNC_Polyhedron(Generator_System& gs, Recycle_Input)
  : Polyhedron(NOT_NECESSARILY_CLOSED,
               check_obj_space_dimension_overflow(gs, NOT_NECESSARILY_CLOSED,
                                                  "NNC_Polyhedron(gs, recycle)",
                                                  "the space dimension of gs "
                                                  "exceeds the maximum allowed "
                                                  "space dimension"),
               Recycle_Input()) {
}

template <typename Interval>
inline
NNC_Polyhedron::NNC_Polyhedron(const Box<Interval>& box, Complexity_Class)
  : Polyhedron(NOT_NECESSARILY_CLOSED,
               check_obj_space_dimension_overflow(box, NOT_NECESSARILY_CLOSED,
                                                  "NNC_Polyhedron(box)",
                                                  "the space dimension of box "
                                                  "exceeds the maximum allowed "
                                                  "space dimension")) {
}

template <typename U>
inline
NNC_Polyhedron::NNC_Polyhedron(const BD_Shape<U>& bd, Complexity_Class)
  : Polyhedron(NOT_NECESSARILY_CLOSED,
               check_space_dimension_overflow(bd.space_dimension(),
                                              NOT_NECESSARILY_CLOSED,
                                              "NNC_Polyhedron(bd)",
                                              "the space dimension of bd "
                                              "exceeds the maximum allowed "
                                              "space dimension"),
               UNIVERSE) {
  add_constraints(bd.constraints());
}

template <typename U>
inline
NNC_Polyhedron::NNC_Polyhedron(const Octagonal_Shape<U>& os, Complexity_Class)
  : Polyhedron(NOT_NECESSARILY_CLOSED,
               check_space_dimension_overflow(os.space_dimension(),
                                              NOT_NECESSARILY_CLOSED,
                                              "NNC_Polyhedron(os)",
                                              "the space dimension of os "
                                              "exceeds the maximum allowed "
                                              "space dimension"),
               UNIVERSE) {
  add_constraints(os.constraints());
}

inline
NNC_Polyhedron::NNC_Polyhedron(const NNC_Polyhedron& y, Complexity_Class)
  : Polyhedron(y) {
}

inline NNC_Polyhedron&
NNC_Polyhedron::operator=(const NNC_Polyhedron& y) {
  Polyhedron::operator=(y);
  return *this;
}

inline NNC_Polyhedron&
NNC_Polyhedron::operator=(const C_Polyhedron& y) {
  NNC_Polyhedron nnc_y(y);
  m_swap(nnc_y);
  return *this;
}

inline bool
NNC_Polyhedron::upper_bound_assign_if_exact(const NNC_Polyhedron& y) {
  return poly_hull_assign_if_exact(y);
}

inline void
NNC_Polyhedron::positive_time_elapse_assign(const Polyhedron& y) {
  Polyhedron::positive_time_elapse_assign_impl(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/NNC_Polyhedron_defs.hh line 266. */

/* Automatically generated from PPL source file ../src/Rational_Box.hh line 1. */
/* Rational_Box class declaration and implementation.
*/


/* Automatically generated from PPL source file ../src/Rational_Box.hh line 29. */

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A box with rational, possibly open boundaries.
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
typedef Box<Rational_Interval> Rational_Box;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/max_space_dimension.hh line 34. */
#include <algorithm>

namespace Parma_Polyhedra_Library {

//! Returns the maximum space dimension this library can handle.
inline dimension_type
max_space_dimension() {
  // Note: we assume that the powerset and the ask-and-tell construction
  // do not limit the space dimension more than their parameters.
  static bool computed = false;
  static dimension_type d = not_a_dimension();
  if (!computed) {
    d = Variable::max_space_dimension();
    d = std::min(d, C_Polyhedron::max_space_dimension());
    d = std::min(d, NNC_Polyhedron::max_space_dimension());
    d = std::min(d, Grid::max_space_dimension());
    // FIXME: what about all other boxes?
    d = std::min(d, Rational_Box::max_space_dimension());
    d = std::min(d, BD_Shape<int8_t>::max_space_dimension());
    d = std::min(d, BD_Shape<int16_t>::max_space_dimension());
    d = std::min(d, BD_Shape<int32_t>::max_space_dimension());
    d = std::min(d, BD_Shape<int64_t>::max_space_dimension());
    d = std::min(d, BD_Shape<float>::max_space_dimension());
    d = std::min(d, BD_Shape<double>::max_space_dimension());
    d = std::min(d, BD_Shape<long double>::max_space_dimension());
    d = std::min(d, BD_Shape<mpz_class>::max_space_dimension());
    d = std::min(d, BD_Shape<mpq_class>::max_space_dimension());
    d = std::min(d, Octagonal_Shape<int8_t>::max_space_dimension());
    d = std::min(d, Octagonal_Shape<int16_t>::max_space_dimension());
    d = std::min(d, Octagonal_Shape<int32_t>::max_space_dimension());
    d = std::min(d, Octagonal_Shape<int64_t>::max_space_dimension());
    d = std::min(d, Octagonal_Shape<float>::max_space_dimension());
    d = std::min(d, Octagonal_Shape<double>::max_space_dimension());
    d = std::min(d, Octagonal_Shape<long double>::max_space_dimension());
    d = std::min(d, Octagonal_Shape<mpz_class>::max_space_dimension());
    d = std::min(d, Octagonal_Shape<mpq_class>::max_space_dimension());
    computed = true;
  }
  return d;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/algorithms.hh line 1. */
/* A collection of useful convex polyhedra algorithms: inline functions.
*/


/* Automatically generated from PPL source file ../src/Pointset_Powerset_defs.hh line 1. */
/* Pointset_Powerset class declaration.
*/


/* Automatically generated from PPL source file ../src/Pointset_Powerset_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename PSET>
class Pointset_Powerset;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Partially_Reduced_Product_defs.hh line 1. */
/* Partially_Reduced_Product class declaration.
*/


/* Automatically generated from PPL source file ../src/Partially_Reduced_Product_defs.hh line 49. */

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Output operator.
/*!
  \relates Parma_Polyhedra_Library::Partially_Reduced_Product
  Writes a textual representation of \p dp on \p s.
*/
template <typename D1, typename D2, typename R>
std::ostream&
operator<<(std::ostream& s, const Partially_Reduced_Product<D1, D2, R>& dp);

} // namespace IO_Operators

//! Swaps \p x with \p y.
/*! \relates Partially_Reduced_Product */
template <typename D1, typename D2, typename R>
void swap(Partially_Reduced_Product<D1, D2, R>& x,
          Partially_Reduced_Product<D1, D2, R>& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if the components of \p x and \p y
  are pairwise equal.

  \relates Partially_Reduced_Product
  Note that \p x and \p y may be dimension-incompatible: in
  those cases, the value <CODE>false</CODE> is returned.
*/
template <typename D1, typename D2, typename R>
bool operator==(const Partially_Reduced_Product<D1, D2, R>& x,
                const Partially_Reduced_Product<D1, D2, R>& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if the components of \p x and \p y
  are not pairwise equal.

  \relates Partially_Reduced_Product
  Note that \p x and \p y may be dimension-incompatible: in
  those cases, the value <CODE>true</CODE> is returned.
*/
template <typename D1, typename D2, typename R>
bool operator!=(const Partially_Reduced_Product<D1, D2, R>& x,
                const Partially_Reduced_Product<D1, D2, R>& y);

} // namespace Parma_Polyhedra_Library


/*! \brief
  This class provides the reduction method for the Smash_Product
  domain.

  \ingroup PPL_CXX_interface
  The reduction classes are used to instantiate the Partially_Reduced_Product
  domain. This class propagates emptiness between its components.
*/
template <typename D1, typename D2>
class Parma_Polyhedra_Library::Smash_Reduction {
public:
  //! Default constructor.
  Smash_Reduction();

  /*! \brief
    The smash reduction operator for propagating emptiness between the
    domain elements \p d1 and \p d2.

    If either of the the domain elements \p d1 or \p d2 is empty
    then the other is also set empty.

    \param d1
    A pointset domain element;

    \param d2
    A pointset domain element;
  */
  void product_reduce(D1& d1, D2& d2);

  //! Destructor.
  ~Smash_Reduction();
};

/*! \brief
  This class provides the reduction method for the Constraints_Product
  domain.

  \ingroup PPL_CXX_interface
  The reduction classes are used to instantiate the Partially_Reduced_Product
  domain. This class adds the constraints defining each of the component
  domains to the other component.
*/
template <typename D1, typename D2>
class Parma_Polyhedra_Library::Constraints_Reduction {
public:
  //! Default constructor.
  Constraints_Reduction();

  /*! \brief
    The constraints reduction operator for sharing constraints between the
    domains.

    The minimized constraint system defining the domain element \p d1
    is added to \p d2 and the minimized constraint system  defining \p d2
    is added to \p d1.
    In each case, the donor domain must provide a constraint system
    in minimal form; this must define a polyhedron in which the
    donor element is contained.
    The recipient domain selects a subset of these constraints
    that it can add to the recipient element.
    For example: if the domain \p D1 is the Grid domain and \p D2
    the NNC Polyhedron domain, then only the equality constraints are copied
    from \p d1 to \p d2 and from \p d2 to \p d1.

    \param d1
    A pointset domain element;

    \param d2
    A pointset domain element;
  */
  void product_reduce(D1& d1, D2& d2);

  //! Destructor.
  ~Constraints_Reduction();
};

/*! \brief
  This class provides the reduction method for the Congruences_Product
  domain.

  \ingroup PPL_CXX_interface
  The reduction classes are used to instantiate the Partially_Reduced_Product
  domain.

  This class uses the minimized congruences defining each of the components.
  For each of the congruences, it checks if the other component
  intersects none, one or more than one hyperplane defined by the congruence
  and adds equalities or emptiness as appropriate; in more detail:
  Letting the components be d1 and d2, then, for each congruence cg
  representing d1:
  - if more than one hyperplane defined by cg intersects
    d2, then d1 and d2 are unchanged;
  - if exactly one hyperplane intersects d2, then d1 and d2 are
    refined with the corresponding equality ;
  - otherwise, d1 and d2 are set to empty.
  Unless d1 and d2 are already empty, the process is repeated where the
  roles of d1 and d2 are reversed.
  If d1 or d2 is empty, then the emptiness is propagated.

*/
template <typename D1, typename D2>
class Parma_Polyhedra_Library::Congruences_Reduction {
public:
  //! Default constructor.
  Congruences_Reduction();

  /*! \brief
    The congruences reduction operator for detect emptiness or any equalities
    implied by each of the congruences defining one of the components
    and the bounds of the other component. It is assumed that the
    components are already constraints reduced.

    The minimized congruence system defining the domain element \p d1
    is used to check if \p d2 intersects none, one or more than one
    of the hyperplanes defined by the congruences: if it intersects none,
    then product is set empty; if it intersects one, then the equality
    defining this hyperplane is added to both components; otherwise,
    the product is unchanged.
    In each case, the donor domain must provide a congruence system
    in minimal form.

    \param d1
    A pointset domain element;

    \param d2
    A pointset domain element;
  */
  void product_reduce(D1& d1, D2& d2);

  //! Destructor.
  ~Congruences_Reduction();
};

/*! \brief
  This class provides the reduction method for the Shape_Preserving_Product
  domain.

  \ingroup PPL_CXX_interface
  The reduction classes are used to instantiate the Partially_Reduced_Product
  domain.

  This reduction method includes the congruences reduction.
  This class uses the minimized constraints defining each of the components.
  For each of the constraints, it checks the frequency and value for the same
  linear expression in the other component. If the constraint does not satisfy
  the implied congruence, the inhomogeneous term is adjusted so that it does.
  Note that, unless the congruences reduction adds equalities, the
  shapes of the domains are unaltered.

*/
template <typename D1, typename D2>
class Parma_Polyhedra_Library::Shape_Preserving_Reduction {
public:
  //! Default constructor.
  Shape_Preserving_Reduction();

  /*! \brief
    The congruences reduction operator for detect emptiness or any equalities
    implied by each of the congruences defining one of the components
    and the bounds of the other component. It is assumed that the
    components are already constraints reduced.

    The minimized congruence system defining the domain element \p d1
    is used to check if \p d2 intersects none, one or more than one
    of the hyperplanes defined by the congruences: if it intersects none,
    then product is set empty; if it intersects one, then the equality
    defining this hyperplane is added to both components; otherwise,
    the product is unchanged.
    In each case, the donor domain must provide a congruence system
    in minimal form.

    \param d1
    A pointset domain element;

    \param d2
    A pointset domain element;
  */
  void product_reduce(D1& d1, D2& d2);

  //! Destructor.
  ~Shape_Preserving_Reduction();
};

/*! \brief
  This class provides the reduction method for the Direct_Product domain.

  \ingroup PPL_CXX_interface
  The reduction classes are used to instantiate the Partially_Reduced_Product
  domain template parameter \p R. This class does no reduction at all.
*/
template <typename D1, typename D2>
class Parma_Polyhedra_Library::No_Reduction {
public:
  //! Default constructor.
  No_Reduction();

  /*! \brief
    The null reduction operator.

    The parameters \p d1 and \p d2 are ignored.
  */
  void product_reduce(D1& d1, D2& d2);

  //! Destructor.
  ~No_Reduction();
};

//! The partially reduced product of two abstractions.
/*! \ingroup PPL_CXX_interface

  \warning
  At present, the supported instantiations for the
  two domain templates \p D1 and \p D2 are the simple pointset domains:
  <CODE>C_Polyhedron</CODE>,
  <CODE>NNC_Polyhedron</CODE>,
  <CODE>Grid</CODE>,
  <CODE>Octagonal_Shape\<T\></CODE>,
  <CODE>BD_Shape\<T\></CODE>,
  <CODE>Box\<T\></CODE>.

  An object of the class <CODE>Partially_Reduced_Product\<D1, D2, R\></CODE>
  represents the (partially reduced) product of two pointset domains \p D1
  and \p D2 where the form of any reduction is defined by the
  reduction class \p R.

  Suppose \f$D_1\f$ and \f$D_2\f$ are two abstract domains
  with concretization functions:
  \f$\fund{\gamma_1}{D_1}{\Rset^n}\f$ and
  \f$\fund{\gamma_2}{D_2}{\Rset^n}\f$, respectively.

  The partially reduced product \f$D = D_1 \times D_2\f$,
  for any reduction class \p R, has a concretization
  \f$\fund{\gamma}{D}{\Rset^n}\f$
  where, if \f$d = (d_1, d_2) \in D\f$
  \f[
    \gamma(d) = \gamma_1(d_1) \inters \gamma_2(d_2).
  \f]

  The operations are defined to be the result of applying the corresponding
  operations on each of the components provided the product is already reduced
  by the reduction method defined by \p R.
  In particular, if \p R is the <CODE>No_Reduction\<D1, D2\></CODE> class,
  then the class <CODE>Partially_Reduced_Product\<D1, D2, R\></CODE> domain
  is the direct product as defined in \ref CC79 "[CC79]".

  How the results on the components are interpreted and
  combined depend on the specific test.
  For example, the test for emptiness will first make sure
  the product is reduced (using the reduction method provided by \p R
  if it is not already known to be reduced) and then test if either component
  is empty; thus, if \p R defines no reduction between its components and
  \f$d = (G, P) \in (\Gset \times \Pset)\f$
  is a direct product in one dimension where \f$G\f$ denotes the set of
  numbers that are integral multiples of 3 while \f$P\f$ denotes the
  set of numbers between 1 and 2, then an operation that tests for
  emptiness should return false.
  However, the test for the universe returns true if and only if the
  test <CODE>is_universe()</CODE> on both components returns true.

  \par
  In all the examples it is assumed that the template \c R is the
  <CODE>No_Reduction\<D1, D2\></CODE> class and that variables
  \c x and \c y are defined (where they are used) as follows:
  \code
  Variable x(0);
  Variable y(1);
  \endcode

  \par Example 1
  The following code builds a direct product of a Grid and NNC Polyhedron,
  corresponding to the positive even integer
  pairs in \f$\Rset^2\f$, given as a system of congruences:
  \code
  Congruence_System cgs;
  cgs.insert((x %= 0) / 2);
  cgs.insert((y %= 0) / 2);
  Partially_Reduced_Product<Grid, NNC_Polyhedron, No_Reduction<D1, D2> >
    dp(cgs);
  dp.add_constraint(x >= 0);
  dp.add_constraint(y >= 0);
  \endcode

  \par Example 2
  The following code builds the same product
  in \f$\Rset^2\f$:
  \code
  Partially_Reduced_Product<Grid, NNC_Polyhedron, No_Reduction<D1, D2> > dp(2);
  dp.add_constraint(x >= 0);
  dp.add_constraint(y >= 0);
  dp.add_congruence((x %= 0) / 2);
  dp.add_congruence((y %= 0) / 2);
  \endcode

  \par Example 3
  The following code will write "dp is empty":
  \code
  Partially_Reduced_Product<Grid, NNC_Polyhedron, No_Reduction<D1, D2> > dp(1);
  dp.add_congruence((x %= 0) / 2);
  dp.add_congruence((x %= 1) / 2);
  if (dp.is_empty())
    cout << "dp is empty." << endl;
  else
    cout << "dp is not empty." << endl;
  \endcode

  \par Example 4
  The following code will write "dp is not empty":
  \code
  Partially_Reduced_Product<Grid, NNC_Polyhedron, No_Reduction<D1, D2> > dp(1);
  dp.add_congruence((x %= 0) / 2);
  dp.add_constraint(x >= 1);
  dp.add_constraint(x <= 1);
  if (dp.is_empty())
    cout << "dp is empty." << endl;
  else
    cout << "dp is not empty." << endl;
  \endcode
*/

template <typename D1, typename D2, typename R>
class Parma_Polyhedra_Library::Partially_Reduced_Product {
public:
  /*! \brief
    Returns the maximum space dimension this product
    can handle.
  */
  static dimension_type max_space_dimension();

  //! Builds an object having the specified properties.
  /*!
    \param num_dimensions
    The number of dimensions of the vector space enclosing the pair;

    \param kind
    Specifies whether a universe or an empty pair has to be built.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space
    dimension.
  */
  explicit Partially_Reduced_Product(dimension_type num_dimensions = 0,
                                     Degenerate_Element kind = UNIVERSE);

  //! Builds a pair, copying a system of congruences.
  /*!
    The pair inherits the space dimension of the congruence system.

    \param cgs
    The system of congruences to be approximated by the pair.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space
    dimension.
  */
  explicit Partially_Reduced_Product(const Congruence_System& cgs);

  //! Builds a pair, recycling a system of congruences.
  /*!
    The pair inherits the space dimension of the congruence system.

    \param cgs
    The system of congruences to be approximates by the pair.
    Its data-structures may be recycled to build the pair.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space
    dimension.
  */
  explicit Partially_Reduced_Product(Congruence_System& cgs);

  //! Builds a pair, copying a system of constraints.
  /*!
    The pair inherits the space dimension of the constraint system.

    \param cs
    The system of constraints to be approximated by the pair.

    \exception std::length_error
    Thrown if \p num_dimensions exceeds the maximum allowed space
    dimension.
  */
  explicit Partially_Reduced_Product(const Constraint_System& cs);

  //! Builds a pair, recycling a system of constraints.
  /*!
    The pair inherits the space dimension of the constraint system.

    \param cs
    The system of constraints to be approximated by the pair.

    \exception std::length_error
    Thrown if the space dimension of \p cs exceeds the maximum allowed
    space dimension.
  */
  explicit Partially_Reduced_Product(Constraint_System& cs);

  //! Builds a product, from a C polyhedron.
  /*!
    Builds a product containing \p ph using algorithms whose
    complexity does not exceed the one specified by \p complexity.
    If \p complexity is \p ANY_COMPLEXITY, then the built product is the
    smallest one containing \p ph.
    The product inherits the space dimension of the polyhedron.

    \param ph
    The polyhedron to be approximated by the product.

    \param complexity
    The complexity that will not be exceeded.

    \exception std::length_error
    Thrown if the space dimension of \p ph exceeds the maximum allowed
    space dimension.
  */
  explicit
  Partially_Reduced_Product(const C_Polyhedron& ph,
                            Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a product, from an NNC polyhedron.
  /*!
    Builds a product containing \p ph using algorithms whose
    complexity does not exceed the one specified by \p complexity.
    If \p complexity is \p ANY_COMPLEXITY, then the built product is the
    smallest one containing \p ph.
    The product inherits the space dimension of the polyhedron.

    \param ph
    The polyhedron to be approximated by the product.

    \param complexity
    The complexity that will not be exceeded.

    \exception std::length_error
    Thrown if the space dimension of \p ph exceeds the maximum allowed
    space dimension.
  */
  explicit
  Partially_Reduced_Product(const NNC_Polyhedron& ph,
                            Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a product, from a grid.
  /*!
    Builds a product containing \p gr.
    The product inherits the space dimension of the grid.

    \param gr
    The grid to be approximated by the product.

    \param complexity
    The complexity is ignored.

    \exception std::length_error
    Thrown if the space dimension of \p gr exceeds the maximum allowed
    space dimension.
  */
  explicit
  Partially_Reduced_Product(const Grid& gr,
                            Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a product out of a box.
  /*!
    Builds a product containing \p box.
    The product inherits the space dimension of the box.

    \param box
    The box representing the pair to be built.

    \param complexity
    The complexity is ignored.

    \exception std::length_error
    Thrown if the space dimension of \p box exceeds the maximum
    allowed space dimension.
  */
  template <typename Interval>
  Partially_Reduced_Product(const Box<Interval>& box,
                            Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a product out of a BD shape.
  /*!
    Builds a product containing \p bd.
    The product inherits the space dimension of the BD shape.

    \param bd
    The BD shape representing the product to be built.

    \param complexity
    The complexity is ignored.

    \exception std::length_error
    Thrown if the space dimension of \p bd exceeds the maximum
    allowed space dimension.
  */
  template <typename U>
  Partially_Reduced_Product(const BD_Shape<U>& bd,
                            Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a product out of an octagonal shape.
  /*!
    Builds a product containing \p os.
    The product inherits the space dimension of the octagonal shape.

    \param os
    The octagonal shape representing the product to be built.

    \param complexity
    The complexity is ignored.

    \exception std::length_error
    Thrown if the space dimension of \p os exceeds the maximum
    allowed space dimension.
  */
  template <typename U>
  Partially_Reduced_Product(const Octagonal_Shape<U>& os,
                            Complexity_Class complexity = ANY_COMPLEXITY);

  //! Ordinary copy constructor.
  Partially_Reduced_Product(const Partially_Reduced_Product& y,
                            Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a conservative, upward approximation of \p y.
  /*!
    The complexity argument is ignored.
  */
  template <typename E1, typename E2, typename S>
  explicit
  Partially_Reduced_Product(const Partially_Reduced_Product<E1, E2, S>& y,
                            Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    The assignment operator.  (\p *this and \p y can be
    dimension-incompatible.)
  */
  Partially_Reduced_Product& operator=(const Partially_Reduced_Product& y);

  //! \name Member Functions that Do Not Modify the Partially_Reduced_Product
  //@{

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  /*! \brief
    Returns the minimum \ref Affine_Independence_and_Affine_Dimension
    "affine dimension"
    (see also \ref Grid_Affine_Dimension "grid affine dimension")
    of the components of \p *this.
  */
  dimension_type affine_dimension() const;

  //! Returns a constant reference to the first of the pair.
  const D1& domain1() const;

  //! Returns a constant reference to the second of the pair.
  const D2& domain2() const;

  //! Returns a system of constraints which approximates \p *this.
  Constraint_System constraints() const;

  /*! \brief
    Returns a system of constraints which approximates \p *this, in
    reduced form.
  */
  Constraint_System minimized_constraints() const;

  //! Returns a system of congruences which approximates \p *this.
  Congruence_System congruences() const;

  /*! \brief
    Returns a system of congruences which approximates \p *this, in
    reduced form.
  */
  Congruence_System minimized_congruences() const;

  //! Returns the relations holding between \p *this and \p c.
  /*
    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible.

    Returns the Poly_Con_Relation \p r for \p *this:
    suppose the first component returns \p r1 and the second \p r2,
    then \p r implies <CODE>is_included()</CODE>
    if and only if one or both of \p r1 and \p r2 imply
    <CODE>is_included()</CODE>;
    \p r implies <CODE>saturates()</CODE>
    if and only if one or both of \p r1 and \p r2 imply
    <CODE>saturates()</CODE>;
    \p r implies <CODE>is_disjoint()</CODE>
    if and only if one or both of \p r1 and \p r2 imply
    <CODE>is_disjoint()</CODE>;
    and \p r implies <CODE>nothing()</CODE>
    if and only if both \p r1 and \p r2 imply
    <CODE>strictly_intersects()</CODE>.
  */
  Poly_Con_Relation relation_with(const Constraint& c) const;

  //! Returns the relations holding between \p *this and \p cg.
  /*
    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are dimension-incompatible.
  */
  Poly_Con_Relation relation_with(const Congruence& cg) const;

  //! Returns the relations holding between \p *this and \p g.
  /*
    \exception std::invalid_argument
    Thrown if \p *this and generator \p g are dimension-incompatible.

    Returns the Poly_Gen_Relation \p r for \p *this:
    suppose the first component returns \p r1 and the second \p r2,
    then \p r = <CODE>subsumes()</CODE>
    if and only if \p r1 = \p r2 = <CODE>subsumes()</CODE>;
    and \p r = <CODE>nothing()</CODE>
    if and only if one or both of \p r1 and \p r2 = <CODE>nothing()</CODE>;
  */
  Poly_Gen_Relation relation_with(const Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if either of the components
    of \p *this are empty.
  */
  bool is_empty() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if both of the components
    of \p *this are the universe.
  */
  bool is_universe() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if both of the components
    of \p *this are topologically closed subsets of the vector space.
  */
  bool is_topologically_closed() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this and \p y are
    componentwise disjoint.

    \exception std::invalid_argument
    Thrown if \p x and \p y are dimension-incompatible.
  */
  bool is_disjoint_from(const Partially_Reduced_Product& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if a component of \p *this
    is discrete.
  */
  bool is_discrete() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if a component of \p *this
    is bounded.
  */
  bool is_bounded() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p var is constrained in
    \p *this.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  bool constrains(Variable var) const;

  //! Returns <CODE>true</CODE> if and only if \p expr is bounded in \p *this.
  /*!
    This method is the same as bounds_from_below.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_above(const Linear_Expression& expr) const;

  //! Returns <CODE>true</CODE> if and only if \p expr is bounded in \p *this.
  /*!
    This method is the same as bounds_from_above.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_below(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty and
    \p expr is bounded from above in \p *this, in which case the
    supremum value is computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if the supremum value can be reached in \p this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded by \p *this,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d and \p
    maximum are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty and
    \p expr is bounded from above in \p *this, in which case the
    supremum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if the supremum value can be reached in \p this.

    \param g
    When maximization succeeds, will be assigned the point or
    closure point where \p expr reaches its supremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded by \p *this,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d, \p maximum
    and \p g are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty and
    \p expr is bounded from below i \p *this, in which case the
    infimum value is computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if the infimum value can be reached in \p this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d
    and \p minimum are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty and
    \p expr is bounded from below in \p *this, in which case the
    infimum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if the infimum value can be reached in \p this.

    \param g
    When minimization succeeds, will be assigned the point or closure
    point where \p expr reaches its infimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d, \p minimum
    and \p point are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if each component of \p *this
    contains the corresponding component of \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool contains(const Partially_Reduced_Product& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if each component of \p *this
    strictly contains the corresponding component of \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool strictly_contains(const Partially_Reduced_Product& y) const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  //@} // Member Functions that Do Not Modify the Partially_Reduced_Product

  //! \name Space Dimension Preserving Member Functions that May Modify the Partially_Reduced_Product
  //@{

  //! Adds constraint \p c to \p *this.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p c are dimension-incompatible.
  */
  void add_constraint(const Constraint& c);

  /*! \brief
    Use the constraint \p c to refine \p *this.

    \param c
    The constraint to be used for refinement.

    \exception std::invalid_argument
    Thrown if \p *this and \p c are dimension-incompatible.
  */
  void refine_with_constraint(const Constraint& c);

  //! Adds a copy of congruence \p cg to \p *this.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are
    dimension-incompatible.
  */
  void add_congruence(const Congruence& cg);

  /*! \brief
    Use the congruence \p cg to refine \p *this.

    \param cg
    The congruence to be used for refinement.

    \exception std::invalid_argument
    Thrown if \p *this and \p cg are dimension-incompatible.
  */
  void refine_with_congruence(const Congruence& cg);

  //! Adds a copy of the congruences in \p cgs to \p *this.
  /*!
    \param cgs
    The congruence system to be added.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible.
  */
  void add_congruences(const Congruence_System& cgs);

  /*! \brief
    Use the congruences in \p cgs to refine \p *this.

    \param  cgs
    The congruences to be used for refinement.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible.
  */
  void refine_with_congruences(const Congruence_System& cgs);

  //! Adds the congruences in \p cgs to *this.
  /*!
    \param cgs
    The congruence system to be added that may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible.

    \warning
    The only assumption that can be made about \p cgs upon successful
    or exceptional return is that it can be safely destroyed.
  */
  void add_recycled_congruences(Congruence_System& cgs);

  //! Adds a copy of the constraint system in \p cs to \p *this.
  /*!
    \param cs
    The constraint system to be added.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible.
  */
  void add_constraints(const Constraint_System& cs);

  /*! \brief
    Use the constraints in \p cs to refine \p *this.

    \param  cs
     The constraints to be used for refinement.

     \exception std::invalid_argument
     Thrown if \p *this and \p cs are dimension-incompatible.
  */
  void refine_with_constraints(const Constraint_System& cs);

  //! Adds the constraint system in \p cs to \p *this.
  /*!
    \param cs
    The constraint system to be added that may be recycled.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are dimension-incompatible.

    \warning
    The only assumption that can be made about \p cs upon successful
    or exceptional return is that it can be safely destroyed.
  */
  void add_recycled_constraints(Constraint_System& cs);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to space dimension \p var, assigning the result to \p *this.

    \param var
    The space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  void unconstrain(Variable var);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to the set of space dimensions \p vars,
    assigning the result to \p *this.

    \param vars
    The set of space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars.
  */
  void unconstrain(const Variables_Set& vars);

  /*! \brief
    Assigns to \p *this the componentwise intersection of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void intersection_assign(const Partially_Reduced_Product& y);

  /*! \brief
    Assigns to \p *this an upper bound of \p *this and \p y
    computed on the corresponding components.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void upper_bound_assign(const Partially_Reduced_Product& y);

  /*! \brief
    Assigns to \p *this an upper bound of \p *this and \p y
    computed on the corresponding components.
    If it is exact on each of the components of \p *this, <CODE>true</CODE>
    is returned, otherwise <CODE>false</CODE> is returned.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool upper_bound_assign_if_exact(const Partially_Reduced_Product& y);

  /*! \brief
    Assigns to \p *this an approximation of the set-theoretic difference
    of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void difference_assign(const Partially_Reduced_Product& y);

  /*! \brief
    Assigns to \p *this the \ref Single_Update_Affine_Functions
    "affine image" of \p
    *this under the function mapping variable \p var to the affine
    expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is assigned;

    \param expr
    The numerator of the affine expression;

    \param denominator
    The denominator of the affine expression (optional argument with
    default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of
    \p *this.

  */
  void affine_image(Variable var,
                    const Linear_Expression& expr,
                    Coefficient_traits::const_reference denominator
                    = Coefficient_one());

  /*! \brief
    Assigns to \p *this the \ref  Single_Update_Affine_Functions
    "affine preimage" of
    \p *this under the function mapping variable \p var to the affine
    expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is substituted;

    \param expr
    The numerator of the affine expression;

    \param denominator
    The denominator of the affine expression (optional argument with
    default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this.
  */
  void affine_preimage(Variable var,
                       const Linear_Expression& expr,
                       Coefficient_traits::const_reference denominator
                         = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym
    (see also \ref Grid_Generalized_Image "generalized affine relation".)

    \param var
    The left hand side variable of the generalized affine relation;

    \param relsym
    The relation symbol;

    \param expr
    The numerator of the right hand side affine expression;

    \param denominator
    The denominator of the right hand side affine expression (optional
    argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void generalized_affine_image(Variable var,
                                Relation_Symbol relsym,
                                const Linear_Expression& expr,
                                Coefficient_traits::const_reference denominator
                                = Coefficient_one());

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym.
   (see also \ref Grid_Generalized_Image "generalized affine relation".)

    \param var
    The left hand side variable of the generalized affine relation;

    \param relsym
    The relation symbol;

    \param expr
    The numerator of the right hand side affine expression;

    \param denominator
    The denominator of the right hand side affine expression (optional
    argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void
  generalized_affine_preimage(Variable var,
                              Relation_Symbol relsym,
                              const Linear_Expression& expr,
                              Coefficient_traits::const_reference denominator
                              = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.
   (see also \ref Grid_Generalized_Image "generalized affine relation".)

    \param lhs
    The left hand side affine expression;

    \param relsym
    The relation symbol;

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void generalized_affine_image(const Linear_Expression& lhs,
                                Relation_Symbol relsym,
                                const Linear_Expression& rhs);

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.
   (see also \ref Grid_Generalized_Image "generalized affine relation".)

    \param lhs
    The left hand side affine expression;

    \param relsym
    The relation symbol;

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void generalized_affine_preimage(const Linear_Expression& lhs,
                                   Relation_Symbol relsym,
                                   const Linear_Expression& rhs);

  /*!
    \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_image(Variable var,
                            const Linear_Expression& lb_expr,
                            const Linear_Expression& ub_expr,
                            Coefficient_traits::const_reference denominator
                            = Coefficient_one());

  /*!
    \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_preimage(Variable var,
                               const Linear_Expression& lb_expr,
                               const Linear_Expression& ub_expr,
                               Coefficient_traits::const_reference denominator
                               = Coefficient_one());

  /*! \brief
    Assigns to \p *this the result of computing the \ref Time_Elapse_Operator
    "time-elapse" between \p *this and \p y.
    (See also \ref Grid_Time_Elapse "time-elapse".)

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void time_elapse_assign(const Partially_Reduced_Product& y);

  //! Assigns to \p *this its topological closure.
  void topological_closure_assign();

  // TODO: Add a way to call other widenings.

  // CHECKME: This may not be a real widening; it depends on the reduction
  //          class R and the widening used.

  /*! \brief
    Assigns to \p *this the result of computing the
    "widening" between \p *this and \p y.

    This widening uses either the congruence or generator systems
    depending on which of the systems describing x and y
    are up to date and minimized.

    \param y
    A product that <EM>must</EM> be contained in \p *this;

    \param tp
    An optional pointer to an unsigned variable storing the number of
    available tokens (to be used when applying the
    \ref Widening_with_Tokens "widening with tokens" delay technique).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void widening_assign(const Partially_Reduced_Product& y,
                       unsigned* tp = NULL);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates for the space dimensions corresponding to \p vars.

    \param vars
    Points with non-integer coordinates for these variables/space-dimensions
    can be discarded.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(const Variables_Set& vars,
                                    Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  //@} // Space Dimension Preserving Member Functions that May Modify [...]

  //! \name Member Functions that May Modify the Dimension of the Vector Space
  //@{

  /*! \brief
    Adds \p m new space dimensions and embeds the components
    of \p *this in the new vector space.

    \param m
    The number of dimensions to add.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the vector
    space to exceed dimension <CODE>max_space_dimension()</CODE>.
 */
  void add_space_dimensions_and_embed(dimension_type m);

  /*! \brief
    Adds \p m new space dimensions and does not embed the components
    in the new vector space.

    \param m
    The number of space dimensions to add.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the
    vector space to exceed dimension <CODE>max_space_dimension()</CODE>.
  */
  void add_space_dimensions_and_project(dimension_type m);

  /*! \brief
    Assigns to the first (resp., second) component of \p *this
    the "concatenation" of the first (resp., second) components
    of \p *this and \p y, taken in this order.
    See also \ref Concatenating_Polyhedra.

    \exception std::length_error
    Thrown if the concatenation would cause the vector space
    to exceed dimension <CODE>max_space_dimension()</CODE>.
  */
  void concatenate_assign(const Partially_Reduced_Product& y);

  //! Removes all the specified dimensions from the vector space.
  /*!
    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be removed.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  /*! \brief
    Removes the higher dimensions of the vector space so that the
    resulting space will have dimension \p new_dimension.

    \exception std::invalid_argument
    Thrown if \p new_dimensions is greater than the space dimension of
    \p *this.
  */
  void remove_higher_space_dimensions(dimension_type new_dimension);

  /*! \brief
    Remaps the dimensions of the vector space according to
    a \ref Mapping_the_Dimensions_of_the_Vector_Space "partial function".

    If \p pfunc maps only some of the dimensions of \p *this then the
    rest will be projected away.

    If the highest dimension mapped to by \p pfunc is higher than the
    highest dimension in \p *this then the number of dimensions in \p
    *this will be increased to the highest dimension mapped to by \p
    pfunc.

    \param pfunc
    The partial function specifying the destiny of each space
    dimension.

    The template class <CODE>Partial_Function</CODE> must provide the following
    methods.
    \code
      bool has_empty_codomain() const
    \endcode
    returns <CODE>true</CODE> if and only if the represented partial
    function has an empty codomain (i.e., it is always undefined).
    The <CODE>has_empty_codomain()</CODE> method will always be called
    before the methods below.  However, if
    <CODE>has_empty_codomain()</CODE> returns <CODE>true</CODE>, none
    of the functions below will be called.
    \code
      dimension_type max_in_codomain() const
    \endcode
    returns the maximum value that belongs to the codomain of the
    partial function.
    The <CODE>max_in_codomain()</CODE> method is called at most once.
    \code
      bool maps(dimension_type i, dimension_type& j) const
    \endcode
    Let \f$f\f$ be the represented function and \f$k\f$ be the value
    of \p i.  If \f$f\f$ is defined in \f$k\f$, then \f$f(k)\f$ is
    assigned to \p j and <CODE>true</CODE> is returned.  If \f$f\f$ is
    undefined in \f$k\f$, then <CODE>false</CODE> is returned.
    This method is called at most \f$n\f$ times, where \f$n\f$ is the
    dimension of the vector space enclosing \p *this.

    The result is undefined if \p pfunc does not encode a partial
    function with the properties described in
    \ref Mapping_the_Dimensions_of_the_Vector_Space
    "specification of the mapping operator".
  */
  template <typename Partial_Function>
  void map_space_dimensions(const Partial_Function& pfunc);

  //! Creates \p m copies of the space dimension corresponding to \p var.
  /*!
    \param var
    The variable corresponding to the space dimension to be replicated;

    \param m
    The number of replicas to be created.

    \exception std::invalid_argument
    Thrown if \p var does not correspond to a dimension of the vector
    space.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the vector
    space to exceed dimension <CODE>max_space_dimension()</CODE>.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    and <CODE>var</CODE> has space dimension \f$k \leq n\f$,
    then the \f$k\f$-th space dimension is
    \ref Expanding_One_Dimension_of_the_Vector_Space_to_Multiple_Dimensions
    "expanded" to \p m new space dimensions
    \f$n\f$, \f$n+1\f$, \f$\dots\f$, \f$n+m-1\f$.
  */
  void expand_space_dimension(Variable var, dimension_type m);

  //! Folds the space dimensions in \p vars into \p dest.
  /*!
    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be folded;

    \param dest
    The variable corresponding to the space dimension that is the
    destination of the folding operation.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p dest or with
    one of the Variable objects contained in \p vars.  Also
    thrown if \p dest is contained in \p vars.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    <CODE>dest</CODE> has space dimension \f$k \leq n\f$,
    \p vars is a set of variables whose maximum space dimension
    is also less than or equal to \f$n\f$, and \p dest is not a member
    of \p vars, then the space dimensions corresponding to
    variables in \p vars are
    \ref Folding_Multiple_Dimensions_of_the_Vector_Space_into_One_Dimension
    "folded" into the \f$k\f$-th space dimension.
  */
  void fold_space_dimensions(const Variables_Set& vars, Variable dest);

  //@} // Member Functions that May Modify the Dimension of the Vector Space

  friend bool operator==<>(const Partially_Reduced_Product<D1, D2, R>& x,
                           const Partially_Reduced_Product<D1, D2, R>& y);

  friend std::ostream&
  Parma_Polyhedra_Library::IO_Operators::
  operator<<<>(std::ostream& s, const Partially_Reduced_Product<D1, D2, R>& dp);

  //! \name Miscellaneous Member Functions
  //@{

  //! Destructor.
  ~Partially_Reduced_Product();

  /*! \brief
    Swaps \p *this with product \p y.  (\p *this and \p y can be
    dimension-incompatible.)
  */
  void m_swap(Partially_Reduced_Product& y);

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

  //! Returns the total size in bytes of the memory occupied by \p *this.
  memory_size_type total_memory_in_bytes() const;

  //! Returns the size in bytes of the memory managed by \p *this.
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns a 32-bit hash code for \p *this.

    If \p x and \p y are such that <CODE>x == y</CODE>,
    then <CODE>x.hash_code() == y.hash_code()</CODE>.
  */
  int32_t hash_code() const;

  //@} // Miscellaneous Member Functions

  //! Reduce.
  /*
    \return
    <CODE>true</CODE> if and only if either of the resulting component
    is strictly contained in the respective original.
  */
  bool reduce() const;

protected:
  //! The type of the first component.
  typedef D1 Domain1;

  //! The type of the second component.
  typedef D2 Domain2;

  //! The first component.
  D1 d1;

  //! The second component.
  D2 d2;

protected:
  //! Clears the reduced flag.
  void clear_reduced_flag() const;

  //! Sets the reduced flag.
  void set_reduced_flag() const;

  //! Return <CODE>true</CODE> if and only if the reduced flag is set.
  bool is_reduced() const;

  /*! \brief
    Flag to record whether the components are reduced with respect
    to each other and the reduction class.
  */
  bool reduced;

private:
  void throw_space_dimension_overflow(const char* method,
                                      const char* reason);
};

namespace Parma_Polyhedra_Library {

/*! \brief
  This class is temporary and will be removed when template typedefs will
  be supported in C++.

  When template typedefs will be supported in C++, what now is verbosely
  denoted by <CODE>Domain_Product\<Domain1, Domain2\>::%Direct_Product</CODE>
  will simply be denoted by <CODE>Direct_Product\<Domain1, Domain2\></CODE>.
*/
template <typename D1, typename D2>
class Domain_Product {
public:
  typedef Partially_Reduced_Product<D1, D2, No_Reduction<D1, D2> >
  Direct_Product;

  typedef Partially_Reduced_Product<D1, D2, Smash_Reduction<D1, D2> >
  Smash_Product;

  typedef Partially_Reduced_Product<D1, D2, Constraints_Reduction<D1, D2> >
  Constraints_Product;

  typedef Partially_Reduced_Product<D1, D2, Congruences_Reduction<D1, D2> >
  Congruences_Product;

  typedef Partially_Reduced_Product<D1, D2, Shape_Preserving_Reduction<D1, D2> >
  Shape_Preserving_Product;
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Partially_Reduced_Product_inlines.hh line 1. */
/* Partially_Reduced_Product class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Partially_Reduced_Product_inlines.hh line 32. */

namespace Parma_Polyhedra_Library {

template <typename D1, typename D2, typename R>
inline dimension_type
Partially_Reduced_Product<D1, D2, R>::max_space_dimension() {
  return (D1::max_space_dimension() < D2::max_space_dimension())
    ? D1::max_space_dimension()
    : D2::max_space_dimension();
}

template <typename D1, typename D2, typename R>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(dimension_type num_dimensions,
                            const Degenerate_Element kind)
  : d1(num_dimensions <= max_space_dimension()
       ? num_dimensions
       : (throw_space_dimension_overflow("Partially_Reduced_Product(n, k)",
                                         "n exceeds the maximum "
                                         "allowed space dimension"),
          num_dimensions),
       kind),
    d2(num_dimensions, kind) {
  set_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(const Congruence_System& cgs)
  : d1(cgs), d2(cgs) {
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(Congruence_System& cgs)
  : d1(const_cast<const Congruence_System&>(cgs)), d2(cgs) {
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(const Constraint_System& cs)
  : d1(cs), d2(cs) {
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(Constraint_System& cs)
  : d1(const_cast<const Constraint_System&>(cs)), d2(cs) {
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(const C_Polyhedron& ph,
                            Complexity_Class complexity)
  : d1(ph, complexity), d2(ph, complexity) {
  set_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(const NNC_Polyhedron& ph,
                            Complexity_Class complexity)
  : d1(ph, complexity), d2(ph, complexity) {
  set_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(const Grid& gr, Complexity_Class)
  : d1(gr), d2(gr) {
  set_reduced_flag();
}

template <typename D1, typename D2, typename R>
template <typename Interval>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(const Box<Interval>& box, Complexity_Class)
  : d1(box), d2(box) {
  set_reduced_flag();
}

template <typename D1, typename D2, typename R>
template <typename U>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(const BD_Shape<U>& bd, Complexity_Class)
  : d1(bd), d2(bd) {
  set_reduced_flag();
}

template <typename D1, typename D2, typename R>
template <typename U>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(const Octagonal_Shape<U>& os, Complexity_Class)
  : d1(os), d2(os) {
  set_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(const Partially_Reduced_Product& y,
                            Complexity_Class)
  : d1(y.d1), d2(y.d2) {
  reduced = y.reduced;
}

template <typename D1, typename D2, typename R>
template <typename E1, typename E2, typename S>
inline
Partially_Reduced_Product<D1, D2, R>
::Partially_Reduced_Product(const Partially_Reduced_Product<E1, E2, S>& y,
                            Complexity_Class complexity)
  : d1(y.space_dimension()), d2(y.space_dimension()), reduced(false) {
  Partially_Reduced_Product<D1, D2, R> pg1(y.domain1(), complexity);
  Partially_Reduced_Product<D1, D2, R> pg2(y.domain2(), complexity);
  pg1.intersection_assign(pg2);
  m_swap(pg1);
}

template <typename D1, typename D2, typename R>
inline
Partially_Reduced_Product<D1, D2, R>::~Partially_Reduced_Product() {
}

template <typename D1, typename D2, typename R>
inline memory_size_type
Partially_Reduced_Product<D1, D2, R>::external_memory_in_bytes() const {
  return d1.external_memory_in_bytes() + d2.external_memory_in_bytes();
}

template <typename D1, typename D2, typename R>
inline memory_size_type
Partially_Reduced_Product<D1, D2, R>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename D1, typename D2, typename R>
inline dimension_type
Partially_Reduced_Product<D1, D2, R>::space_dimension() const {
  PPL_ASSERT(d1.space_dimension() == d2.space_dimension());
  return d1.space_dimension();
}

template <typename D1, typename D2, typename R>
inline dimension_type
Partially_Reduced_Product<D1, D2, R>::affine_dimension() const {
  reduce();
  const dimension_type d1_dim = d1.affine_dimension();
  const dimension_type d2_dim = d2.affine_dimension();
  return std::min(d1_dim, d2_dim);
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::unconstrain(const Variable var) {
  reduce();
  d1.unconstrain(var);
  d2.unconstrain(var);
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>::unconstrain(const Variables_Set& vars) {
  reduce();
  d1.unconstrain(vars);
  d2.unconstrain(vars);
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::intersection_assign(const Partially_Reduced_Product& y) {
  d1.intersection_assign(y.d1);
  d2.intersection_assign(y.d2);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::difference_assign(const Partially_Reduced_Product& y) {
  reduce();
  y.reduce();
  d1.difference_assign(y.d1);
  d2.difference_assign(y.d2);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::upper_bound_assign(const Partially_Reduced_Product& y) {
  reduce();
  y.reduce();
  d1.upper_bound_assign(y.d1);
  d2.upper_bound_assign(y.d2);
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>
::upper_bound_assign_if_exact(const Partially_Reduced_Product& y) {
  reduce();
  y.reduce();
  D1 d1_copy = d1;
  bool ub_exact = d1_copy.upper_bound_assign_if_exact(y.d1);
  if (!ub_exact) {
    return false;
  }
  ub_exact = d2.upper_bound_assign_if_exact(y.d2);
  if (!ub_exact) {
    return false;
  }
  using std::swap;
  swap(d1, d1_copy);
  return true;
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::affine_image(Variable var,
               const Linear_Expression& expr,
               Coefficient_traits::const_reference denominator) {
  d1.affine_image(var, expr, denominator);
  d2.affine_image(var, expr, denominator);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::affine_preimage(Variable var,
                  const Linear_Expression& expr,
                  Coefficient_traits::const_reference denominator) {
  d1.affine_preimage(var, expr, denominator);
  d2.affine_preimage(var, expr, denominator);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::generalized_affine_image(Variable var,
                           const Relation_Symbol relsym,
                           const Linear_Expression& expr,
                           Coefficient_traits::const_reference denominator) {
  d1.generalized_affine_image(var, relsym, expr, denominator);
  d2.generalized_affine_image(var, relsym, expr, denominator);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::generalized_affine_preimage(Variable var,
                              const Relation_Symbol relsym,
                              const Linear_Expression& expr,
                              Coefficient_traits::const_reference denominator) {
  d1.generalized_affine_preimage(var, relsym, expr, denominator);
  d2.generalized_affine_preimage(var, relsym, expr, denominator);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::generalized_affine_image(const Linear_Expression& lhs,
                           const Relation_Symbol relsym,
                           const Linear_Expression& rhs) {
  d1.generalized_affine_image(lhs, relsym, rhs);
  d2.generalized_affine_image(lhs, relsym, rhs);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::generalized_affine_preimage(const Linear_Expression& lhs,
                              const Relation_Symbol relsym,
                              const Linear_Expression& rhs) {
  d1.generalized_affine_preimage(lhs, relsym, rhs);
  d2.generalized_affine_preimage(lhs, relsym, rhs);
  clear_reduced_flag();
}


template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::bounded_affine_image(Variable var,
                       const Linear_Expression& lb_expr,
                       const Linear_Expression& ub_expr,
                       Coefficient_traits::const_reference denominator) {
  d1.bounded_affine_image(var, lb_expr, ub_expr, denominator);
  d2.bounded_affine_image(var, lb_expr, ub_expr, denominator);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::bounded_affine_preimage(Variable var,
                          const Linear_Expression& lb_expr,
                          const Linear_Expression& ub_expr,
                          Coefficient_traits::const_reference denominator) {
  d1.bounded_affine_preimage(var, lb_expr, ub_expr, denominator);
  d2.bounded_affine_preimage(var, lb_expr, ub_expr, denominator);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::time_elapse_assign(const Partially_Reduced_Product& y) {
  reduce();
  y.reduce();
  d1.time_elapse_assign(y.d1);
  d2.time_elapse_assign(y.d2);
  PPL_ASSERT_HEAVY(OK());
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>::topological_closure_assign() {
  d1.topological_closure_assign();
  d2.topological_closure_assign();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>::m_swap(Partially_Reduced_Product& y) {
  using std::swap;
  swap(d1, y.d1);
  swap(d2, y.d2);
  swap(reduced, y.reduced);
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>::add_constraint(const Constraint& c) {
  d1.add_constraint(c);
  d2.add_constraint(c);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>::refine_with_constraint(const Constraint& c) {
  d1.refine_with_constraint(c);
  d2.refine_with_constraint(c);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>::add_congruence(const Congruence& cg) {
  d1.add_congruence(cg);
  d2.add_congruence(cg);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>::refine_with_congruence(const Congruence& cg) {
  d1.refine_with_congruence(cg);
  d2.refine_with_congruence(cg);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::add_constraints(const Constraint_System& cs) {
  d1.add_constraints(cs);
  d2.add_constraints(cs);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::refine_with_constraints(const Constraint_System& cs) {
  d1.refine_with_constraints(cs);
  d2.refine_with_constraints(cs);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::add_congruences(const Congruence_System& cgs) {
  d1.add_congruences(cgs);
  d2.add_congruences(cgs);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::refine_with_congruences(const Congruence_System& cgs) {
  d1.refine_with_congruences(cgs);
  d2.refine_with_congruences(cgs);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::drop_some_non_integer_points(Complexity_Class complexity) {
  reduce();
  d1.drop_some_non_integer_points(complexity);
  d2.drop_some_non_integer_points(complexity);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::drop_some_non_integer_points(const Variables_Set& vars,
                                    Complexity_Class complexity) {
  reduce();
  d1.drop_some_non_integer_points(vars, complexity);
  d2.drop_some_non_integer_points(vars, complexity);
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
inline Partially_Reduced_Product<D1, D2, R>&
Partially_Reduced_Product<D1, D2, R>
::operator=(const Partially_Reduced_Product& y) {
  d1 = y.d1;
  d2 = y.d2;
  reduced = y.reduced;
  return *this;
}

template <typename D1, typename D2, typename R>
inline const D1&
Partially_Reduced_Product<D1, D2, R>::domain1() const {
  reduce();
  return d1;
}

template <typename D1, typename D2, typename R>
inline const D2&
Partially_Reduced_Product<D1, D2, R>::domain2() const {
  reduce();
  return d2;
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>::is_empty() const {
  reduce();
  return d1.is_empty() || d2.is_empty();
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>::is_universe() const {
  return d1.is_universe() && d2.is_universe();
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>::is_topologically_closed() const {
  reduce();
  return d1.is_topologically_closed() && d2.is_topologically_closed();
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>
::is_disjoint_from(const Partially_Reduced_Product& y) const {
  reduce();
  y.reduce();
  return d1.is_disjoint_from(y.d1) || d2.is_disjoint_from(y.d2);
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>::is_discrete() const {
  reduce();
  return d1.is_discrete() || d2.is_discrete();
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>::is_bounded() const {
  reduce();
  return d1.is_bounded() || d2.is_bounded();
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>
::bounds_from_above(const Linear_Expression& expr) const {
  reduce();
  return d1.bounds_from_above(expr) || d2.bounds_from_above(expr);
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>
::bounds_from_below(const Linear_Expression& expr) const {
  reduce();
  return d1.bounds_from_below(expr) || d2.bounds_from_below(expr);
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>::constrains(Variable var) const {
  reduce();
  return d1.constrains(var) || d2.constrains(var);
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::widening_assign(const Partially_Reduced_Product& y,
                  unsigned* tp) {
  // FIXME(0.10.1): In general this is _NOT_ a widening since the reduction
  //        may mean that the sequence does not satisfy the ascending
  //        chain condition.
  //        However, for the direct, smash and constraints product
  //        it may be ok - but this still needs checking.
  reduce();
  y.reduce();
  d1.widening_assign(y.d1, tp);
  d2.widening_assign(y.d2, tp);
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::add_space_dimensions_and_embed(dimension_type m) {
  d1.add_space_dimensions_and_embed(m);
  d2.add_space_dimensions_and_embed(m);
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::add_space_dimensions_and_project(dimension_type m) {
  d1.add_space_dimensions_and_project(m);
  d2.add_space_dimensions_and_project(m);
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::concatenate_assign(const Partially_Reduced_Product& y) {
  d1.concatenate_assign(y.d1);
  d2.concatenate_assign(y.d2);
  if (!is_reduced() || !y.is_reduced()) {
    clear_reduced_flag();
  }
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::remove_space_dimensions(const Variables_Set& vars) {
  d1.remove_space_dimensions(vars);
  d2.remove_space_dimensions(vars);
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::remove_higher_space_dimensions(dimension_type new_dimension) {
  d1.remove_higher_space_dimensions(new_dimension);
  d2.remove_higher_space_dimensions(new_dimension);
}

template <typename D1, typename D2, typename R>
template <typename Partial_Function>
inline void
Partially_Reduced_Product<D1, D2, R>
::map_space_dimensions(const Partial_Function& pfunc) {
  d1.map_space_dimensions(pfunc);
  d2.map_space_dimensions(pfunc);
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::expand_space_dimension(Variable var, dimension_type m) {
  d1.expand_space_dimension(var, m);
  d2.expand_space_dimension(var, m);
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>
::fold_space_dimensions(const Variables_Set& vars,
                        Variable dest) {
  d1.fold_space_dimensions(vars, dest);
  d2.fold_space_dimensions(vars, dest);
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>
::contains(const Partially_Reduced_Product& y) const {
  reduce();
  y.reduce();
  return d1.contains(y.d1) && d2.contains(y.d2);
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>
::strictly_contains(const Partially_Reduced_Product& y) const {
  reduce();
  y.reduce();
  return (d1.contains(y.d1) && d2.strictly_contains(y.d2))
    || (d2.contains(y.d2) && d1.strictly_contains(y.d1));
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>::reduce() const {
  Partially_Reduced_Product& dp
    = const_cast<Partially_Reduced_Product&>(*this);
  if (dp.is_reduced()) {
    return false;
  }
  R r;
  r.product_reduce(dp.d1, dp.d2);
  set_reduced_flag();
  return true;
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>::is_reduced() const {
  return reduced;
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>::clear_reduced_flag() const {
  const_cast<Partially_Reduced_Product&>(*this).reduced = false;
}

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>::set_reduced_flag() const {
  const_cast<Partially_Reduced_Product&>(*this).reduced = true;
}

PPL_OUTPUT_3_PARAM_TEMPLATE_DEFINITIONS(D1, D2, R, Partially_Reduced_Product)

template <typename D1, typename D2, typename R>
inline void
Partially_Reduced_Product<D1, D2, R>::ascii_dump(std::ostream& s) const {
  const char yes = '+';
  const char no = '-';
  s << "Partially_Reduced_Product\n";
  s << (reduced ? yes : no) << "reduced\n";
  s << "Domain 1:\n";
  d1.ascii_dump(s);
  s << "Domain 2:\n";
  d2.ascii_dump(s);
}

template <typename D1, typename D2, typename R>
inline int32_t
Partially_Reduced_Product<D1, D2, R>::hash_code() const {
  return hash_code_from_dimension(space_dimension());
}

/*! \relates Parma_Polyhedra_Library::Partially_Reduced_Product */
template <typename D1, typename D2, typename R>
inline bool
operator==(const Partially_Reduced_Product<D1, D2, R>& x,
           const Partially_Reduced_Product<D1, D2, R>& y) {
  x.reduce();
  y.reduce();
  return x.d1 == y.d1 && x.d2 == y.d2;
}

/*! \relates Parma_Polyhedra_Library::Partially_Reduced_Product */
template <typename D1, typename D2, typename R>
inline bool
operator!=(const Partially_Reduced_Product<D1, D2, R>& x,
           const Partially_Reduced_Product<D1, D2, R>& y) {
  return !(x == y);
}

/*! \relates Parma_Polyhedra_Library::Partially_Reduced_Product */
template <typename D1, typename D2, typename R>
inline std::ostream&
IO_Operators::operator<<(std::ostream& s,
                         const Partially_Reduced_Product<D1, D2, R>& dp) {
  return s << "Domain 1:\n"
           << dp.d1
           << "Domain 2:\n"
           << dp.d2;
}

} // namespace Parma_Polyhedra_Library

namespace Parma_Polyhedra_Library {

template <typename D1, typename D2>
inline
No_Reduction<D1, D2>::No_Reduction() {
}

template <typename D1, typename D2>
void No_Reduction<D1, D2>::product_reduce(D1&, D2&) {
}

template <typename D1, typename D2>
inline
No_Reduction<D1, D2>::~No_Reduction() {
}

template <typename D1, typename D2>
inline
Smash_Reduction<D1, D2>::Smash_Reduction() {
}

template <typename D1, typename D2>
inline
Smash_Reduction<D1, D2>::~Smash_Reduction() {
}

template <typename D1, typename D2>
inline
Constraints_Reduction<D1, D2>::Constraints_Reduction() {
}

template <typename D1, typename D2>
inline
Constraints_Reduction<D1, D2>::~Constraints_Reduction() {
}

template <typename D1, typename D2>
inline
Congruences_Reduction<D1, D2>::Congruences_Reduction() {
}

template <typename D1, typename D2>
inline
Congruences_Reduction<D1, D2>::~Congruences_Reduction() {
}

template <typename D1, typename D2>
inline
Shape_Preserving_Reduction<D1, D2>::Shape_Preserving_Reduction() {
}

template <typename D1, typename D2>
inline
Shape_Preserving_Reduction<D1, D2>::~Shape_Preserving_Reduction() {
}

/*! \relates Partially_Reduced_Product */
template <typename D1, typename D2, typename R>
inline void
swap(Partially_Reduced_Product<D1, D2, R>& x,
     Partially_Reduced_Product<D1, D2, R>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Partially_Reduced_Product_templates.hh line 1. */
/* Partially_Reduced_Product class implementation:
   non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/Partially_Reduced_Product_templates.hh line 31. */
#include <algorithm>
#include <deque>

namespace Parma_Polyhedra_Library {

template <typename D1, typename D2, typename R>
void
Partially_Reduced_Product<D1, D2, R>
::throw_space_dimension_overflow(const char* method,
                                 const char* reason) {
  std::ostringstream s;
  s << "PPL::Partially_Reduced_Product::" << method << ":" << std::endl
    << reason << ".";
  throw std::length_error(s.str());
}

template <typename D1, typename D2, typename R>
Constraint_System
Partially_Reduced_Product<D1, D2, R>::constraints() const {
  reduce();
  Constraint_System cs = d2.constraints();
  const Constraint_System& cs1 = d1.constraints();
  for (Constraint_System::const_iterator i = cs1.begin(),
         cs_end = cs1.end(); i != cs_end; ++i) {
    cs.insert(*i);
  }
  return cs;
}

template <typename D1, typename D2, typename R>
Constraint_System
Partially_Reduced_Product<D1, D2, R>::minimized_constraints() const {
  reduce();
  Constraint_System cs = d2.constraints();
  const Constraint_System& cs1 = d1.constraints();
  for (Constraint_System::const_iterator i = cs1.begin(),
         cs_end = cs1.end(); i != cs_end; ++i) {
    cs.insert(*i);
  }
  if (cs.has_strict_inequalities()) {
    NNC_Polyhedron ph(cs);
    return ph.minimized_constraints();
  }
  else {
    C_Polyhedron ph(cs);
    return ph.minimized_constraints();
  }
}

template <typename D1, typename D2, typename R>
Congruence_System
Partially_Reduced_Product<D1, D2, R>::congruences() const {
  reduce();
  Congruence_System cgs = d2.congruences();
  const Congruence_System& cgs1 = d1.congruences();
  for (Congruence_System::const_iterator i = cgs1.begin(),
         cgs_end = cgs1.end(); i != cgs_end; ++i) {
    cgs.insert(*i);
  }
  return cgs;
}

template <typename D1, typename D2, typename R>
Congruence_System
Partially_Reduced_Product<D1, D2, R>::minimized_congruences() const {
  reduce();
  Congruence_System cgs = d2.congruences();
  const Congruence_System& cgs1 = d1.congruences();
  for (Congruence_System::const_iterator i = cgs1.begin(),
         cgs_end = cgs1.end(); i != cgs_end; ++i) {
    cgs.insert(*i);
  }
  Grid gr(cgs);
  return gr.minimized_congruences();
}

template <typename D1, typename D2, typename R>
void
Partially_Reduced_Product<D1, D2, R>
::add_recycled_constraints(Constraint_System& cs) {
  if (d1.can_recycle_constraint_systems()) {
    d2.refine_with_constraints(cs);
    d1.add_recycled_constraints(cs);
  }
  else {
    if (d2.can_recycle_constraint_systems()) {
      d1.refine_with_constraints(cs);
      d2.add_recycled_constraints(cs);
    }
    else {
      d1.add_constraints(cs);
      d2.add_constraints(cs);
    }
  }
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
void
Partially_Reduced_Product<D1, D2, R>
::add_recycled_congruences(Congruence_System& cgs) {
  if (d1.can_recycle_congruence_systems()) {
    d2.refine_with_congruences(cgs);
    d1.add_recycled_congruences(cgs);
  }
  else {
    if (d2.can_recycle_congruence_systems()) {
      d1.refine_with_congruences(cgs);
      d2.add_recycled_congruences(cgs);
    }
    else {
      d1.add_congruences(cgs);
      d2.add_congruences(cgs);
    }
  }
  clear_reduced_flag();
}

template <typename D1, typename D2, typename R>
Poly_Gen_Relation
Partially_Reduced_Product<D1, D2, R>
::relation_with(const Generator& g) const {
  reduce();
  if (Poly_Gen_Relation::nothing() == d1.relation_with(g)
      || Poly_Gen_Relation::nothing() == d2.relation_with(g)) {
    return Poly_Gen_Relation::nothing();
  }
  else {
    return Poly_Gen_Relation::subsumes();
  }
}

template <typename D1, typename D2, typename R>
Poly_Con_Relation
Partially_Reduced_Product<D1, D2, R>
::relation_with(const Constraint& c) const {
  reduce();
  Poly_Con_Relation relation1 = d1.relation_with(c);
  Poly_Con_Relation relation2 = d2.relation_with(c);

  Poly_Con_Relation result = Poly_Con_Relation::nothing();

  if (relation1.implies(Poly_Con_Relation::is_included())) {
    result = result && Poly_Con_Relation::is_included();
  }
  else if (relation2.implies(Poly_Con_Relation::is_included())) {
    result = result && Poly_Con_Relation::is_included();
  }
  if (relation1.implies(Poly_Con_Relation::saturates())) {
    result = result && Poly_Con_Relation::saturates();
  }
  else if (relation2.implies(Poly_Con_Relation::saturates())) {
    result = result && Poly_Con_Relation::saturates();
  }
  if (relation1.implies(Poly_Con_Relation::is_disjoint())) {
    result = result && Poly_Con_Relation::is_disjoint();
  }
  else if (relation2.implies(Poly_Con_Relation::is_disjoint())) {
    result = result && Poly_Con_Relation::is_disjoint();
  }

  return result;
}

template <typename D1, typename D2, typename R>
Poly_Con_Relation
Partially_Reduced_Product<D1, D2, R>
::relation_with(const Congruence& cg) const {
  reduce();
  Poly_Con_Relation relation1 = d1.relation_with(cg);
  Poly_Con_Relation relation2 = d2.relation_with(cg);

  Poly_Con_Relation result = Poly_Con_Relation::nothing();

  if (relation1.implies(Poly_Con_Relation::is_included())) {
    result = result && Poly_Con_Relation::is_included();
  }
  else if (relation2.implies(Poly_Con_Relation::is_included())) {
    result = result && Poly_Con_Relation::is_included();
  }
  if (relation1.implies(Poly_Con_Relation::saturates())) {
    result = result && Poly_Con_Relation::saturates();
  }
  else if (relation2.implies(Poly_Con_Relation::saturates())) {
    result = result && Poly_Con_Relation::saturates();
  }
  if (relation1.implies(Poly_Con_Relation::is_disjoint())) {
    result = result && Poly_Con_Relation::is_disjoint();
  }
  else if (relation2.implies(Poly_Con_Relation::is_disjoint())) {
    result = result && Poly_Con_Relation::is_disjoint();
  }

  return result;
}

template <typename D1, typename D2, typename R>
bool
Partially_Reduced_Product<D1, D2, R>
::maximize(const Linear_Expression& expr,
           Coefficient& sup_n,
           Coefficient& sup_d,
           bool& maximum) const {
  reduce();

  if (is_empty()) {
    return false;
  }

  PPL_DIRTY_TEMP_COEFFICIENT(sup1_n);
  PPL_DIRTY_TEMP_COEFFICIENT(sup1_d);
  PPL_DIRTY_TEMP_COEFFICIENT(sup2_n);
  PPL_DIRTY_TEMP_COEFFICIENT(sup2_d);
  bool maximum1;
  bool maximum2;
  bool r1 = d1.maximize(expr, sup1_n, sup1_d, maximum1);
  bool r2 = d2.maximize(expr, sup2_n, sup2_d, maximum2);
  // If neither is bounded from above, return false.
  if (!r1 && !r2) {
    return false;
  }
  // If only d2 is bounded from above, then use the values for d2.
  if (!r1) {
    sup_n = sup2_n;
    sup_d = sup2_d;
    maximum = maximum2;
    return true;
  }
  // If only d1 is bounded from above, then use the values for d1.
  if (!r2) {
    sup_n = sup1_n;
    sup_d = sup1_d;
    maximum = maximum1;
    return true;
  }
  // If both d1 and d2 are bounded from above, then use the minimum values.
  if (sup2_d * sup1_n >= sup1_d * sup2_n) {
    sup_n = sup1_n;
    sup_d = sup1_d;
    maximum = maximum1;
  }
  else {
    sup_n = sup2_n;
    sup_d = sup2_d;
    maximum = maximum2;
  }
  return true;
}

template <typename D1, typename D2, typename R>
bool
Partially_Reduced_Product<D1, D2, R>
::minimize(const Linear_Expression& expr,
           Coefficient& inf_n,
           Coefficient& inf_d,
           bool& minimum) const {
  reduce();

  if (is_empty()) {
    return false;
  }
  PPL_ASSERT(reduced);

  PPL_DIRTY_TEMP_COEFFICIENT(inf1_n);
  PPL_DIRTY_TEMP_COEFFICIENT(inf1_d);
  PPL_DIRTY_TEMP_COEFFICIENT(inf2_n);
  PPL_DIRTY_TEMP_COEFFICIENT(inf2_d);
  bool minimum1;
  bool minimum2;
  bool r1 = d1.minimize(expr, inf1_n, inf1_d, minimum1);
  bool r2 = d2.minimize(expr, inf2_n, inf2_d, minimum2);
  // If neither is bounded from below, return false.
  if (!r1 && !r2) {
    return false;
  }
  // If only d2 is bounded from below, then use the values for d2.
  if (!r1) {
    inf_n = inf2_n;
    inf_d = inf2_d;
    minimum = minimum2;
    return true;
  }
  // If only d1 is bounded from below, then use the values for d1.
  if (!r2) {
    inf_n = inf1_n;
    inf_d = inf1_d;
    minimum = minimum1;
    return true;
  }
  // If both d1 and d2 are bounded from below, then use the minimum values.
  if (inf2_d * inf1_n <= inf1_d * inf2_n) {
    inf_n = inf1_n;
    inf_d = inf1_d;
    minimum = minimum1;
  }
  else {
    inf_n = inf2_n;
    inf_d = inf2_d;
    minimum = minimum2;
  }
  return true;
}

template <typename D1, typename D2, typename R>
bool
Partially_Reduced_Product<D1, D2, R>
::maximize(const Linear_Expression& expr,
           Coefficient& sup_n,
           Coefficient& sup_d,
           bool& maximum,
           Generator& g) const {
  reduce();

  if (is_empty()) {
    return false;
  }
  PPL_ASSERT(reduced);

  PPL_DIRTY_TEMP_COEFFICIENT(sup1_n);
  PPL_DIRTY_TEMP_COEFFICIENT(sup1_d);
  PPL_DIRTY_TEMP_COEFFICIENT(sup2_n);
  PPL_DIRTY_TEMP_COEFFICIENT(sup2_d);
  bool maximum1;
  bool maximum2;
  Generator g1(point());
  Generator g2(point());
  bool r1 = d1.maximize(expr, sup1_n, sup1_d, maximum1, g1);
  bool r2 = d2.maximize(expr, sup2_n, sup2_d, maximum2, g2);
  // If neither is bounded from above, return false.
  if (!r1 && !r2) {
    return false;
  }
  // If only d2 is bounded from above, then use the values for d2.
  if (!r1) {
    sup_n = sup2_n;
    sup_d = sup2_d;
    maximum = maximum2;
    g = g2;
    return true;
  }
  // If only d1 is bounded from above, then use the values for d1.
  if (!r2) {
    sup_n = sup1_n;
    sup_d = sup1_d;
    maximum = maximum1;
    g = g1;
    return true;
  }
  // If both d1 and d2 are bounded from above, then use the minimum values.
  if (sup2_d * sup1_n >= sup1_d * sup2_n) {
    sup_n = sup1_n;
    sup_d = sup1_d;
    maximum = maximum1;
    g = g1;
  }
  else {
    sup_n = sup2_n;
    sup_d = sup2_d;
    maximum = maximum2;
    g = g2;
  }
  return true;
}

template <typename D1, typename D2, typename R>
bool
Partially_Reduced_Product<D1, D2, R>
::minimize(const Linear_Expression& expr,
           Coefficient& inf_n,
           Coefficient& inf_d,
           bool& minimum,
           Generator& g) const {
  reduce();

  if (is_empty()) {
    return false;
  }
  PPL_ASSERT(reduced);

  PPL_DIRTY_TEMP_COEFFICIENT(inf1_n);
  PPL_DIRTY_TEMP_COEFFICIENT(inf1_d);
  PPL_DIRTY_TEMP_COEFFICIENT(inf2_n);
  PPL_DIRTY_TEMP_COEFFICIENT(inf2_d);
  bool minimum1;
  bool minimum2;
  Generator g1(point());
  Generator g2(point());
  bool r1 = d1.minimize(expr, inf1_n, inf1_d, minimum1, g1);
  bool r2 = d2.minimize(expr, inf2_n, inf2_d, minimum2, g2);
  // If neither is bounded from below, return false.
  if (!r1 && !r2) {
    return false;
  }
  // If only d2 is bounded from below, then use the values for d2.
  if (!r1) {
    inf_n = inf2_n;
    inf_d = inf2_d;
    minimum = minimum2;
    g = g2;
    return true;
  }
  // If only d1 is bounded from below, then use the values for d1.
  if (!r2) {
    inf_n = inf1_n;
    inf_d = inf1_d;
    minimum = minimum1;
    g = g1;
    return true;
  }
  // If both d1 and d2 are bounded from below, then use the minimum values.
  if (inf2_d * inf1_n <= inf1_d * inf2_n) {
    inf_n = inf1_n;
    inf_d = inf1_d;
    minimum = minimum1;
    g = g1;
  }
  else {
    inf_n = inf2_n;
    inf_d = inf2_d;
    minimum = minimum2;
    g = g2;
  }
  return true;
}

template <typename D1, typename D2, typename R>
inline bool
Partially_Reduced_Product<D1, D2, R>::OK() const {
  if (reduced) {
    Partially_Reduced_Product<D1, D2, R> dp1 = *this;
    Partially_Reduced_Product<D1, D2, R> dp2 = *this;
    /* Force dp1 reduction */
    dp1.clear_reduced_flag();
    dp1.reduce();
    if (dp1 != dp2) {
      return false;
    }
  }
  return d1.OK() && d2.OK();
}

template <typename D1, typename D2, typename R>
bool
Partially_Reduced_Product<D1, D2, R>::ascii_load(std::istream& s) {
  const char yes = '+';
  const char no = '-';
  std::string str;
  if (!(s >> str) || str != "Partially_Reduced_Product") {
    return false;
  }
  if (!(s >> str)
      || (str[0] != yes && str[0] != no)
      || str.substr(1) != "reduced") {
    return false;
  }
  reduced = (str[0] == yes);
  if (!(s >> str) || str != "Domain") {
    return false;
  }
  if (!(s >> str) || str != "1:") {
    return false;
  }
  if (!d1.ascii_load(s)) {
    return false;
  }
  if (!(s >> str) || str != "Domain") {
    return false;
  }
  if (!(s >> str) || str != "2:") {
    return false;
  }
  return d2.ascii_load(s);
}

template <typename D1, typename D2>
void Smash_Reduction<D1, D2>::product_reduce(D1& d1, D2& d2) {
  using std::swap;
  if (d2.is_empty()) {
    if (!d1.is_empty()) {
      D1 new_d1(d1.space_dimension(), EMPTY);
      swap(d1, new_d1);
    }
  }
  else if (d1.is_empty()) {
    D2 new_d2(d2.space_dimension(), EMPTY);
    swap(d2, new_d2);
  }
}

template <typename D1, typename D2>
void Constraints_Reduction<D1, D2>::product_reduce(D1& d1, D2& d2) {
  if (d1.is_empty() || d2.is_empty()) {
    // If one of the components is empty, do the smash reduction and return.
    Parma_Polyhedra_Library::Smash_Reduction<D1, D2> sr;
    sr.product_reduce(d1, d2);
    return;
  }
  else {
    using std::swap;
    dimension_type space_dim = d1.space_dimension();
    d1.refine_with_constraints(d2.minimized_constraints());
    if (d1.is_empty()) {
      D2 new_d2(space_dim, EMPTY);
      swap(d2, new_d2);
      return;
    }
    d2.refine_with_constraints(d1.minimized_constraints());
    if (d2.is_empty()) {
      D1 new_d1(space_dim, EMPTY);
      swap(d1, new_d1);
    }
  }
}

/* Auxiliary procedure for the Congruences_Reduction() method.
   If more than one hyperplane defined by congruence cg intersect
   d2, then d1 and d2 are unchanged; if exactly one intersects d2, then
   the corresponding equality is added to d1 and d2;
   otherwise d1 and d2 are set empty. */
template <typename D1, typename D2>
bool shrink_to_congruence_no_check(D1& d1, D2& d2, const Congruence& cg) {
  // It is assumed that cg is a proper congruence.
  PPL_ASSERT(cg.modulus() != 0);
  // It is assumed that cg is satisfied by all points in d1.
  PPL_ASSERT(d1.relation_with(cg) == Poly_Con_Relation::is_included());

  Linear_Expression e(cg.expression());

  // Find the maximum and minimum bounds for the domain element d with the
  // linear expression e.
  PPL_DIRTY_TEMP_COEFFICIENT(max_numer);
  PPL_DIRTY_TEMP_COEFFICIENT(max_denom);
  bool max_included;
  PPL_DIRTY_TEMP_COEFFICIENT(min_numer);
  PPL_DIRTY_TEMP_COEFFICIENT(min_denom);
  if (d2.maximize(e, max_numer, max_denom, max_included)) {
    bool min_included;
    if (d2.minimize(e, min_numer, min_denom, min_included)) {
      // Adjust values to allow for the denominators max_denom and min_denom.
      max_numer *= min_denom;
      min_numer *= max_denom;
      PPL_DIRTY_TEMP_COEFFICIENT(denom);
      PPL_DIRTY_TEMP_COEFFICIENT(mod);
      denom = max_denom * min_denom;
      mod = cg.modulus() * denom;
      // If the difference between the maximum and minimum bounds is more than
      // twice the modulus, then there will be two neighboring hyperplanes
      // defined by cg that are intersected by the domain element d;
      // there is no possible reduction in this case.
      PPL_DIRTY_TEMP_COEFFICIENT(mod2);
      mod2 = 2 * mod;
      if (max_numer - min_numer < mod2
          || (max_numer - min_numer == mod2 && (!max_included || !min_included)))
        {
          PPL_DIRTY_TEMP_COEFFICIENT(shrink_amount);
          PPL_DIRTY_TEMP_COEFFICIENT(max_decreased);
          PPL_DIRTY_TEMP_COEFFICIENT(min_increased);
          // Find the amount by which the maximum value may be decreased.
          shrink_amount = max_numer % mod;
          if (!max_included && shrink_amount == 0) {
            shrink_amount = mod;
          }
          if (shrink_amount < 0) {
            shrink_amount += mod;
          }
          max_decreased = max_numer - shrink_amount;
          // Find the amount by which the minimum value may be increased.
          shrink_amount = min_numer % mod;
          if (!min_included && shrink_amount == 0) {
            shrink_amount = - mod;
          }
          if (shrink_amount > 0) {
            shrink_amount -= mod;
          }
          min_increased = min_numer - shrink_amount;
          if (max_decreased == min_increased) {
            // The domain element d2 intersects exactly one hyperplane
            // defined by cg, so add the equality to d1 and d2.
            Constraint new_c(denom * e == min_increased);
            d1.refine_with_constraint(new_c);
            d2.refine_with_constraint(new_c);
            return true;
          }
          else {
            if (max_decreased < min_increased) {
              using std::swap;
              // In this case, d intersects no hyperplanes defined by cg,
              // so set d to empty and return false.
              D1 new_d1(d1.space_dimension(), EMPTY);
              swap(d1, new_d1);
              D2 new_d2(d2.space_dimension(), EMPTY);
              swap(d2, new_d2);
              return false;
            }
          }
        }
    }
  }
  return true;
}

template <typename D1, typename D2>
void
Congruences_Reduction<D1, D2>::product_reduce(D1& d1, D2& d2) {
  if (d1.is_empty() || d2.is_empty()) {
    // If one of the components is empty, do the smash reduction and return.
    Parma_Polyhedra_Library::Smash_Reduction<D1, D2> sr;
    sr.product_reduce(d1, d2);
    return;
  }
  // Use the congruences representing d1 to shrink both components.
  const Congruence_System cgs1 = d1.minimized_congruences();
  for (Congruence_System::const_iterator i = cgs1.begin(),
         cgs_end = cgs1.end(); i != cgs_end; ++i) {
    const Congruence& cg1 = *i;
    if (cg1.is_equality()) {
      d2.refine_with_congruence(cg1);
    }
    else {
      if (!Parma_Polyhedra_Library::
          shrink_to_congruence_no_check(d1, d2, cg1)) {
        // The product is empty.
        return;
      }
    }
  }
  // Use the congruences representing d2 to shrink both components.
  const Congruence_System cgs2 = d2.minimized_congruences();
  for (Congruence_System::const_iterator i = cgs2.begin(),
         cgs_end = cgs2.end(); i != cgs_end; ++i) {
    const Congruence& cg2 = *i;
    if (cg2.is_equality()) {
      d1.refine_with_congruence(cg2);
    }
    else {
      if (!Parma_Polyhedra_Library::
          shrink_to_congruence_no_check(d2, d1, cg2)) {
        // The product is empty.
        return;
      }
    }
  }
}

template <typename D1, typename D2>
void
Shape_Preserving_Reduction<D1, D2>::product_reduce(D1& d1, D2& d2) {
  // First do the congruences reduction.
  Parma_Polyhedra_Library::Congruences_Reduction<D1, D2> cgr;
  cgr.product_reduce(d1, d2);
  if (d1.is_empty()) {
    return;
  }

  PPL_DIRTY_TEMP_COEFFICIENT(freq_n);
  PPL_DIRTY_TEMP_COEFFICIENT(freq_d);
  PPL_DIRTY_TEMP_COEFFICIENT(val_n);
  PPL_DIRTY_TEMP_COEFFICIENT(val_d);

  // Use the constraints representing d2.
  Constraint_System cs = d2.minimized_constraints();
  Constraint_System refining_cs;
  for (Constraint_System::const_iterator i = cs.begin(),
         cs_end = cs.end(); i != cs_end; ++i) {
    const Constraint& c = *i;
    if (c.is_equality()) {
      continue;
    }
    // Check the frequency and value of the linear expression for
    // the constraint `c'.
    Linear_Expression le(c.expression());
    if (!d1.frequency(le, freq_n, freq_d, val_n, val_d)) {
      // Nothing to do.
      continue;
    }
    if (val_n == 0) {
      // Nothing to do.
      continue;
    }
    // Adjust the value of the inhomogeneous term to satisfy
    // the implied congruence.
    if (val_n < 0) {
      val_n = val_n*freq_d + val_d*freq_n;
      val_d *= freq_d;
    }
    le *= val_d;
    le -= val_n;
    refining_cs.insert(le >= 0);
  }
  d2.refine_with_constraints(refining_cs);

  // Use the constraints representing d1.
  cs = d1.minimized_constraints();
  refining_cs.clear();
  for (Constraint_System::const_iterator i = cs.begin(),
         cs_end = cs.end(); i != cs_end; ++i) {
    const Constraint& c = *i;
    if (c.is_equality()) {
      // Equalities already shared.
      continue;
    }
    // Check the frequency and value of the linear expression for
    // the constraint `c'.
    Linear_Expression le(c.expression());
    if (!d2.frequency(le, freq_n, freq_d, val_n, val_d)) {
      // Nothing to do.
      continue;
    }
    if (val_n == 0) {
      // Nothing to do.
      continue;
    }
    // Adjust the value of the inhomogeneous term to satisfy
    // the implied congruence.
    if (val_n < 0) {
      val_n = val_n*freq_d + val_d*freq_n;
      val_d *= freq_d;
    }
    le *= val_d;
    le -= val_n;
    refining_cs.insert(le >= 0);
  }
  d1.refine_with_constraints(refining_cs);

  // The reduction may have introduced additional equalities
  // so these must be shared with the other component.
  Parma_Polyhedra_Library::Constraints_Reduction<D1, D2> cr;
  cr.product_reduce(d1, d2);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Partially_Reduced_Product_defs.hh line 1688. */

/* Automatically generated from PPL source file ../src/Determinate_defs.hh line 1. */
/* Determinate class declaration.
*/


/* Automatically generated from PPL source file ../src/Determinate_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename PSET>
class Determinate;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Determinate_defs.hh line 32. */
#include <iosfwd>

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Determinate */
template <typename PSET>
void swap(Determinate<PSET>& x, Determinate<PSET>& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if \p x and \p y are the same
  COW-wrapped pointset.

  \relates Determinate
*/
template <typename PSET>
bool operator==(const Determinate<PSET>& x, const Determinate<PSET>& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if \p x and \p y are different
  COW-wrapped pointsets.

  \relates Determinate
*/
template <typename PSET>
bool operator!=(const Determinate<PSET>& x, const Determinate<PSET>& y);

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Determinate */
template <typename PSET>
std::ostream&
operator<<(std::ostream&, const Determinate<PSET>&);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library

/*! \brief
  A wrapper for PPL pointsets, providing them with a
  <EM>determinate constraint system</EM> interface, as defined
  in \ref Bag98 "[Bag98]".

  The implementation uses a copy-on-write optimization, making the
  class suitable for constructions, like the <EM>finite powerset</EM>
  and <EM>ask-and-tell</EM> of \ref Bag98 "[Bag98]", that are likely
  to perform many copies.

  \ingroup PPL_CXX_interface
*/
template <typename PSET>
class Parma_Polyhedra_Library::Determinate {
public:
  //! \name Constructors and Destructor
  //@{

  /*! \brief
    Constructs a COW-wrapped object corresponding to the pointset \p pset.
  */
  Determinate(const PSET& pset);

  /*! \brief
    Constructs a COW-wrapped object corresponding to the pointset
    defined by \p cs.
  */
  Determinate(const Constraint_System& cs);

  /*! \brief
    Constructs a COW-wrapped object corresponding to the pointset
    defined by \p cgs.
  */
  Determinate(const Congruence_System& cgs);

  //! Copy constructor.
  Determinate(const Determinate& y);

  //! Destructor.
  ~Determinate();

  //@} // Constructors and Destructor

  //! \name Member Functions that Do Not Modify the Domain Element
  //@{

  //! Returns a const reference to the embedded pointset.
  const PSET& pointset() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this embeds the universe
    element \p PSET.
  */
  bool is_top() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this embeds the empty
    element of \p PSET.
  */
  bool is_bottom() const;

  //! Returns <CODE>true</CODE> if and only if \p *this entails \p y.
  bool definitely_entails(const Determinate& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this and \p y
    are definitely equivalent.
  */
  bool is_definitely_equivalent_to(const Determinate& y) const;

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  /*! \brief
    Returns a lower bound to the size in bytes of the memory
    managed by \p *this.
  */
  memory_size_type external_memory_in_bytes() const;

  /*!
    Returns <CODE>true</CODE> if and only if this domain
    has a nontrivial weakening operator.
  */
  static bool has_nontrivial_weakening();

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  //@} // Member Functions that Do Not Modify the Domain Element


  //! \name Member Functions that May Modify the Domain Element
  //@{

  //! Assigns to \p *this the upper bound of \p *this and \p y.
  void upper_bound_assign(const Determinate& y);

  //! Assigns to \p *this the meet of \p *this and \p y.
  void meet_assign(const Determinate& y);

  //! Assigns to \p *this the result of weakening \p *this with \p y.
  void weakening_assign(const Determinate& y);

  /*! \brief
    Assigns to \p *this the \ref Concatenating_Polyhedra "concatenation"
    of \p *this and \p y, taken in this order.
  */
  void concatenate_assign(const Determinate& y);

  //! Returns a reference to the embedded element.
  PSET& pointset();

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    On return from this method, the representation of \p *this
    is not shared by different Determinate objects.
  */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  void mutate();

  //! Assignment operator.
  Determinate& operator=(const Determinate& y);

  //! Swaps \p *this with \p y.
  void m_swap(Determinate& y);

  //@} // Member Functions that May Modify the Domain Element

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  //! A function adapter for the Determinate class.
  /*! \ingroup PPL_CXX_interface
    It lifts a Binary_Operator_Assign function object, taking arguments
    of type PSET, producing the corresponding function object taking
    arguments of type Determinate<PSET>.

    The template parameter Binary_Operator_Assign is supposed to
    implement an <EM>apply and assign</EM> function, i.e., a function
    having signature <CODE>void foo(PSET& x, const PSET& y)</CODE> that
    applies an operator to \c x and \c y and assigns the result to \c x.
    For instance, such a function object is obtained by
    <CODE>std::mem_fun_ref(&C_Polyhedron::intersection_assign)</CODE>.
  */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  template <typename Binary_Operator_Assign>
  class Binary_Operator_Assign_Lifter {
  public:
    //! Explicit unary constructor.
    explicit
    Binary_Operator_Assign_Lifter(Binary_Operator_Assign op_assign);

    //! Function-application operator.
    void operator()(Determinate& x, const Determinate& y) const;

  private:
    //! The function object to be lifted.
    Binary_Operator_Assign op_assign_;
  };

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Helper function returning a Binary_Operator_Assign_Lifter object,
    also allowing for the deduction of template arguments.
  */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
  template <typename Binary_Operator_Assign>
  static Binary_Operator_Assign_Lifter<Binary_Operator_Assign>
  lift_op_assign(Binary_Operator_Assign op_assign);

private:
  //! The possibly shared representation of a Determinate object.
  /*! \ingroup PPL_CXX_interface
    By adopting the <EM>copy-on-write</EM> technique, a single
    representation of the base-level object may be shared by more than
    one object of the class Determinate.
  */
  class Rep {
  private:
    /*! \brief
      Count the number of references:
      -   0: leaked, \p pset is non-const;
      -   1: one reference, \p pset is non-const;
      - > 1: more than one reference, \p pset is const.
    */
    mutable unsigned long references;

    //! Private and unimplemented: assignment not allowed.
    Rep& operator=(const Rep& y);

    //! Private and unimplemented: copies not allowed.
    Rep(const Rep& y);

    //! Private and unimplemented: default construction not allowed.
    Rep();

  public:
    //! The possibly shared, embedded pointset.
    PSET pset;

    /*! \brief
      Builds a new representation by creating a pointset
      of the specified kind, in the specified vector space.
    */
    Rep(dimension_type num_dimensions, Degenerate_Element kind);

    //! Builds a new representation by copying the pointset \p p.
    Rep(const PSET& p);

    //! Builds a new representation by copying the constraints in \p cs.
    Rep(const Constraint_System& cs);

    //! Builds a new representation by copying the constraints in \p cgs.
    Rep(const Congruence_System& cgs);

    //! Destructor.
    ~Rep();

    //! Registers a new reference.
    void new_reference() const;

    /*! \brief
      Unregisters one reference; returns <CODE>true</CODE> if and only if
      the representation has become unreferenced.
    */
    bool del_reference() const;

    //! True if and only if this representation is currently shared.
    bool is_shared() const;

    /*! \brief
      Returns a lower bound to the total size in bytes of the memory
      occupied by \p *this.
    */
    memory_size_type total_memory_in_bytes() const;

    /*! \brief
      Returns a lower bound to the size in bytes of the memory
      managed by \p *this.
    */
    memory_size_type external_memory_in_bytes() const;
  };

  /*! \brief
    A pointer to the possibly shared representation of
    the base-level domain element.
  */
  Rep* prep;

  friend bool
  operator==<PSET>(const Determinate<PSET>& x, const Determinate<PSET>& y);

  friend bool
  operator!=<PSET>(const Determinate<PSET>& x, const Determinate<PSET>& y);
};

/* Automatically generated from PPL source file ../src/Determinate_inlines.hh line 1. */
/* Determinate class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Determinate_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename PSET>
inline
Determinate<PSET>::Rep::Rep(dimension_type num_dimensions,
                          Degenerate_Element kind)
  : references(0), pset(num_dimensions, kind) {
}

template <typename PSET>
inline
Determinate<PSET>::Rep::Rep(const PSET& p)
  : references(0), pset(p) {
}

template <typename PSET>
inline
Determinate<PSET>::Rep::Rep(const Constraint_System& cs)
  : references(0), pset(cs) {
}

template <typename PSET>
inline
Determinate<PSET>::Rep::Rep(const Congruence_System& cgs)
  : references(0), pset(cgs) {
}

template <typename PSET>
inline
Determinate<PSET>::Rep::~Rep() {
  PPL_ASSERT(references == 0);
}

template <typename PSET>
inline void
Determinate<PSET>::Rep::new_reference() const {
  ++references;
}

template <typename PSET>
inline bool
Determinate<PSET>::Rep::del_reference() const {
  return --references == 0;
}

template <typename PSET>
inline bool
Determinate<PSET>::Rep::is_shared() const {
  return references > 1;
}

template <typename PSET>
inline memory_size_type
Determinate<PSET>::Rep::external_memory_in_bytes() const {
  return pset.external_memory_in_bytes();
}

template <typename PSET>
inline memory_size_type
Determinate<PSET>::Rep::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename PSET>
inline
Determinate<PSET>::Determinate(const PSET& pset)
  : prep(new Rep(pset)) {
  prep->new_reference();
}

template <typename PSET>
inline
Determinate<PSET>::Determinate(const Constraint_System& cs)
  : prep(new Rep(cs)) {
  prep->new_reference();
}

template <typename PSET>
inline
Determinate<PSET>::Determinate(const Congruence_System& cgs)
  : prep(new Rep(cgs)) {
  prep->new_reference();
}

template <typename PSET>
inline
Determinate<PSET>::Determinate(const Determinate& y)
  : prep(y.prep) {
  prep->new_reference();
}

template <typename PSET>
inline
Determinate<PSET>::~Determinate() {
  if (prep->del_reference()) {
    delete prep;
  }
}

template <typename PSET>
inline Determinate<PSET>&
Determinate<PSET>::operator=(const Determinate& y) {
  y.prep->new_reference();
  if (prep->del_reference()) {
    delete prep;
  }
  prep = y.prep;
  return *this;
}

template <typename PSET>
inline void
Determinate<PSET>::m_swap(Determinate& y) {
  using std::swap;
  swap(prep, y.prep);
}

template <typename PSET>
inline void
Determinate<PSET>::mutate() {
  if (prep->is_shared()) {
    Rep* const new_prep = new Rep(prep->pset);
    (void) prep->del_reference();
    new_prep->new_reference();
    prep = new_prep;
  }
}

template <typename PSET>
inline const PSET&
Determinate<PSET>::pointset() const {
  return prep->pset;
}

template <typename PSET>
inline PSET&
Determinate<PSET>::pointset() {
  mutate();
  return prep->pset;
}

template <typename PSET>
inline void
Determinate<PSET>::upper_bound_assign(const Determinate& y) {
  pointset().upper_bound_assign(y.pointset());
}

template <typename PSET>
inline void
Determinate<PSET>::meet_assign(const Determinate& y) {
  pointset().intersection_assign(y.pointset());
}

template <typename PSET>
inline bool
Determinate<PSET>::has_nontrivial_weakening() {
  // FIXME: the following should be turned into a query to PSET.  This
  // can be postponed until the time the ask-and-tell construction is
  // revived.
  return false;
}

template <typename PSET>
inline void
Determinate<PSET>::weakening_assign(const Determinate& y) {
  // FIXME: the following should be turned into a proper
  // implementation.  This can be postponed until the time the
  // ask-and-tell construction is revived.
  pointset().difference_assign(y.pointset());
}

template <typename PSET>
inline void
Determinate<PSET>::concatenate_assign(const Determinate& y) {
  pointset().concatenate_assign(y.pointset());
}

template <typename PSET>
inline bool
Determinate<PSET>::definitely_entails(const Determinate& y) const {
  return prep == y.prep || y.prep->pset.contains(prep->pset);
}

template <typename PSET>
inline bool
Determinate<PSET>::is_definitely_equivalent_to(const Determinate& y) const {
  return prep == y.prep || prep->pset == y.prep->pset;
}

template <typename PSET>
inline bool
Determinate<PSET>::is_top() const {
  return prep->pset.is_universe();
}

template <typename PSET>
inline bool
Determinate<PSET>::is_bottom() const {
  return prep->pset.is_empty();
}

template <typename PSET>
inline memory_size_type
Determinate<PSET>::external_memory_in_bytes() const {
  return prep->total_memory_in_bytes();
}

template <typename PSET>
inline memory_size_type
Determinate<PSET>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename PSET>
inline bool
Determinate<PSET>::OK() const {
  return prep->pset.OK();
}

namespace IO_Operators {

/*! \relates Parma_Polyhedra_Library::Determinate */
template <typename PSET>
inline std::ostream&
operator<<(std::ostream& s, const Determinate<PSET>& x) {
  s << x.pointset();
  return s;
}

} // namespace IO_Operators

/*! \relates Determinate */
template <typename PSET>
inline bool
operator==(const Determinate<PSET>& x, const Determinate<PSET>& y) {
  return x.prep == y.prep || x.prep->pset == y.prep->pset;
}

/*! \relates Determinate */
template <typename PSET>
inline bool
operator!=(const Determinate<PSET>& x, const Determinate<PSET>& y) {
  return x.prep != y.prep && x.prep->pset != y.prep->pset;
}

template <typename PSET>
template <typename Binary_Operator_Assign>
inline
Determinate<PSET>::Binary_Operator_Assign_Lifter<Binary_Operator_Assign>::
Binary_Operator_Assign_Lifter(Binary_Operator_Assign op_assign)
  : op_assign_(op_assign) {
}

template <typename PSET>
template <typename Binary_Operator_Assign>
inline void
Determinate<PSET>::Binary_Operator_Assign_Lifter<Binary_Operator_Assign>::
operator()(Determinate& x, const Determinate& y) const {
  op_assign_(x.pointset(), y.pointset());
}

template <typename PSET>
template <typename Binary_Operator_Assign>
inline
Determinate<PSET>::Binary_Operator_Assign_Lifter<Binary_Operator_Assign>
Determinate<PSET>::lift_op_assign(Binary_Operator_Assign op_assign) {
  return Binary_Operator_Assign_Lifter<Binary_Operator_Assign>(op_assign);
}

/*! \relates Determinate */
template <typename PSET>
inline void
swap(Determinate<PSET>& x, Determinate<PSET>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Determinate_defs.hh line 329. */

/* Automatically generated from PPL source file ../src/Powerset_defs.hh line 1. */
/* Powerset class declaration.
*/


/* Automatically generated from PPL source file ../src/Powerset_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename D>
class Powerset;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/iterator_to_const_defs.hh line 1. */
/* iterator_to_const and const_iterator_to_const class declarations.
*/


/* Automatically generated from PPL source file ../src/iterator_to_const_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename Container>
class iterator_to_const;

template <typename Container>
class const_iterator_to_const;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/iterator_to_const_defs.hh line 29. */
#include <iterator>

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! An iterator on a sequence of read-only objects.
/*! \ingroup PPL_CXX_interface
  This template class implements a bidirectional <EM>read-only</EM>
  iterator on the sequence of objects <CODE>Container</CODE>.
  By using this iterator class it is not possible to modify the objects
  contained in <CODE>Container</CODE>; rather, object modification has
  to be implemented by object replacement, i.e., by using the methods
  provided by <CODE>Container</CODE> to remove/insert objects.
  Such a policy (a modifiable container of read-only objects) allows
  for a reliable enforcement of invariants (such as sortedness of the
  objects in the sequence).

  \note
  For any developers' need, suitable friend declarations allow for
  accessing the low-level iterators on the sequence of objects.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Container>
class Parma_Polyhedra_Library::iterator_to_const {
private:
  //! The type of the underlying mutable iterator.
  typedef typename Container::iterator Base;

  //! A shortcut for naming the const_iterator traits.
  typedef typename
  std::iterator_traits<typename Container::const_iterator> Traits;

  //! A (mutable) iterator on the sequence of elements.
  Base base;

  //! Constructs from the lower-level iterator.
  iterator_to_const(const Base& b);

  friend class const_iterator_to_const<Container>;
  template <typename T> friend class Powerset;

public:
  // Same traits of the const_iterator, therefore
  // forbidding the direct modification of sequence elements.
  typedef typename Traits::iterator_category iterator_category;
  typedef typename Traits::value_type value_type;
  typedef typename Traits::difference_type difference_type;
  typedef typename Traits::pointer pointer;
  typedef typename Traits::reference reference;

  //! Default constructor.
  iterator_to_const();

  //! Copy constructor.
  iterator_to_const(const iterator_to_const& y);

  //! Dereference operator.
  reference operator*() const;

  //! Indirect access operator.
  pointer operator->() const;

  //! Prefix increment operator.
  iterator_to_const& operator++();

  //! Postfix increment operator.
  iterator_to_const operator++(int);

  //! Prefix decrement operator.
  iterator_to_const& operator--();

  //! Postfix decrement operator.
  iterator_to_const operator--(int);

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this and \p y are identical.
  */
  bool operator==(const iterator_to_const& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this and \p y are different.
  */
  bool operator!=(const iterator_to_const& y) const;
};

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! A %const_iterator on a sequence of read-only objects.
/*! \ingroup PPL_CXX_interface
  This class, besides implementing a read-only bidirectional iterator
  on a read-only sequence of objects, ensures interoperability
  with template class iterator_to_const.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Container>
class Parma_Polyhedra_Library::const_iterator_to_const {
private:
  //! The type of the underlying %const_iterator.
  typedef typename Container::const_iterator Base;

  //! A shortcut for naming traits.
  typedef typename std::iterator_traits<Base> Traits;

  //! A %const_iterator on the sequence of elements.
  Base base;

  //! Constructs from the lower-level const_iterator.
  const_iterator_to_const(const Base& b);

  friend class iterator_to_const<Container>;
  template <typename T> friend class Powerset;

public:
  // Same traits of the underlying const_iterator.
  typedef typename Traits::iterator_category iterator_category;
  typedef typename Traits::value_type value_type;
  typedef typename Traits::difference_type difference_type;
  typedef typename Traits::pointer pointer;
  typedef typename Traits::reference reference;

  //! Default constructor.
  const_iterator_to_const();

  //! Copy constructor.
  const_iterator_to_const(const const_iterator_to_const& y);

  //! Constructs from the corresponding non-const iterator.
  const_iterator_to_const(const iterator_to_const<Container>& y);

  //! Dereference operator.
  reference operator*() const;

  //! Indirect member selector.
  pointer operator->() const;

  //! Prefix increment operator.
  const_iterator_to_const& operator++();

  //! Postfix increment operator.
  const_iterator_to_const operator++(int);

  //! Prefix decrement operator.
  const_iterator_to_const& operator--();

  //! Postfix decrement operator.
  const_iterator_to_const operator--(int);

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this and \p y are identical.
  */
  bool operator==(const const_iterator_to_const& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if
    \p *this and \p y are different.
  */
  bool operator!=(const const_iterator_to_const& y) const;
};

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Mixed comparison operator: returns <CODE>true</CODE> if and only
  if (the const version of) \p x is identical to \p y.

  \relates const_iterator_to_const
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Container>
bool
operator==(const iterator_to_const<Container>& x,
           const const_iterator_to_const<Container>& y);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  Mixed comparison operator: returns <CODE>true</CODE> if and only
  if (the const version of) \p x is different from \p y.

  \relates const_iterator_to_const
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename Container>
bool
operator!=(const iterator_to_const<Container>& x,
           const const_iterator_to_const<Container>& y);

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/iterator_to_const_inlines.hh line 1. */
/* iterator_to_const and const_iterator_to_const class implementations:
   inline functions.
*/


namespace Parma_Polyhedra_Library {

template <typename Container>
inline
iterator_to_const<Container>::iterator_to_const()
  : base() {
}

template <typename Container>
inline
iterator_to_const<Container>::iterator_to_const(const iterator_to_const& y)
  : base(y.base) {
}

template <typename Container>
inline
iterator_to_const<Container>::iterator_to_const(const Base& b)
  : base(b) {
}

template <typename Container>
inline typename iterator_to_const<Container>::reference
iterator_to_const<Container>::operator*() const {
  return *base;
}

template <typename Container>
inline typename iterator_to_const<Container>::pointer
iterator_to_const<Container>::operator->() const {
  return &*base;
}

template <typename Container>
inline iterator_to_const<Container>&
iterator_to_const<Container>::operator++() {
  ++base;
  return *this;
}

template <typename Container>
inline iterator_to_const<Container>
iterator_to_const<Container>::operator++(int) {
  iterator_to_const tmp = *this;
  operator++();
  return tmp;
}

template <typename Container>
inline iterator_to_const<Container>&
iterator_to_const<Container>::operator--() {
  --base;
  return *this;
}

template <typename Container>
inline iterator_to_const<Container>
iterator_to_const<Container>::operator--(int) {
  iterator_to_const tmp = *this;
  operator--();
  return tmp;
}

template <typename Container>
inline bool
iterator_to_const<Container>::operator==(const iterator_to_const& y) const {
  return base == y.base;
}

template <typename Container>
inline bool
iterator_to_const<Container>::operator!=(const iterator_to_const& y) const {
  return !operator==(y);
}

template <typename Container>
inline
const_iterator_to_const<Container>::const_iterator_to_const()
  : base() {
}

template <typename Container>
inline
const_iterator_to_const<Container>
::const_iterator_to_const(const const_iterator_to_const& y)
  : base(y.base) {
}

template <typename Container>
inline
const_iterator_to_const<Container>::const_iterator_to_const(const Base& b)
  : base(b) {
}

template <typename Container>
inline typename const_iterator_to_const<Container>::reference
const_iterator_to_const<Container>::operator*() const {
  return *base;
}

template <typename Container>
inline typename const_iterator_to_const<Container>::pointer
const_iterator_to_const<Container>::operator->() const {
  return &*base;
}

template <typename Container>
inline const_iterator_to_const<Container>&
const_iterator_to_const<Container>::operator++() {
  ++base;
  return *this;
}

template <typename Container>
inline const_iterator_to_const<Container>
const_iterator_to_const<Container>::operator++(int) {
  const_iterator_to_const tmp = *this;
  operator++();
  return tmp;
}

template <typename Container>
inline const_iterator_to_const<Container>&
const_iterator_to_const<Container>::operator--() {
  --base;
  return *this;
}

template <typename Container>
inline const_iterator_to_const<Container>
const_iterator_to_const<Container>::operator--(int) {
  const_iterator_to_const tmp = *this;
  operator--();
  return tmp;
}

template <typename Container>
inline bool
const_iterator_to_const<Container>
::operator==(const const_iterator_to_const& y) const {
  return base == y.base;
}

template <typename Container>
inline bool
const_iterator_to_const<Container>
::operator!=(const const_iterator_to_const& y) const {
  return !operator==(y);
}

template <typename Container>
inline
const_iterator_to_const<Container>
::const_iterator_to_const(const iterator_to_const<Container>& y)
  : base(y.base) {
}

/*! \relates const_iterator_to_const */
template <typename Container>
inline bool
operator==(const iterator_to_const<Container>& x,
           const const_iterator_to_const<Container>& y) {
  return const_iterator_to_const<Container>(x).operator==(y);
}

/*! \relates const_iterator_to_const */
template <typename Container>
inline bool
operator!=(const iterator_to_const<Container>& x,
           const const_iterator_to_const<Container>& y) {
  return !(x == y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/iterator_to_const_defs.hh line 219. */

/* Automatically generated from PPL source file ../src/Powerset_defs.hh line 30. */
#include <iosfwd>
#include <iterator>
#include <list>

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Powerset */
template <typename D>
void swap(Powerset<D>& x, Powerset<D>& y);

//! Returns <CODE>true</CODE> if and only if \p x and \p y are equivalent.
/*! \relates Powerset */
template <typename D>
bool
operator==(const Powerset<D>& x, const Powerset<D>& y);

//! Returns <CODE>true</CODE> if and only if \p x and \p y are not equivalent.
/*! \relates Powerset */
template <typename D>
bool
operator!=(const Powerset<D>& x, const Powerset<D>& y);

namespace IO_Operators {

//! Output operator.
/*! \relates Parma_Polyhedra_Library::Powerset */
template <typename D>
std::ostream&
operator<<(std::ostream& s, const Powerset<D>& x);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library


//! The powerset construction on a base-level domain.
/*! \ingroup PPL_CXX_interface
  This class offers a generic implementation of a
  <EM>powerset</EM> domain as defined in Section \ref powerset.

  Besides invoking the available methods on the disjuncts of a Powerset,
  this class also provides bidirectional iterators that allow for a
  direct inspection of these disjuncts. For a consistent handling of
  Omega-reduction, all the iterators are <EM>read-only</EM>, meaning
  that the disjuncts cannot be overwritten. Rather, by using the class
  <CODE>iterator</CODE>, it is possible to drop one or more disjuncts
  (possibly so as to later add back modified versions).  As an example
  of iterator usage, the following template function drops from
  powerset \p ps all the disjuncts that would have become redundant by
  the addition of an external element \p d.

  \code
template <typename D>
void
drop_subsumed(Powerset<D>& ps, const D& d) {
  for (typename Powerset<D>::iterator i = ps.begin(),
         ps_end = ps.end(), i != ps_end; )
    if (i->definitely_entails(d))
      i = ps.drop_disjunct(i);
    else
      ++i;
}
  \endcode

  The template class D must provide the following methods.
  \code
    memory_size_type total_memory_in_bytes() const
  \endcode
  Returns a lower bound on the total size in bytes of the memory
  occupied by the instance of D.
  \code
    bool is_top() const
  \endcode
  Returns <CODE>true</CODE> if and only if the instance of D is the top
  element of the domain.
  \code
    bool is_bottom() const
  \endcode
  Returns <CODE>true</CODE> if and only if the instance of D is the
  bottom element of the domain.
  \code
    bool definitely_entails(const D& y) const
  \endcode
  Returns <CODE>true</CODE> if the instance of D definitely entails
  <CODE>y</CODE>.  Returns <CODE>false</CODE> if the instance may not
  entail <CODE>y</CODE> (i.e., if the instance does not entail
  <CODE>y</CODE> or if entailment could not be decided).
  \code
    void upper_bound_assign(const D& y)
  \endcode
  Assigns to the instance of D an upper bound of the instance and
  <CODE>y</CODE>.
  \code
    void meet_assign(const D& y)
  \endcode
  Assigns to the instance of D the meet of the instance and
  <CODE>y</CODE>.
  \code
    bool OK() const
  \endcode
  Returns <CODE>true</CODE> if the instance of D is in a consistent
  state, else returns <CODE>false</CODE>.

  The following operators on the template class D must be defined.
  \code
    operator<<(std::ostream& s, const D& x)
  \endcode
  Writes a textual representation of the instance of D on
  <CODE>s</CODE>.
  \code
    operator==(const D& x, const D& y)
  \endcode
  Returns <CODE>true</CODE> if and only if <CODE>x</CODE> and
  <CODE>y</CODE> are equivalent D's.
  \code
    operator!=(const D& x, const D& y)
  \endcode
  Returns <CODE>true</CODE> if and only if <CODE>x</CODE> and
  <CODE>y</CODE> are different D's.
*/
template <typename D>
class Parma_Polyhedra_Library::Powerset {
public:
  //! \name Constructors and Destructor
  //@{

  /*! \brief
    Default constructor: builds the bottom of the powerset constraint
    system (i.e., the empty powerset).
  */
  Powerset();

  //! Copy constructor.
  Powerset(const Powerset& y);

  /*! \brief
    If \p d is not bottom, builds a powerset containing only \p d.
    Builds the empty powerset otherwise.
  */
  explicit Powerset(const D& d);

  //! Destructor.
  ~Powerset();

  //@} // Constructors and Destructor

  //! \name Member Functions that Do Not Modify the Powerset Object
  //@{

  /*! \brief
    Returns <CODE>true</CODE> if \p *this definitely entails \p y.
    Returns <CODE>false</CODE> if \p *this may not entail \p y
    (i.e., if \p *this does not entail \p y or if entailment could
    not be decided).
  */
  bool definitely_entails(const Powerset& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is the top
    element of the powerset constraint system (i.e., it represents
    the universe).
  */
  bool is_top() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is the bottom
    element of the powerset constraint system (i.e., it represents
    the empty set).
  */
  bool is_bottom() const;

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  /*! \brief
    Returns a lower bound to the size in bytes of the memory
    managed by \p *this.
  */
  memory_size_type external_memory_in_bytes() const;

  //! Checks if all the invariants are satisfied.
  // FIXME: document and perhaps use an enum instead of a bool.
  bool OK(bool disallow_bottom = false) const;

  //@} // Member Functions that Do Not Modify the Powerset Object

protected:
  //! A powerset is implemented as a sequence of elements.
  /*!
    The particular sequence employed must support efficient deletion
    in any position and efficient back insertion.
  */
  typedef std::list<D> Sequence;

  //! Alias for the low-level iterator on the disjuncts.
  typedef typename Sequence::iterator Sequence_iterator;

  //! Alias for the low-level %const_iterator on the disjuncts.
  typedef typename Sequence::const_iterator Sequence_const_iterator;

  //! The sequence container holding powerset's elements.
  Sequence sequence;

  //! If <CODE>true</CODE>, \p *this is Omega-reduced.
  mutable bool reduced;

public:
  // Sequence manipulation types, accessors and modifiers
  typedef typename Sequence::size_type size_type;
  typedef typename Sequence::value_type value_type;

  /*! \brief
    Alias for a <EM>read-only</EM> bidirectional %iterator on the
    disjuncts of a Powerset element.

    By using this iterator type, the disjuncts cannot be overwritten,
    but they can be removed using methods
    <CODE>drop_disjunct(iterator position)</CODE> and
    <CODE>drop_disjuncts(iterator first, iterator last)</CODE>,
    while still ensuring a correct handling of Omega-reduction.
  */
  typedef iterator_to_const<Sequence> iterator;

  //! A bidirectional %const_iterator on the disjuncts of a Powerset element.
  typedef const_iterator_to_const<Sequence> const_iterator;

  //! The reverse iterator type built from Powerset::iterator.
  typedef std::reverse_iterator<iterator> reverse_iterator;

  //! The reverse iterator type built from Powerset::const_iterator.
  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

  //! \name Member Functions for the Direct Manipulation of Disjuncts
  //@{

  /*! \brief
    Drops from the sequence of disjuncts in \p *this all the
    non-maximal elements so that \p *this is non-redundant.

    This method is declared <CODE>const</CODE> because, even though
    Omega-reduction may change the syntactic representation of \p *this,
    its semantics will be unchanged.
  */
  void omega_reduce() const;

  //! Returns the number of disjuncts.
  size_type size() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if there are no disjuncts in
    \p *this.
  */
  bool empty() const;

  /*! \brief
    Returns an iterator pointing to the first disjunct, if \p *this
    is not empty; otherwise, returns the past-the-end iterator.
  */
  iterator begin();

  //! Returns the past-the-end iterator.
  iterator end();

  /*! \brief
    Returns a const_iterator pointing to the first disjunct, if \p *this
    is not empty; otherwise, returns the past-the-end const_iterator.
  */
  const_iterator begin() const;

  //! Returns the past-the-end const_iterator.
  const_iterator end() const;

  /*! \brief
    Returns a reverse_iterator pointing to the last disjunct, if \p *this
    is not empty; otherwise, returns the before-the-start reverse_iterator.
  */
  reverse_iterator rbegin();

  //! Returns the before-the-start reverse_iterator.
  reverse_iterator rend();

  /*! \brief
    Returns a const_reverse_iterator pointing to the last disjunct,
    if \p *this is not empty; otherwise, returns the before-the-start
    const_reverse_iterator.
  */
  const_reverse_iterator rbegin() const;

  //! Returns the before-the-start const_reverse_iterator.
  const_reverse_iterator rend() const;

  //! Adds to \p *this the disjunct \p d.
  void add_disjunct(const D& d);

  /*! \brief
    Drops the disjunct in \p *this pointed to by \p position, returning
    an iterator to the disjunct following \p position.
  */
  iterator drop_disjunct(iterator position);

  //! Drops all the disjuncts from \p first to \p last (excluded).
  void drop_disjuncts(iterator first, iterator last);

  //! Drops all the disjuncts, making \p *this an empty powerset.
  void clear();

  //@} // Member Functions for the Direct Manipulation of Disjuncts

  //! \name Member Functions that May Modify the Powerset Object
  //@{

  //! The assignment operator.
  Powerset& operator=(const Powerset& y);

  //! Swaps \p *this with \p y.
  void m_swap(Powerset& y);

  //! Assigns to \p *this the least upper bound of \p *this and \p y.
  void least_upper_bound_assign(const Powerset& y);

  //! Assigns to \p *this an upper bound of \p *this and \p y.
  /*!
    The result will be the least upper bound of \p *this and \p y.
  */
  void upper_bound_assign(const Powerset& y);

  /*! \brief
    Assigns to \p *this the least upper bound of \p *this and \p y
    and returns \c true.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  bool upper_bound_assign_if_exact(const Powerset& y);

  //! Assigns to \p *this the meet of \p *this and \p y.
  void meet_assign(const Powerset& y);

  /*! \brief
    If \p *this is not empty (i.e., it is not the bottom element),
    it is reduced to a singleton obtained by computing an upper-bound
    of all the disjuncts.
  */
  void collapse();

  //@} // Member Functions that May Modify the Powerset element

protected:
  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this does not contain
    non-maximal elements.
  */
  bool is_omega_reduced() const;

  /*! \brief Upon return, \p *this will contain at most \p
    max_disjuncts elements; the set of disjuncts in positions greater
    than or equal to \p max_disjuncts, will be replaced at that
    position by their upper-bound.
  */
  void collapse(unsigned max_disjuncts);

  /*! \brief
    Adds to \p *this the disjunct \p d,
    assuming \p d is not the bottom element and ensuring
    partial Omega-reduction.

    If \p d is not the bottom element and is not Omega-redundant with
    respect to elements in positions between \p first and \p last, all
    elements in these positions that would be made Omega-redundant by the
    addition of \p d are dropped and \p d is added to the reduced
    sequence.
    If \p *this is reduced before an invocation of this method,
    it will be reduced upon successful return from the method.
  */
  iterator add_non_bottom_disjunct_preserve_reduction(const D& d,
                                                      iterator first,
                                                      iterator last);

  /*! \brief
    Adds to \p *this the disjunct \p d, assuming \p d is not the
    bottom element and preserving Omega-reduction.

    If \p *this is reduced before an invocation of this method,
    it will be reduced upon successful return from the method.
  */
  void add_non_bottom_disjunct_preserve_reduction(const D& d);

  /*! \brief
    Assigns to \p *this the result of applying \p op_assign pairwise
    to the elements in \p *this and \p y.

    The elements of the powerset result are obtained by applying
    \p op_assign to each pair of elements whose components are drawn
    from \p *this and \p y, respectively.
  */
  template <typename Binary_Operator_Assign>
  void pairwise_apply_assign(const Powerset& y,
                             Binary_Operator_Assign op_assign);

private:
  /*! \brief
    Does the hard work of checking whether \p *this contains non-maximal
    elements and returns <CODE>true</CODE> if and only if it does not.
  */
  bool check_omega_reduced() const;

  /*! \brief
    Replaces the disjunct \p *sink by an upper bound of itself and
    all the disjuncts following it.
  */
  void collapse(Sequence_iterator sink);
};

/* Automatically generated from PPL source file ../src/Powerset_inlines.hh line 1. */
/* Powerset class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Powerset_inlines.hh line 28. */
#include <algorithm>

namespace Parma_Polyhedra_Library {

template <typename D>
inline typename Powerset<D>::iterator
Powerset<D>::begin() {
  return sequence.begin();
}

template <typename D>
inline typename Powerset<D>::iterator
Powerset<D>::end() {
  return sequence.end();
}

template <typename D>
inline typename Powerset<D>::const_iterator
Powerset<D>::begin() const {
  return sequence.begin();
}

template <typename D>
inline typename Powerset<D>::const_iterator
Powerset<D>::end() const {
  return sequence.end();
}

template <typename D>
inline typename Powerset<D>::reverse_iterator
Powerset<D>::rbegin() {
  return reverse_iterator(end());
}

template <typename D>
inline typename Powerset<D>::reverse_iterator
Powerset<D>::rend() {
  return reverse_iterator(begin());
}

template <typename D>
inline typename Powerset<D>::const_reverse_iterator
Powerset<D>::rbegin() const {
  return const_reverse_iterator(end());
}

template <typename D>
inline typename Powerset<D>::const_reverse_iterator
Powerset<D>::rend() const {
  return const_reverse_iterator(begin());
}

template <typename D>
inline typename Powerset<D>::size_type
Powerset<D>::size() const {
  return sequence.size();
}

template <typename D>
inline bool
Powerset<D>::empty() const {
  return sequence.empty();
}

template <typename D>
inline typename Powerset<D>::iterator
Powerset<D>::drop_disjunct(iterator position) {
  return sequence.erase(position.base);
}

template <typename D>
inline void
Powerset<D>::drop_disjuncts(iterator first, iterator last) {
  sequence.erase(first.base, last.base);
}

template <typename D>
inline void
Powerset<D>::clear() {
  sequence.clear();
}

template <typename D>
inline
Powerset<D>::Powerset(const Powerset& y)
  : sequence(y.sequence), reduced(y.reduced) {
}

template <typename D>
inline Powerset<D>&
Powerset<D>::operator=(const Powerset& y) {
  sequence = y.sequence;
  reduced = y.reduced;
  return *this;
}

template <typename D>
inline void
Powerset<D>::m_swap(Powerset& y) {
  std::swap(sequence, y.sequence);
  std::swap(reduced, y.reduced);
}

template <typename D>
inline
Powerset<D>::Powerset()
  : sequence(), reduced(true) {
}

template <typename D>
inline
Powerset<D>::Powerset(const D& d)
  : sequence(), reduced(false) {
  sequence.push_back(d);
  PPL_ASSERT_HEAVY(OK());
}

template <typename D>
inline
Powerset<D>::~Powerset() {
}

template <typename D>
inline void
Powerset<D>::add_non_bottom_disjunct_preserve_reduction(const D& d) {
  // !d.is_bottom() is asserted by the callee.
  add_non_bottom_disjunct_preserve_reduction(d, begin(), end());
}

template <typename D>
inline void
Powerset<D>::add_disjunct(const D& d) {
  sequence.push_back(d);
  reduced = false;
}

/*! \relates Powerset */
template <typename D>
inline
bool operator!=(const Powerset<D>& x, const Powerset<D>& y) {
  return !(x == y);
}

template <typename D>
inline bool
Powerset<D>::is_top() const {
  // Must perform omega-reduction for correctness.
  omega_reduce();
  const_iterator xi = begin();
  const_iterator x_end = end();
  return xi != x_end && xi->is_top() && ++xi == x_end;
}

template <typename D>
inline bool
Powerset<D>::is_bottom() const {
  // Must perform omega-reduction for correctness.
  omega_reduce();
  return empty();
}

template <typename D>
inline void
Powerset<D>::collapse() {
  if (!empty()) {
    collapse(sequence.begin());
  }
}

template <typename D>
inline void
Powerset<D>::meet_assign(const Powerset& y) {
  pairwise_apply_assign(y, std::mem_fun_ref(&D::meet_assign));
}

template <typename D>
inline void
Powerset<D>::upper_bound_assign(const Powerset& y) {
  least_upper_bound_assign(y);
}

template <typename D>
inline bool
Powerset<D>::upper_bound_assign_if_exact(const Powerset& y) {
  least_upper_bound_assign(y);
  return true;
}

template <typename D>
inline memory_size_type
Powerset<D>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

/*! \relates Powerset */
template <typename D>
inline void
swap(Powerset<D>& x, Powerset<D>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Powerset_templates.hh line 1. */
/* Powerset class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/Powerset_templates.hh line 29. */
#include <iostream>
#include <algorithm>

namespace Parma_Polyhedra_Library {

template <typename D>
void
Powerset<D>::collapse(const Sequence_iterator sink) {
  PPL_ASSERT(sink != sequence.end());
  D& d = *sink;
  iterator x_sink = sink;
  iterator next_x_sink = x_sink;
  ++next_x_sink;
  iterator x_end = end();
  for (const_iterator xi = next_x_sink; xi != x_end; ++xi) {
    d.upper_bound_assign(*xi);
  }
  // Drop the surplus disjuncts.
  drop_disjuncts(next_x_sink, x_end);

  // Ensure omega-reduction.
  for (iterator xi = begin(); xi != x_sink; ) {
    if (xi->definitely_entails(d)) {
      xi = drop_disjunct(xi);
    }
    else {
      ++xi;
    }
  }

  PPL_ASSERT_HEAVY(OK());
}

template <typename D>
void
Powerset<D>::omega_reduce() const {
  if (reduced) {
    return;
  }
  Powerset& x = const_cast<Powerset&>(*this);
  // First remove all bottom elements.
  for (iterator xi = x.begin(), x_end = x.end(); xi != x_end; ) {
    if (xi->is_bottom()) {
      xi = x.drop_disjunct(xi);
    }
    else {
      ++xi;
    }
  }
  // Then remove non-maximal elements.
  for (iterator xi = x.begin(); xi != x.end(); ) {
    const D& xv = *xi;
    bool dropping_xi = false;
    for (iterator yi = x.begin(); yi != x.end(); ) {
      if (xi == yi) {
        ++yi;
      }
      else {
        const D& yv = *yi;
        if (yv.definitely_entails(xv)) {
          yi = x.drop_disjunct(yi);
        }
        else if (xv.definitely_entails(yv)) {
          dropping_xi = true;
          break;
        }
        else {
          ++yi;
        }
      }
    }
    if (dropping_xi) {
      xi = x.drop_disjunct(xi);
    }
    else {
      ++xi;
    }
    if (abandon_expensive_computations != 0 && xi != x.end()) {
      // Hurry up!
      x.collapse(xi.base);
      break;
    }
  }
  reduced = true;
  PPL_ASSERT_HEAVY(OK());
}

template <typename D>
void
Powerset<D>::collapse(const unsigned max_disjuncts) {
  PPL_ASSERT(max_disjuncts > 0);
  // Omega-reduce before counting the number of disjuncts.
  omega_reduce();
  size_type n = size();
  if (n > max_disjuncts) {
    // Let `i' point to the last disjunct that will survive.
    iterator i = begin();
    std::advance(i, max_disjuncts-1);
    // This disjunct will be assigned an upper-bound of itself and of
    // all the disjuncts that follow.
    collapse(i.base);
  }
  PPL_ASSERT_HEAVY(OK());
  PPL_ASSERT(is_omega_reduced());
}

template <typename D>
bool
Powerset<D>::check_omega_reduced() const {
  for (const_iterator x_begin = begin(), x_end = end(),
         xi = x_begin; xi != x_end; ++xi) {
    const D& xv = *xi;
    if (xv.is_bottom()) {
      return false;
    }
    for (const_iterator yi = x_begin; yi != x_end; ++yi) {
      if (xi == yi) {
        continue;
      }
      const D& yv = *yi;
      if (xv.definitely_entails(yv) || yv.definitely_entails(xv)) {
        return false;
      }
    }
  }
  return true;
}

template <typename D>
bool
Powerset<D>::is_omega_reduced() const {
  if (!reduced && check_omega_reduced()) {
    reduced = true;
  }
  return reduced;
}

template <typename D>
typename Powerset<D>::iterator
Powerset<D>::add_non_bottom_disjunct_preserve_reduction(const D& d,
                                                        iterator first,
                                                        iterator last) {
  PPL_ASSERT_HEAVY(!d.is_bottom());
  for (iterator xi = first; xi != last; ) {
    const D& xv = *xi;
    if (d.definitely_entails(xv)) {
      return first;
    }
    else if (xv.definitely_entails(d)) {
      if (xi == first) {
        ++first;
      }
      xi = drop_disjunct(xi);
    }
    else {
      ++xi;
    }
  }
  sequence.push_back(d);
  PPL_ASSERT_HEAVY(OK());
  return first;
}

template <typename D>
bool
Powerset<D>::definitely_entails(const Powerset& y) const {
  const Powerset<D>& x = *this;
  bool found = true;
  for (const_iterator xi = x.begin(),
         x_end = x.end(); found && xi != x_end; ++xi) {
    found = false;
    for (const_iterator yi = y.begin(),
           y_end = y.end(); !found && yi != y_end; ++yi) {
      found = (*xi).definitely_entails(*yi);
    }
  }
  return found;
}

/*! \relates Powerset */
template <typename D>
bool
operator==(const Powerset<D>& x, const Powerset<D>& y) {
  x.omega_reduce();
  y.omega_reduce();
  if (x.size() != y.size()) {
    return false;
  }
  // Take a copy of `y' and work with it.
  Powerset<D> z = y;
  for (typename Powerset<D>::const_iterator xi = x.begin(),
         x_end = x.end(); xi != x_end; ++xi) {
    typename Powerset<D>::iterator zi = z.begin();
    typename Powerset<D>::iterator z_end = z.end();
    zi = std::find(zi, z_end, *xi);
    if (zi == z_end) {
      return false;
    }
    else {
      z.drop_disjunct(zi);
    }
  }
  return true;
}

template <typename D>
template <typename Binary_Operator_Assign>
void
Powerset<D>::pairwise_apply_assign(const Powerset& y,
                                   Binary_Operator_Assign op_assign) {
  // Ensure omega-reduction here, since what follows has quadratic complexity.
  omega_reduce();
  y.omega_reduce();
  Sequence new_sequence;
  for (const_iterator xi = begin(), x_end = end(),
         y_begin = y.begin(), y_end = y.end(); xi != x_end; ++xi) {
    for (const_iterator yi = y_begin; yi != y_end; ++yi) {
      D zi = *xi;
      op_assign(zi, *yi);
      if (!zi.is_bottom()) {
        new_sequence.push_back(zi);
      }
    }
  }
  // Put the new sequence in place.
  std::swap(sequence, new_sequence);
  reduced = false;
  PPL_ASSERT_HEAVY(OK());
}

template <typename D>
void
Powerset<D>::least_upper_bound_assign(const Powerset& y) {
  // Ensure omega-reduction here, since what follows has quadratic complexity.
  omega_reduce();
  y.omega_reduce();
  iterator old_begin = begin();
  iterator old_end = end();
  for (const_iterator i = y.begin(), y_end = y.end(); i != y_end; ++i) {
    old_begin = add_non_bottom_disjunct_preserve_reduction(*i,
                                                           old_begin,
                                                           old_end);
  }
  PPL_ASSERT_HEAVY(OK());
}

namespace IO_Operators {

/*! \relates Parma_Polyhedra_Library::Powerset */
template <typename D>
std::ostream&
operator<<(std::ostream& s, const Powerset<D>& x) {
  if (x.is_bottom()) {
    s << "false";
  }
  else if (x.is_top()) {
    s << "true";
  }
  else {
    for (typename Powerset<D>::const_iterator i = x.begin(),
           x_end = x.end(); i != x_end; ) {
      s << "{ " << *i << " }";
      ++i;
      if (i != x_end) {
        s << ", ";
      }
    }
  }
  return s;
}

} // namespace IO_Operators

template <typename D>
memory_size_type
Powerset<D>::external_memory_in_bytes() const {
  memory_size_type bytes = 0;
  for (const_iterator xi = begin(), x_end = end(); xi != x_end; ++xi) {
    bytes += xi->total_memory_in_bytes();
    // We assume there is at least a forward and a backward link, and
    // that the pointers implementing them are at least the size of
    // pointers to `D'.
    bytes += 2*sizeof(D*);
  }
  return bytes;
}

template <typename D>
bool
Powerset<D>::OK(const bool disallow_bottom) const {
  for (const_iterator xi = begin(), x_end = end(); xi != x_end; ++xi) {
    if (!xi->OK()) {
      return false;
    }
    if (disallow_bottom && xi->is_bottom()) {
#ifndef NDEBUG
      std::cerr << "Bottom element in powerset!"
                << std::endl;
#endif
      return false;
    }
  }
  if (reduced && !check_omega_reduced()) {
#ifndef NDEBUG
    std::cerr << "Powerset claims to be reduced, but it is not!"
              << std::endl;
#endif
    return false;
  }
  return true;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Powerset_defs.hh line 449. */

/* Automatically generated from PPL source file ../src/Pointset_Powerset_defs.hh line 44. */
#include <iosfwd>
#include <list>
#include <map>

//! The powerset construction instantiated on PPL pointset domains.
/*! \ingroup PPL_CXX_interface
  \warning
  At present, the supported instantiations for the
  disjunct domain template \p PSET are the simple pointset domains:
  <CODE>C_Polyhedron</CODE>,
  <CODE>NNC_Polyhedron</CODE>,
  <CODE>Grid</CODE>,
  <CODE>Octagonal_Shape\<T\></CODE>,
  <CODE>BD_Shape\<T\></CODE>,
  <CODE>Box\<T\></CODE>.
*/
template <typename PSET>
class Parma_Polyhedra_Library::Pointset_Powerset
  : public Parma_Polyhedra_Library::Powerset
<Parma_Polyhedra_Library::Determinate<PSET> > {
public:
  typedef PSET element_type;

private:
  typedef Determinate<PSET> Det_PSET;
  typedef Powerset<Det_PSET> Base;

public:
  //! Returns the maximum space dimension a Pointset_Powerset<PSET> can handle.
  static dimension_type max_space_dimension();

  //! \name Constructors
  //@{

  //! Builds a universe (top) or empty (bottom) Pointset_Powerset.
  /*!
    \param num_dimensions
    The number of dimensions of the vector space enclosing the powerset;

    \param kind
    Specifies whether the universe or the empty powerset has to be built.
  */
  explicit
  Pointset_Powerset(dimension_type num_dimensions = 0,
                    Degenerate_Element kind = UNIVERSE);

  //! Ordinary copy constructor.
  /*!
    The complexity argument is ignored.
  */
  Pointset_Powerset(const Pointset_Powerset& y,
                    Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    Conversion constructor: the type <CODE>QH</CODE> of the disjuncts
    in the source powerset is different from <CODE>PSET</CODE>.

    \param y
    The powerset to be used to build the new powerset.

    \param complexity
    The maximal complexity of any algorithms used.
  */
  template <typename QH>
  explicit Pointset_Powerset(const Pointset_Powerset<QH>& y,
                             Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    Creates a Pointset_Powerset from a product
    This will be created as a single disjunct of type PSET that
    approximates the product.
  */
  template <typename QH1, typename QH2, typename R>
  explicit
  Pointset_Powerset(const Partially_Reduced_Product<QH1, QH2, R>& prp,
                    Complexity_Class complexity = ANY_COMPLEXITY);

  /*! \brief
    Creates a Pointset_Powerset with a single disjunct approximating
    the system of constraints \p cs.
  */
  explicit Pointset_Powerset(const Constraint_System& cs);

  /*! \brief
    Creates a Pointset_Powerset with a single disjunct approximating
    the system of congruences \p cgs.
  */
  explicit Pointset_Powerset(const Congruence_System& cgs);


  //! Builds a pointset_powerset out of a closed polyhedron.
  /*!
    Builds a powerset that is either empty (if the polyhedron is found
    to be empty) or contains a single disjunct approximating the
    polyhedron; this must only use algorithms that do not exceed the
    specified complexity.  The powerset inherits the space dimension
    of the polyhedron.

    \param ph
    The closed polyhedron to be used to build the powerset.

    \param complexity
    The maximal complexity of any algorithms used.

    \exception std::length_error
    Thrown if the space dimension of \p ph exceeds the maximum
    allowed space dimension.
  */
  explicit Pointset_Powerset(const C_Polyhedron& ph,
                             Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a pointset_powerset out of an nnc polyhedron.
  /*!
    Builds a powerset that is either empty (if the polyhedron is found
    to be empty) or contains a single disjunct approximating the
    polyhedron; this must only use algorithms that do not exceed the
    specified complexity.  The powerset inherits the space dimension
    of the polyhedron.

    \param ph
    The closed polyhedron to be used to build the powerset.

    \param complexity
    The maximal complexity of any algorithms used.

    \exception std::length_error
    Thrown if the space dimension of \p ph exceeds the maximum
    allowed space dimension.
  */
  explicit Pointset_Powerset(const NNC_Polyhedron& ph,
                             Complexity_Class complexity = ANY_COMPLEXITY);


  //! Builds a pointset_powerset out of a grid.
  /*!
    If the grid is nonempty, builds a powerset containing a single
    disjunct approximating the grid. Builds the empty powerset
    otherwise. The powerset inherits the space dimension of the grid.

    \param gr
    The grid to be used to build the powerset.

    \param complexity
    This argument is ignored.

    \exception std::length_error
    Thrown if the space dimension of \p gr exceeds the maximum
    allowed space dimension.
  */
  explicit Pointset_Powerset(const Grid& gr,
                             Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a pointset_powerset out of an octagonal shape.
  /*!
    If the octagonal shape is nonempty, builds a powerset
    containing a single disjunct approximating the octagonal
    shape. Builds the empty powerset otherwise. The powerset
    inherits the space dimension of the octagonal shape.

    \param os
    The octagonal shape to be used to build the powerset.

    \param complexity
    This argument is ignored.

    \exception std::length_error
    Thrown if the space dimension of \p os exceeds the maximum
    allowed space dimension.
  */
  template <typename T>
  explicit Pointset_Powerset(const Octagonal_Shape<T>& os,
                             Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a pointset_powerset out of a bd shape.
  /*!
    If the bd shape is nonempty, builds a powerset containing a
    single disjunct approximating the bd shape. Builds the empty
    powerset otherwise.  The powerset inherits the space dimension
    of the bd shape.

    \param bds
    The bd shape to be used to build the powerset.

    \param complexity
    This argument is ignored.

    \exception std::length_error
    Thrown if the space dimension of \p bds exceeds the maximum
    allowed space dimension.
  */
  template <typename T>
  explicit Pointset_Powerset(const BD_Shape<T>& bds,
                             Complexity_Class complexity = ANY_COMPLEXITY);

  //! Builds a pointset_powerset out of a box.
  /*!
    If the box is nonempty, builds a powerset containing a single
    disjunct approximating the box. Builds the empty powerset
    otherwise.  The powerset inherits the space dimension of the box.

    \param box
    The box to be used to build the powerset.

    \param complexity
    This argument is ignored.

    \exception std::length_error
    Thrown if the space dimension of \p box exceeds the maximum
    allowed space dimension.
  */
  template <typename Interval>
  explicit Pointset_Powerset(const Box<Interval>& box,
                             Complexity_Class complexity = ANY_COMPLEXITY);

  //@} // Constructors and Destructor

  //! \name Member Functions that Do Not Modify the Pointset_Powerset
  //@{

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type space_dimension() const;

  //! Returns the dimension of the vector space enclosing \p *this.
  dimension_type affine_dimension() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is
    an empty powerset.
  */
  bool is_empty() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    is the top element of the powerset lattice.
  */
  bool is_universe() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if all the disjuncts
    in \p *this are topologically closed.
  */
  bool is_topologically_closed() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if all elements in \p *this
    are bounded.
  */
  bool is_bounded() const;

  //! Returns <CODE>true</CODE> if and only if \p *this and \p y are disjoint.
  /*!
    \exception std::invalid_argument
    Thrown if \p x and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool is_disjoint_from(const Pointset_Powerset& y) const;

  //! Returns <CODE>true</CODE> if and only if \p *this is discrete.
  bool is_discrete() const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p var is constrained in
    \p *this.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.

    \note
    A variable is constrained if there exists a non-redundant disjunct
    that is constraining the variable: this definition relies on the
    powerset lattice structure and may be somewhat different from the
    geometric intuition.
    For instance, variable \f$x\f$ is constrained in the powerset
    \f[
      \mathit{ps} = \bigl\{ \{ x \geq 0 \}, \{ x \leq 0 \} \bigr\},
    \f]
    even though \f$\mathit{ps}\f$ is geometrically equal to the
    whole vector space.
  */
  bool constrains(Variable var) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p expr is
    bounded from above in \p *this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_above(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p expr is
    bounded from below in \p *this.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.
  */
  bool bounds_from_below(const Linear_Expression& expr) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from above in \p *this, in which case
    the supremum value is computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if and only if the supremum is also the maximum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from above,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d
    and \p maximum are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from above in \p *this, in which case
    the supremum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be maximized subject to \p *this;

    \param sup_n
    The numerator of the supremum value;

    \param sup_d
    The denominator of the supremum value;

    \param maximum
    <CODE>true</CODE> if and only if the supremum is also the maximum value;

    \param g
    When maximization succeeds, will be assigned the point or
    closure point where \p expr reaches its supremum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from above,
    <CODE>false</CODE> is returned and \p sup_n, \p sup_d, \p maximum
    and \p g are left untouched.
  */
  bool maximize(const Linear_Expression& expr,
                Coefficient& sup_n, Coefficient& sup_d, bool& maximum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from below in \p *this, in which case
    the infimum value is computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if and only if the infimum is also the minimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d
    and \p minimum are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum) const;


  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is not empty
    and \p expr is bounded from below in \p *this, in which case
    the infimum value and a point where \p expr reaches it are computed.

    \param expr
    The linear expression to be minimized subject to \p *this;

    \param inf_n
    The numerator of the infimum value;

    \param inf_d
    The denominator of the infimum value;

    \param minimum
    <CODE>true</CODE> if and only if the infimum is also the minimum value;

    \param g
    When minimization succeeds, will be assigned a point or
    closure point where \p expr reaches its infimum value.

    \exception std::invalid_argument
    Thrown if \p expr and \p *this are dimension-incompatible.

    If \p *this is empty or \p expr is not bounded from below,
    <CODE>false</CODE> is returned and \p inf_n, \p inf_d, \p minimum
    and \p g are left untouched.
  */
  bool minimize(const Linear_Expression& expr,
                Coefficient& inf_n, Coefficient& inf_d, bool& minimum,
                Generator& g) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this geometrically
    covers \p y, i.e., if any point (in some element) of \p y is also
    a point (of some element) of \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.

    \warning
    This may be <EM>really</EM> expensive!
  */
  bool geometrically_covers(const Pointset_Powerset& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this is geometrically
    equal to \p y, i.e., if (the elements of) \p *this and \p y
    contain the same set of points.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.

    \warning
    This may be <EM>really</EM> expensive!
  */
  bool geometrically_equals(const Pointset_Powerset& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if each disjunct
      of \p y is contained in a disjunct of \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool contains(const Pointset_Powerset& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if each disjunct
      of \p y is strictly contained in a disjunct of \p *this.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool strictly_contains(const Pointset_Powerset& y) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if \p *this
    contains at least one integer point.
  */
  bool contains_integer_point() const;

  /*! \brief
    Returns the relations holding between the powerset \p *this
    and the constraint \p c.

    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are dimension-incompatible.
  */
  Poly_Con_Relation relation_with(const Constraint& c) const;

  /*! \brief
    Returns the relations holding between the powerset \p *this
    and the generator \p g.

    \exception std::invalid_argument
    Thrown if \p *this and generator \p g are dimension-incompatible.
  */
  Poly_Gen_Relation relation_with(const Generator& g) const;

  /*! \brief
    Returns the relations holding between the powerset \p *this
    and the congruence \p c.

    \exception std::invalid_argument
    Thrown if \p *this and congruence \p c are dimension-incompatible.
  */
  Poly_Con_Relation relation_with(const Congruence& cg) const;

  /*! \brief
    Returns a lower bound to the total size in bytes of the memory
    occupied by \p *this.
  */
  memory_size_type total_memory_in_bytes() const;

  /*! \brief
    Returns a lower bound to the size in bytes of the memory
    managed by \p *this.
  */
  memory_size_type external_memory_in_bytes() const;

  /*! \brief
    Returns a 32-bit hash code for \p *this.

    If \p x and \p y are such that <CODE>x == y</CODE>,
    then <CODE>x.hash_code() == y.hash_code()</CODE>.
  */
  int32_t hash_code() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

  //@} // Member Functions that Do Not Modify the Pointset_Powerset

  //! \name Space Dimension Preserving Member Functions that May Modify the Pointset_Powerset
  //@{

  //! Adds to \p *this the disjunct \p ph.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and \p ph are dimension-incompatible.
  */
  void add_disjunct(const PSET& ph);

  //! Intersects \p *this with constraint \p c.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and constraint \p c are topology-incompatible
    or dimension-incompatible.
  */
  void add_constraint(const Constraint& c);

  /*! \brief
    Use the constraint \p c to refine \p *this.

    \param c
    The constraint to be used for refinement.

    \exception std::invalid_argument
    Thrown if \p *this and \p c are dimension-incompatible.
  */
  void refine_with_constraint(const Constraint& c);

  //! Intersects \p *this with the constraints in \p cs.
  /*!
    \param cs
    The constraints to intersect with.

    \exception std::invalid_argument
    Thrown if \p *this and \p cs are topology-incompatible or
    dimension-incompatible.
  */
  void add_constraints(const Constraint_System& cs);

  /*! \brief
    Use the constraints in \p cs to refine \p *this.

    \param  cs
     The constraints to be used for refinement.

     \exception std::invalid_argument
     Thrown if \p *this and \p cs are dimension-incompatible.
  */
  void refine_with_constraints(const Constraint_System& cs);

  //! Intersects \p *this with congruence \p cg.
  /*!
    \exception std::invalid_argument
    Thrown if \p *this and congruence \p cg are topology-incompatible
    or dimension-incompatible.
  */
  void add_congruence(const Congruence& cg);

  /*! \brief
    Use the congruence \p cg to refine \p *this.

    \param cg
    The congruence to be used for refinement.

    \exception std::invalid_argument
    Thrown if \p *this and \p cg are dimension-incompatible.
  */
  void refine_with_congruence(const Congruence& cg);

  //! Intersects \p *this with the congruences in \p cgs.
  /*!
    \param cgs
    The congruences to intersect with.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are topology-incompatible or
    dimension-incompatible.
  */
  void add_congruences(const Congruence_System& cgs);

  /*! \brief
    Use the congruences in \p cgs to refine \p *this.

    \param  cgs
    The congruences to be used for refinement.

    \exception std::invalid_argument
    Thrown if \p *this and \p cgs are dimension-incompatible.
  */
  void refine_with_congruences(const Congruence_System& cgs);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to space dimension \p var, assigning the result to \p *this.

    \param var
    The space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p var is not a space dimension of \p *this.
  */
  void unconstrain(Variable var);

  /*! \brief
    Computes the \ref Cylindrification "cylindrification" of \p *this with
    respect to the set of space dimensions \p vars,
    assigning the result to \p *this.

    \param vars
    The set of space dimension that will be unconstrained.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars.
  */
  void unconstrain(const Variables_Set& vars);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  /*! \brief
    Possibly tightens \p *this by dropping some points with non-integer
    coordinates for the space dimensions corresponding to \p vars.

    \param vars
    Points with non-integer coordinates for these variables/space-dimensions
    can be discarded.

    \param complexity
    The maximal complexity of any algorithms used.

    \note
    Currently there is no optimality guarantee, not even if
    \p complexity is <CODE>ANY_COMPLEXITY</CODE>.
  */
  void drop_some_non_integer_points(const Variables_Set& vars,
                                    Complexity_Class complexity
                                    = ANY_COMPLEXITY);

  //! Assigns to \p *this its topological closure.
  void topological_closure_assign();

  //! Assigns to \p *this the intersection of \p *this and \p y.
  /*!
    The result is obtained by intersecting each disjunct in \p *this
    with each disjunct in \p y and collecting all these intersections.
  */
  void intersection_assign(const Pointset_Powerset& y);

  /*! \brief
    Assigns to \p *this an (a smallest)
    over-approximation as a powerset of the disjunct domain of the
    set-theoretical difference of \p *this and \p y.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.
  */
  void difference_assign(const Pointset_Powerset& y);

  /*! \brief
    Assigns to \p *this a \ref Powerset_Meet_Preserving_Simplification
    "meet-preserving simplification" of \p *this with respect to \p y.
    If \c false is returned, then the intersection is empty.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are topology-incompatible or
    dimension-incompatible.
  */
  bool simplify_using_context_assign(const Pointset_Powerset& y);

  /*! \brief
    Assigns to \p *this the
    \ref Single_Update_Affine_Functions "affine image"
    of \p *this under the function mapping variable \p var to the
    affine expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is assigned;

    \param expr
    The numerator of the affine expression;

    \param denominator
    The denominator of the affine expression (optional argument with
    default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of
    \p *this.
  */
  void affine_image(Variable var,
                    const Linear_Expression& expr,
                    Coefficient_traits::const_reference denominator
                      = Coefficient_one());

  /*! \brief
    Assigns to \p *this the
    \ref Single_Update_Affine_Functions "affine preimage"
    of \p *this under the function mapping variable \p var to the
    affine expression specified by \p expr and \p denominator.

    \param var
    The variable to which the affine expression is assigned;

    \param expr
    The numerator of the affine expression;

    \param denominator
    The denominator of the affine expression (optional argument with
    default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of
    \p *this.
  */
  void affine_preimage(Variable var,
                    const Linear_Expression& expr,
                    Coefficient_traits::const_reference denominator
                      = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym.

    \param var
    The left hand side variable of the generalized affine relation;

    \param relsym
    The relation symbol;

    \param expr
    The numerator of the right hand side affine expression;

    \param denominator
    The denominator of the right hand side affine expression (optional
    argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void generalized_affine_image(Variable var,
                                Relation_Symbol relsym,
                                const Linear_Expression& expr,
                                Coefficient_traits::const_reference denominator
                                  = Coefficient_one());

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{var}' \relsym \frac{\mathrm{expr}}{\mathrm{denominator}}\f$,
    where \f$\mathord{\relsym}\f$ is the relation symbol encoded
    by \p relsym.

    \param var
    The left hand side variable of the generalized affine relation;

    \param relsym
    The relation symbol;

    \param expr
    The numerator of the right hand side affine expression;

    \param denominator
    The denominator of the right hand side affine expression (optional
    argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p expr and \p *this are
    dimension-incompatible or if \p var is not a space dimension of \p *this
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void
  generalized_affine_preimage(Variable var,
                              Relation_Symbol relsym,
                              const Linear_Expression& expr,
                              Coefficient_traits::const_reference denominator
                              = Coefficient_one());

  /*! \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.

    \param lhs
    The left hand side affine expression;

    \param relsym
    The relation symbol;

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void generalized_affine_image(const Linear_Expression& lhs,
                                Relation_Symbol relsym,
                                const Linear_Expression& rhs);

  /*! \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Generalized_Affine_Relations "generalized affine relation"
    \f$\mathrm{lhs}' \relsym \mathrm{rhs}\f$, where
    \f$\mathord{\relsym}\f$ is the relation symbol encoded by \p relsym.

    \param lhs
    The left hand side affine expression;

    \param relsym
    The relation symbol;

    \param rhs
    The right hand side affine expression.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p lhs or \p rhs
    or if \p *this is a C_Polyhedron and \p relsym is a strict
    relation symbol.
  */
  void generalized_affine_preimage(const Linear_Expression& lhs,
                                   Relation_Symbol relsym,
                                   const Linear_Expression& rhs);

  /*!
    \brief
    Assigns to \p *this the image of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_image(Variable var,
                            const Linear_Expression& lb_expr,
                            const Linear_Expression& ub_expr,
                            Coefficient_traits::const_reference denominator
                            = Coefficient_one());

  /*!
    \brief
    Assigns to \p *this the preimage of \p *this with respect to the
    \ref Single_Update_Bounded_Affine_Relations "bounded affine relation"
    \f$\frac{\mathrm{lb\_expr}}{\mathrm{denominator}}
         \leq \mathrm{var}'
           \leq \frac{\mathrm{ub\_expr}}{\mathrm{denominator}}\f$.

    \param var
    The variable updated by the affine relation;

    \param lb_expr
    The numerator of the lower bounding affine expression;

    \param ub_expr
    The numerator of the upper bounding affine expression;

    \param denominator
    The (common) denominator for the lower and upper bounding
    affine expressions (optional argument with default value 1).

    \exception std::invalid_argument
    Thrown if \p denominator is zero or if \p lb_expr (resp., \p ub_expr)
    and \p *this are dimension-incompatible or if \p var is not a space
    dimension of \p *this.
  */
  void bounded_affine_preimage(Variable var,
                               const Linear_Expression& lb_expr,
                               const Linear_Expression& ub_expr,
                               Coefficient_traits::const_reference denominator
                               = Coefficient_one());

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref Time_Elapse_Operator "time-elapse" between \p *this and \p y.

    The result is obtained by computing the pairwise
    \ref Time_Elapse_Operator "time elapse" of each disjunct
    in \p *this with each disjunct in \p y.
  */
  void time_elapse_assign(const Pointset_Powerset& y);

  /*! \brief
    \ref Wrapping_Operator "Wraps" the specified dimensions of the
    vector space.

    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be wrapped.

    \param w
    The width of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param r
    The representation of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param o
    The overflow behavior of the bounded integer type corresponding to
    all the dimensions to be wrapped.

    \param cs_p
    Possibly null pointer to a constraint system whose variables
    are contained in \p vars.  If <CODE>*cs_p</CODE> depends on
    variables not in \p vars, the behavior is undefined.
    When non-null, the pointed-to constraint system is assumed to
    represent the conditional or looping construct guard with respect
    to which wrapping is performed.  Since wrapping requires the
    computation of upper bounds and due to non-distributivity of
    constraint refinement over upper bounds, passing a constraint
    system in this way can be more precise than refining the result of
    the wrapping operation with the constraints in <CODE>*cs_p</CODE>.

    \param complexity_threshold
    A precision parameter of the \ref Wrapping_Operator "wrapping operator":
    higher values result in possibly improved precision.

    \param wrap_individually
    <CODE>true</CODE> if the dimensions should be wrapped individually
    (something that results in much greater efficiency to the detriment of
    precision).

    \exception std::invalid_argument
    Thrown if <CODE>*cs_p</CODE> is dimension-incompatible with
    \p vars, or if \p *this is dimension-incompatible \p vars or with
    <CODE>*cs_p</CODE>.
  */
  void wrap_assign(const Variables_Set& vars,
                   Bounded_Integer_Type_Width w,
                   Bounded_Integer_Type_Representation r,
                   Bounded_Integer_Type_Overflow o,
                   const Constraint_System* cs_p = 0,
                   unsigned complexity_threshold = 16,
                   bool wrap_individually = true);

  /*! \brief
    Assign to \p *this the result of (recursively) merging together
    the pairs of disjuncts whose upper-bound is the same as their
    set-theoretical union.

    On exit, for all the pairs \f$\cP\f$, \f$\cQ\f$ of different disjuncts
    in \p *this, we have \f$\cP \uplus \cQ \neq \cP \union \cQ\f$.
  */
  void pairwise_reduce();

  /*! \brief
    Assigns to \p *this the result of applying the
    \ref pps_bgp99_extrapolation "BGP99 extrapolation operator"
    to \p *this and \p y, using the widening function \p widen_fun
    and the cardinality threshold \p max_disjuncts.

    \param y
    A powerset that <EM>must</EM> definitely entail \p *this;

    \param widen_fun
    The widening function to be used on polyhedra objects. It is obtained
    from the corresponding widening method by using the helper function
    Parma_Polyhedra_Library::widen_fun_ref. Legal values are, e.g.,
    <CODE>widen_fun_ref(&Polyhedron::H79_widening_assign)</CODE> and
    <CODE>widen_fun_ref(&Polyhedron::limited_H79_extrapolation_assign, cs)</CODE>;

    \param max_disjuncts
    The maximum number of disjuncts occurring in the powerset \p *this
    <EM>before</EM> starting the computation. If this number is exceeded,
    some of the disjuncts in \p *this are collapsed (i.e., joined together).

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.

    For a description of the extrapolation operator,
    see \ref BGP99 "[BGP99]" and \ref BHZ03b "[BHZ03b]".
  */
  template <typename Widening>
  void BGP99_extrapolation_assign(const Pointset_Powerset& y,
                                  Widening widen_fun,
                                  unsigned max_disjuncts);

  /*! \brief
    Assigns to \p *this the result of computing the
    \ref pps_certificate_widening "BHZ03-widening"
    between \p *this and \p y, using the widening function \p widen_fun
    certified by the convergence certificate \p Cert.

    \param y
    The finite powerset computed in the previous iteration step.
    It <EM>must</EM> definitely entail \p *this;

    \param widen_fun
    The widening function to be used on disjuncts.
    It is obtained from the corresponding widening method by using
    the helper function widen_fun_ref. Legal values are, e.g.,
    <CODE>widen_fun_ref(&Polyhedron::H79_widening_assign)</CODE> and
    <CODE>widen_fun_ref(&Polyhedron::limited_H79_extrapolation_assign, cs)</CODE>.

    \exception std::invalid_argument
    Thrown if \p *this and \p y are dimension-incompatible.

    \warning
    In order to obtain a proper widening operator, the template parameter
    \p Cert should be a finite convergence certificate for the base-level
    widening function \p widen_fun; otherwise, an extrapolation operator is
    obtained.
    For a description of the methods that should be provided
    by \p Cert, see BHRZ03_Certificate or H79_Certificate.
  */
  template <typename Cert, typename Widening>
  void BHZ03_widening_assign(const Pointset_Powerset& y, Widening widen_fun);

  //@} // Space Dimension Preserving Member Functions that May Modify [...]

  //! \name Member Functions that May Modify the Dimension of the Vector Space
  //@{

  /*! \brief
    The assignment operator
    (\p *this and \p y can be dimension-incompatible).
  */
  Pointset_Powerset& operator=(const Pointset_Powerset& y);

  /*! \brief
    Conversion assignment: the type <CODE>QH</CODE> of the disjuncts
    in the source powerset is different from <CODE>PSET</CODE>
    (\p *this and \p y can be dimension-incompatible).
  */
  template <typename QH>
  Pointset_Powerset& operator=(const Pointset_Powerset<QH>& y);

  //! Swaps \p *this with \p y.
  void m_swap(Pointset_Powerset& y);

  /*! \brief
    Adds \p m new dimensions to the vector space containing \p *this
    and embeds each disjunct in \p *this in the new space.
  */
  void add_space_dimensions_and_embed(dimension_type m);

  /*! \brief
    Adds \p m new dimensions to the vector space containing \p *this
    without embedding the disjuncts in \p *this in the new space.
  */
  void add_space_dimensions_and_project(dimension_type m);

  //! Assigns to \p *this the concatenation of \p *this and \p y.
  /*!
    The result is obtained by computing the pairwise
    \ref Concatenating_Polyhedra "concatenation" of each disjunct
    in \p *this with each disjunct in \p y.
  */
  void concatenate_assign(const Pointset_Powerset& y);

  //! Removes all the specified space dimensions.
  /*!
    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be removed.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with one of the
    Variable objects contained in \p vars.
  */
  void remove_space_dimensions(const Variables_Set& vars);

  /*! \brief
    Removes the higher space dimensions so that the resulting space
    will have dimension \p new_dimension.

    \exception std::invalid_argument
    Thrown if \p new_dimensions is greater than the space dimension
    of \p *this.
  */
  void remove_higher_space_dimensions(dimension_type new_dimension);

  /*! \brief
    Remaps the dimensions of the vector space according to
    a partial function.

    See also Polyhedron::map_space_dimensions.
  */
  template <typename Partial_Function>
  void map_space_dimensions(const Partial_Function& pfunc);

  //! Creates \p m copies of the space dimension corresponding to \p var.
  /*!
    \param var
    The variable corresponding to the space dimension to be replicated;

    \param m
    The number of replicas to be created.

    \exception std::invalid_argument
    Thrown if \p var does not correspond to a dimension of the vector
    space.

    \exception std::length_error
    Thrown if adding \p m new space dimensions would cause the vector
    space to exceed dimension <CODE>max_space_dimension()</CODE>.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    and <CODE>var</CODE> has space dimension \f$k \leq n\f$,
    then the \f$k\f$-th space dimension is
    \ref Expanding_One_Dimension_of_the_Vector_Space_to_Multiple_Dimensions
    "expanded" to \p m new space dimensions
    \f$n\f$, \f$n+1\f$, \f$\dots\f$, \f$n+m-1\f$.
  */
  void expand_space_dimension(Variable var, dimension_type m);

  //! Folds the space dimensions in \p vars into \p dest.
  /*!
    \param vars
    The set of Variable objects corresponding to the space dimensions
    to be folded;

    \param dest
    The variable corresponding to the space dimension that is the
    destination of the folding operation.

    \exception std::invalid_argument
    Thrown if \p *this is dimension-incompatible with \p dest or with
    one of the Variable objects contained in \p vars.  Also
    thrown if \p dest is contained in \p vars.

    If \p *this has space dimension \f$n\f$, with \f$n > 0\f$,
    <CODE>dest</CODE> has space dimension \f$k \leq n\f$,
    \p vars is a set of variables whose maximum space dimension
    is also less than or equal to \f$n\f$, and \p dest is not a member
    of \p vars, then the space dimensions corresponding to
    variables in \p vars are
    \ref Folding_Multiple_Dimensions_of_the_Vector_Space_into_One_Dimension
    "folded" into the \f$k\f$-th space dimension.
  */
  void fold_space_dimensions(const Variables_Set& vars, Variable dest);

  //@} // Member Functions that May Modify the Dimension of the Vector Space

public:
  typedef typename Base::size_type size_type;
  typedef typename Base::value_type value_type;
  typedef typename Base::iterator iterator;
  typedef typename Base::const_iterator const_iterator;
  typedef typename Base::reverse_iterator reverse_iterator;
  typedef typename Base::const_reverse_iterator const_reverse_iterator;

  PPL_OUTPUT_DECLARATIONS

  /*! \brief
    Loads from \p s an ASCII representation (as produced by
    ascii_dump(std::ostream&) const) and sets \p *this accordingly.
    Returns <CODE>true</CODE> if successful, <CODE>false</CODE> otherwise.
  */
  bool ascii_load(std::istream& s);

private:
  typedef typename Base::Sequence Sequence;
  typedef typename Base::Sequence_iterator Sequence_iterator;
  typedef typename Base::Sequence_const_iterator Sequence_const_iterator;

  //! The number of dimensions of the enclosing vector space.
  dimension_type space_dim;

  /*! \brief
    Assigns to \p dest a \ref Powerset_Meet_Preserving_Simplification
    "powerset meet-preserving enlargement" of itself with respect to
    \p *this.  If \c false is returned, then the intersection is empty.

    \note
    It is assumed that \p *this and \p dest are topology-compatible
    and dimension-compatible.
  */
  bool intersection_preserving_enlarge_element(PSET& dest) const;

  /*! \brief
    Assigns to \p *this the result of applying the BGP99 heuristics
    to \p *this and \p y, using the widening function \p widen_fun.
  */
  template <typename Widening>
  void BGP99_heuristics_assign(const Pointset_Powerset& y, Widening widen_fun);

  //! Records in \p cert_ms the certificates for this set of disjuncts.
  template <typename Cert>
  void collect_certificates(std::map<Cert, size_type,
                                     typename Cert::Compare>& cert_ms) const;

  /*! \brief
    Returns <CODE>true</CODE> if and only if the current set of disjuncts
    is stabilizing with respect to the multiset of certificates \p y_cert_ms.
  */
  template <typename Cert>
  bool is_cert_multiset_stabilizing(const std::map<Cert, size_type,
                                                   typename Cert::Compare>&
                                    y_cert_ms) const;

  /*! \brief
    Template helper: common implementation for constraints
    and congruences.
  */
  template <typename Cons_or_Congr>
  Poly_Con_Relation relation_with_aux(const Cons_or_Congr& c) const;

  // FIXME: here it should be enough to befriend the template constructor
  // template <typename QH>
  // Pointset_Powerset(const Pointset_Powerset<QH>&),
  // but, apparently, this cannot be done.
  friend class Pointset_Powerset<NNC_Polyhedron>;
};

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Pointset_Powerset */
template <typename PSET>
void swap(Pointset_Powerset<PSET>& x, Pointset_Powerset<PSET>& y);

//! Partitions \p q with respect to \p p.
/*! \relates Pointset_Powerset
  Let \p p and \p q be two polyhedra.
  The function returns an object <CODE>r</CODE> of type
  <CODE>std::pair\<PSET, Pointset_Powerset\<NNC_Polyhedron\> \></CODE>
  such that
  - <CODE>r.first</CODE> is the intersection of \p p and \p q;
  - <CODE>r.second</CODE> has the property that all its elements are
    pairwise disjoint and disjoint from \p p;
  - the set-theoretical union of <CODE>r.first</CODE> with all the
    elements of <CODE>r.second</CODE> gives \p q (i.e., <CODE>r</CODE>
    is the representation of a partition of \p q).

  \if Include_Implementation_Details

  See
  <A HREF="http://bugseng.com/products/ppl/Documentation/bibliography#Srivastava93">
  this paper</A> for more information about the implementation.
  \endif
*/
template <typename PSET>
std::pair<PSET, Pointset_Powerset<NNC_Polyhedron> >
linear_partition(const PSET& p, const PSET& q);

/*! \brief
  Returns <CODE>true</CODE> if and only if the union of
  the NNC polyhedra in \p ps contains the NNC polyhedron \p ph.

  \relates Pointset_Powerset
*/
bool
check_containment(const NNC_Polyhedron& ph,
                  const Pointset_Powerset<NNC_Polyhedron>& ps);


/*! \brief
  Partitions the grid \p q with respect to grid \p p if and only if
  such a partition is finite.

  \relates Parma_Polyhedra_Library::Pointset_Powerset
  Let \p p and \p q be two grids.
  The function returns an object <CODE>r</CODE> of type
  <CODE>std::pair\<PSET, Pointset_Powerset\<Grid\> \></CODE>
  such that
  - <CODE>r.first</CODE> is the intersection of \p p and \p q;
  - If there is a finite partition of \p q with respect to \p p
    the Boolean <CODE>finite_partition</CODE> is set to true and
    <CODE>r.second</CODE> has the property that all its elements are
    pairwise disjoint and disjoint from \p p and the set-theoretical
    union of <CODE>r.first</CODE> with all the elements of
    <CODE>r.second</CODE> gives \p q (i.e., <CODE>r</CODE>
    is the representation of a partition of \p q).
  - Otherwise the Boolean <CODE>finite_partition</CODE> is set to false
    and the singleton set that contains \p q is stored in
    <CODE>r.second</CODE>r.
*/
std::pair<Grid, Pointset_Powerset<Grid> >
approximate_partition(const Grid& p, const Grid& q, bool& finite_partition);

/*! \brief
  Returns <CODE>true</CODE> if and only if the union of
  the grids \p ps contains the grid \p g.

  \relates Pointset_Powerset
*/
bool
check_containment(const Grid& ph,
                  const Pointset_Powerset<Grid>& ps);

/*! \brief
  Returns <CODE>true</CODE> if and only if the union of
  the objects in \p ps contains \p ph.

  \relates Pointset_Powerset
  \note
  It is assumed that the template parameter PSET can be converted
  without precision loss into an NNC_Polyhedron; otherwise,
  an incorrect result might be obtained.
*/
template <typename PSET>
bool
check_containment(const PSET& ph, const Pointset_Powerset<PSET>& ps);

// CHECKME: according to the Intel compiler, the declaration of the
// following specialization (of the class template parameter) should come
// before the declaration of the corresponding full specialization
// (where the member template parameter is specialized too).
template <>
template <typename QH>
Pointset_Powerset<NNC_Polyhedron>
::Pointset_Powerset(const Pointset_Powerset<QH>& y,
                    Complexity_Class);

// Non-inline full specializations should be declared here
// so as to inhibit multiple instantiations of the generic template.
template <>
template <>
Pointset_Powerset<NNC_Polyhedron>
::Pointset_Powerset(const Pointset_Powerset<C_Polyhedron>& y,
                    Complexity_Class);

template <>
template <>
Pointset_Powerset<NNC_Polyhedron>
::Pointset_Powerset(const Pointset_Powerset<Grid>& y,
                    Complexity_Class);

template <>
template <>
Pointset_Powerset<C_Polyhedron>
::Pointset_Powerset(const Pointset_Powerset<NNC_Polyhedron>& y,
                    Complexity_Class);

template <>
void
Pointset_Powerset<NNC_Polyhedron>
::difference_assign(const Pointset_Powerset& y);

template <>
void
Pointset_Powerset<Grid>
::difference_assign(const Pointset_Powerset& y);

template <>
bool
Pointset_Powerset<NNC_Polyhedron>
::geometrically_covers(const Pointset_Powerset& y) const;

template <>
bool
Pointset_Powerset<Grid>
::geometrically_covers(const Pointset_Powerset& y) const;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Pointset_Powerset_inlines.hh line 1. */
/* Pointset_Powerset class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Pointset_Powerset_inlines.hh line 35. */
#include <algorithm>
#include <deque>

namespace Parma_Polyhedra_Library {

template <typename PSET>
inline dimension_type
Pointset_Powerset<PSET>::space_dimension() const {
  return space_dim;
}

template <typename PSET>
inline dimension_type
Pointset_Powerset<PSET>::max_space_dimension() {
  return PSET::max_space_dimension();
}

template <typename PSET>
inline
Pointset_Powerset<PSET>::Pointset_Powerset(dimension_type num_dimensions,
                                           Degenerate_Element kind)
  : Base(), space_dim(num_dimensions) {
  Pointset_Powerset& x = *this;
  if (kind == UNIVERSE) {
    x.sequence.push_back(Determinate<PSET>(PSET(num_dimensions, kind)));
  }
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
inline
Pointset_Powerset<PSET>::Pointset_Powerset(const Pointset_Powerset& y,
                                           Complexity_Class)
  : Base(y), space_dim(y.space_dim) {
}

template <typename PSET>
inline
Pointset_Powerset<PSET>::Pointset_Powerset(const C_Polyhedron& ph,
                                           Complexity_Class complexity)
  : Base(), space_dim(ph.space_dimension()) {
  Pointset_Powerset& x = *this;
  if (complexity == ANY_COMPLEXITY) {
    if (ph.is_empty()) {
      return;
    }
  }
  else {
    x.reduced = false;
  }
  x.sequence.push_back(Determinate<PSET>(PSET(ph, complexity)));
  x.reduced = false;
  PPL_ASSERT_HEAVY(OK());
}

template <typename PSET>
inline
Pointset_Powerset<PSET>::Pointset_Powerset(const NNC_Polyhedron& ph,
                                           Complexity_Class complexity)
  : Base(), space_dim(ph.space_dimension()) {
  Pointset_Powerset& x = *this;
  if (complexity == ANY_COMPLEXITY) {
    if (ph.is_empty()) {
      return;
    }
  }
  else {
    x.reduced = false;
  }
  x.sequence.push_back(Determinate<PSET>(PSET(ph, complexity)));
  PPL_ASSERT_HEAVY(OK());
}

template <typename PSET>
inline
Pointset_Powerset<PSET>::Pointset_Powerset(const Grid& gr,
                                           Complexity_Class)
  : Base(), space_dim(gr.space_dimension()) {
  Pointset_Powerset& x = *this;
  if (!gr.is_empty()) {
    x.sequence.push_back(Determinate<PSET>(PSET(gr)));
  }
  PPL_ASSERT_HEAVY(OK());
}

template <typename PSET>
template <typename QH1, typename QH2, typename R>
inline
Pointset_Powerset<PSET>
::Pointset_Powerset(const Partially_Reduced_Product<QH1, QH2, R>& prp,
                    Complexity_Class complexity)
  : Base(), space_dim(prp.space_dimension()) {
  Pointset_Powerset& x = *this;
  if (complexity == ANY_COMPLEXITY) {
    if (prp.is_empty()) {
      return;
    }
  }
  else {
    x.reduced = false;
  }
  x.sequence.push_back(Determinate<PSET>(PSET(prp, complexity)));
  x.reduced = false;
  PPL_ASSERT_HEAVY(OK());
}

template <typename PSET>
template <typename Interval>
Pointset_Powerset<PSET>::Pointset_Powerset(const Box<Interval>& box,
                                           Complexity_Class)
  : Base(), space_dim(box.space_dimension()) {
  Pointset_Powerset& x = *this;
  if (!box.is_empty()) {
    x.sequence.push_back(Determinate<PSET>(PSET(box)));
  }
  PPL_ASSERT_HEAVY(OK());
}

template <typename PSET>
template <typename T>
Pointset_Powerset<PSET>::Pointset_Powerset(const Octagonal_Shape<T>& os,
                                           Complexity_Class)
  : Base(), space_dim(os.space_dimension()) {
  Pointset_Powerset& x = *this;
  if (!os.is_empty()) {
    x.sequence.push_back(Determinate<PSET>(PSET(os)));
  }
  PPL_ASSERT_HEAVY(OK());
}

template <typename PSET>
template <typename T>
Pointset_Powerset<PSET>::Pointset_Powerset(const BD_Shape<T>& bds,
                                           Complexity_Class)
  : Base(), space_dim(bds.space_dimension()) {
  Pointset_Powerset& x = *this;
  if (!bds.is_empty()) {
    x.sequence.push_back(Determinate<PSET>(PSET(bds)));
  }
  PPL_ASSERT_HEAVY(OK());
}

template <typename PSET>
inline
Pointset_Powerset<PSET>::Pointset_Powerset(const Constraint_System& cs)
  : Base(Determinate<PSET>(cs)), space_dim(cs.space_dimension()) {
  PPL_ASSERT_HEAVY(OK());
}

template <typename PSET>
inline
Pointset_Powerset<PSET>::Pointset_Powerset(const Congruence_System& cgs)
  : Base(Determinate<PSET>(cgs)), space_dim(cgs.space_dimension()) {
  PPL_ASSERT_HEAVY(OK());
}

template <typename PSET>
inline Pointset_Powerset<PSET>&
Pointset_Powerset<PSET>::operator=(const Pointset_Powerset& y) {
  Pointset_Powerset& x = *this;
  x.Base::operator=(y);
  x.space_dim = y.space_dim;
  return x;
}

template <typename PSET>
inline void
Pointset_Powerset<PSET>::m_swap(Pointset_Powerset& y) {
  Pointset_Powerset& x = *this;
  x.Base::m_swap(y);
  using std::swap;
  swap(x.space_dim, y.space_dim);
}

template <typename PSET>
template <typename QH>
inline Pointset_Powerset<PSET>&
Pointset_Powerset<PSET>::operator=(const Pointset_Powerset<QH>& y) {
  Pointset_Powerset& x = *this;
  Pointset_Powerset<PSET> ps(y);
  swap(x, ps);
  return x;
}

template <typename PSET>
inline void
Pointset_Powerset<PSET>::intersection_assign(const Pointset_Powerset& y) {
  Pointset_Powerset& x = *this;
  x.pairwise_apply_assign(y,
                          Det_PSET::lift_op_assign(std::mem_fun_ref(&PSET::intersection_assign)));
}

template <typename PSET>
inline void
Pointset_Powerset<PSET>::time_elapse_assign(const Pointset_Powerset& y) {
  Pointset_Powerset& x = *this;
  x.pairwise_apply_assign(y,
                          Det_PSET::lift_op_assign(std::mem_fun_ref(&PSET::time_elapse_assign)));
}

template <typename PSET>
inline Poly_Con_Relation
Pointset_Powerset<PSET>::relation_with(const Constraint& c) const {
  return relation_with_aux(c);
}

template <typename PSET>
inline Poly_Con_Relation
Pointset_Powerset<PSET>::relation_with(const Congruence& cg) const {
  return relation_with_aux(cg);
}

template <typename PSET>
inline bool
Pointset_Powerset<PSET>
::geometrically_covers(const Pointset_Powerset& y) const {
  // This code is only used when PSET is an abstraction of NNC_Polyhedron.
  const Pointset_Powerset<NNC_Polyhedron> xx(*this);
  const Pointset_Powerset<NNC_Polyhedron> yy(y);
  return xx.geometrically_covers(yy);
}

template <typename PSET>
inline bool
Pointset_Powerset<PSET>
::geometrically_equals(const Pointset_Powerset& y) const {
  // This code is only used when PSET is an abstraction of NNC_Polyhedron.
  const Pointset_Powerset<NNC_Polyhedron> xx(*this);
  const Pointset_Powerset<NNC_Polyhedron> yy(y);
  return xx.geometrically_covers(yy) && yy.geometrically_covers(xx);
}

template <>
inline bool
Pointset_Powerset<Grid>
::geometrically_equals(const Pointset_Powerset& y) const {
  const Pointset_Powerset& x = *this;
  return x.geometrically_covers(y) && y.geometrically_covers(x);
}

template <>
inline bool
Pointset_Powerset<NNC_Polyhedron>
::geometrically_equals(const Pointset_Powerset& y) const {
  const Pointset_Powerset& x = *this;
  return x.geometrically_covers(y) && y.geometrically_covers(x);
}

template <typename PSET>
inline memory_size_type
Pointset_Powerset<PSET>::external_memory_in_bytes() const {
  return Base::external_memory_in_bytes();
}

template <typename PSET>
inline memory_size_type
Pointset_Powerset<PSET>::total_memory_in_bytes() const {
  return sizeof(*this) + external_memory_in_bytes();
}

template <typename PSET>
inline int32_t
Pointset_Powerset<PSET>::hash_code() const {
  return hash_code_from_dimension(space_dimension());
}

template <typename PSET>
inline void
Pointset_Powerset<PSET>
::difference_assign(const Pointset_Powerset& y) {
  // This code is only used when PSET is an abstraction of NNC_Polyhedron.
  Pointset_Powerset<NNC_Polyhedron> nnc_this(*this);
  Pointset_Powerset<NNC_Polyhedron> nnc_y(y);
  nnc_this.difference_assign(nnc_y);
  *this = nnc_this;
}

/*! \relates Pointset_Powerset */
template <typename PSET>
inline bool
check_containment(const PSET& ph, const Pointset_Powerset<PSET>& ps) {
  // This code is only used when PSET is an abstraction of NNC_Polyhedron.
  const NNC_Polyhedron ph_nnc = NNC_Polyhedron(ph.constraints());
  const Pointset_Powerset<NNC_Polyhedron> ps_nnc(ps);
  return check_containment(ph_nnc, ps_nnc);
}

/*! \relates Pointset_Powerset */
template <>
inline bool
check_containment(const C_Polyhedron& ph,
                  const Pointset_Powerset<C_Polyhedron>& ps) {
  return check_containment(NNC_Polyhedron(ph),
                           Pointset_Powerset<NNC_Polyhedron>(ps));
}

/*! \relates Pointset_Powerset */
template <typename PSET>
inline void
swap(Pointset_Powerset<PSET>& x, Pointset_Powerset<PSET>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Pointset_Powerset_templates.hh line 1. */
/* Pointset_Powerset class implementation: non-inline template functions.
*/


/* Automatically generated from PPL source file ../src/Pointset_Powerset_templates.hh line 33. */
#include <algorithm>
#include <deque>
#include <string>
#include <iostream>
#include <sstream>
#include <stdexcept>

namespace Parma_Polyhedra_Library {

template <typename PSET>
void
Pointset_Powerset<PSET>::add_disjunct(const PSET& ph) {
  Pointset_Powerset& x = *this;
  if (x.space_dimension() != ph.space_dimension()) {
    std::ostringstream s;
    s << "PPL::Pointset_Powerset<PSET>::add_disjunct(ph):\n"
      << "this->space_dimension() == " << x.space_dimension() << ", "
      << "ph.space_dimension() == " << ph.space_dimension() << ".";
    throw std::invalid_argument(s.str());
  }
  x.sequence.push_back(Determinate<PSET>(ph));
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <>
template <typename QH>
Pointset_Powerset<NNC_Polyhedron>
::Pointset_Powerset(const Pointset_Powerset<QH>& y,
                    Complexity_Class complexity)
  : Base(), space_dim(y.space_dimension()) {
  Pointset_Powerset& x = *this;
  for (typename Pointset_Powerset<QH>::const_iterator i = y.begin(),
         y_end = y.end(); i != y_end; ++i) {
    x.sequence.push_back(Determinate<NNC_Polyhedron>
                         (NNC_Polyhedron(i->pointset(), complexity)));

  }
  // FIXME: If the domain elements can be represented _exactly_ as NNC
  // polyhedra, then having x.reduced = y.reduced is correct. This is
  // the case if the domains are both linear and convex which holds
  // for all the currently supported instantiations except for
  // Grids; for this reason the Grid specialization has a
  // separate implementation.  For any non-linear or non-convex
  // domains (e.g., a domain of Intervals with restrictions or a
  // domain of circles) that may be supported in the future, the
  // assignment x.reduced = y.reduced will be a bug.
  x.reduced = y.reduced;

  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
template <typename QH>
Pointset_Powerset<PSET>
::Pointset_Powerset(const Pointset_Powerset<QH>& y,
                    Complexity_Class complexity)
  : Base(), space_dim(y.space_dimension()) {
  Pointset_Powerset& x = *this;
  for (typename Pointset_Powerset<QH>::const_iterator i = y.begin(),
         y_end = y.end(); i != y_end; ++i) {
    x.sequence.push_back(Determinate<PSET>(PSET(i->pointset(), complexity)));
  }
  // Note: this might be non-reduced even when `y' is known to be
  // omega-reduced, because the constructor of PSET may have made
  // different QH elements to become comparable.
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::concatenate_assign(const Pointset_Powerset& y) {
  Pointset_Powerset& x = *this;
  // Ensure omega-reduction here, since what follows has quadratic complexity.
  x.omega_reduce();
  y.omega_reduce();
  Pointset_Powerset<PSET> new_x(x.space_dim + y.space_dim, EMPTY);
  for (const_iterator xi = x.begin(), x_end = x.end(),
         y_begin = y.begin(), y_end = y.end(); xi != x_end; ) {
    for (const_iterator yi = y_begin; yi != y_end; ++yi) {
      Det_PSET zi = *xi;
      zi.concatenate_assign(*yi);
      PPL_ASSERT_HEAVY(!zi.is_bottom());
      new_x.sequence.push_back(zi);
    }
    ++xi;
    if ((abandon_expensive_computations != 0)
        && (xi != x_end) && (y_begin != y_end)) {
      // Hurry up!
      PSET x_ph = xi->pointset();
      for (++xi; xi != x_end; ++xi) {
        x_ph.upper_bound_assign(xi->pointset());
      }
      const_iterator yi = y_begin;
      PSET y_ph = yi->pointset();
      for (++yi; yi != y_end; ++yi) {
        y_ph.upper_bound_assign(yi->pointset());
      }
      x_ph.concatenate_assign(y_ph);
      swap(x, new_x);
      x.add_disjunct(x_ph);
      PPL_ASSERT_HEAVY(x.OK());
      return;
    }
  }
  swap(x, new_x);
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::add_constraint(const Constraint& c) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().add_constraint(c);
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::refine_with_constraint(const Constraint& c) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().refine_with_constraint(c);
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::add_constraints(const Constraint_System& cs) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().add_constraints(cs);
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::refine_with_constraints(const Constraint_System& cs) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().refine_with_constraints(cs);
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::add_congruence(const Congruence& cg) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().add_congruence(cg);
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::refine_with_congruence(const Congruence& cg) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().refine_with_congruence(cg);
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::add_congruences(const Congruence_System& cgs) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().add_congruences(cgs);
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::refine_with_congruences(const Congruence_System& cgs) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().refine_with_congruences(cgs);
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::unconstrain(const Variable var) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().unconstrain(var);
    x.reduced = false;
  }
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::unconstrain(const Variables_Set& vars) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().unconstrain(vars);
    x.reduced = false;
  }
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::add_space_dimensions_and_embed(dimension_type m) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().add_space_dimensions_and_embed(m);
  }
  x.space_dim += m;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::add_space_dimensions_and_project(dimension_type m) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().add_space_dimensions_and_project(m);
  }
  x.space_dim += m;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::remove_space_dimensions(const Variables_Set& vars) {
  Pointset_Powerset& x = *this;
  Variables_Set::size_type num_removed = vars.size();
  if (num_removed > 0) {
    for (Sequence_iterator si = x.sequence.begin(),
           s_end = x.sequence.end(); si != s_end; ++si) {
      si->pointset().remove_space_dimensions(vars);
      x.reduced = false;
    }
    x.space_dim -= num_removed;
    PPL_ASSERT_HEAVY(x.OK());
  }
}

template <typename PSET>
void
Pointset_Powerset<PSET>
::remove_higher_space_dimensions(dimension_type new_dimension) {
  Pointset_Powerset& x = *this;
  if (new_dimension < x.space_dim) {
    for (Sequence_iterator si = x.sequence.begin(),
           s_end = x.sequence.end(); si != s_end; ++si) {
      si->pointset().remove_higher_space_dimensions(new_dimension);
      x.reduced = false;
    }
    x.space_dim = new_dimension;
    PPL_ASSERT_HEAVY(x.OK());
  }
}

template <typename PSET>
template <typename Partial_Function>
void
Pointset_Powerset<PSET>::map_space_dimensions(const Partial_Function& pfunc) {
  Pointset_Powerset& x = *this;
  if (x.is_bottom()) {
    dimension_type n = 0;
    for (dimension_type i = x.space_dim; i-- > 0; ) {
      dimension_type new_i;
      if (pfunc.maps(i, new_i)) {
        ++n;
      }
    }
    x.space_dim = n;
  }
  else {
    Sequence_iterator s_begin = x.sequence.begin();
    for (Sequence_iterator si = s_begin,
           s_end = x.sequence.end(); si != s_end; ++si) {
      si->pointset().map_space_dimensions(pfunc);
    }
    x.space_dim = s_begin->pointset().space_dimension();
    x.reduced = false;
  }
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::expand_space_dimension(Variable var,
                                                dimension_type m) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().expand_space_dimension(var, m);
  }
  x.space_dim += m;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::fold_space_dimensions(const Variables_Set& vars,
                                               Variable dest) {
  Pointset_Powerset& x = *this;
  Variables_Set::size_type num_folded = vars.size();
  if (num_folded > 0) {
    for (Sequence_iterator si = x.sequence.begin(),
           s_end = x.sequence.end(); si != s_end; ++si) {
      si->pointset().fold_space_dimensions(vars, dest);
    }
  }
  x.space_dim -= num_folded;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::affine_image(Variable var,
                                      const Linear_Expression& expr,
                                      Coefficient_traits::const_reference
                                      denominator) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().affine_image(var, expr, denominator);
    // Note that the underlying domain can apply conservative approximation:
    // that is why it would not be correct to make the loss of reduction
    // conditional on `var' and `expr'.
    x.reduced = false;
  }
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::affine_preimage(Variable var,
                                         const Linear_Expression& expr,
                                         Coefficient_traits::const_reference
                                         denominator) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().affine_preimage(var, expr, denominator);
    // Note that the underlying domain can apply conservative approximation:
    // that is why it would not be correct to make the loss of reduction
    // conditional on `var' and `expr'.
    x.reduced = false;
  }
  PPL_ASSERT_HEAVY(x.OK());
}


template <typename PSET>
void
Pointset_Powerset<PSET>
::generalized_affine_image(const Linear_Expression& lhs,
                           const Relation_Symbol relsym,
                           const Linear_Expression& rhs) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().generalized_affine_image(lhs, relsym, rhs);
    x.reduced = false;
  }
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>
::generalized_affine_preimage(const Linear_Expression& lhs,
                              const Relation_Symbol relsym,
                              const Linear_Expression& rhs) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().generalized_affine_preimage(lhs, relsym, rhs);
    x.reduced = false;
  }
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>
::generalized_affine_image(Variable var,
                           const Relation_Symbol relsym,
                           const Linear_Expression& expr,
                           Coefficient_traits::const_reference denominator) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().generalized_affine_image(var, relsym, expr, denominator);
    x.reduced = false;
  }
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>
::generalized_affine_preimage(Variable var,
                              const Relation_Symbol relsym,
                              const Linear_Expression& expr,
                              Coefficient_traits::const_reference
                              denominator) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().generalized_affine_preimage(var, relsym, expr, denominator);
    x.reduced = false;
  }
  PPL_ASSERT_HEAVY(x.OK());
}


template <typename PSET>
void
Pointset_Powerset<PSET>
::bounded_affine_image(Variable var,
                       const Linear_Expression& lb_expr,
                       const Linear_Expression& ub_expr,
                       Coefficient_traits::const_reference denominator) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().bounded_affine_image(var, lb_expr, ub_expr, denominator);
    x.reduced = false;
  }
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>
::bounded_affine_preimage(Variable var,
                          const Linear_Expression& lb_expr,
                          const Linear_Expression& ub_expr,
                          Coefficient_traits::const_reference denominator) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().bounded_affine_preimage(var, lb_expr, ub_expr,
                                          denominator);
    x.reduced = false;
  }
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
dimension_type
Pointset_Powerset<PSET>::affine_dimension() const {
  // The affine dimension of the powerset is the affine dimension of
  // the smallest vector space in which it can be embedded.
  const Pointset_Powerset& x = *this;
  C_Polyhedron x_ph(space_dim, EMPTY);

  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    PSET pi(si->pointset());
    if (!pi.is_empty()) {
      C_Polyhedron phi(space_dim);
      const Constraint_System& cs = pi.minimized_constraints();
      for (Constraint_System::const_iterator i = cs.begin(),
             cs_end = cs.end(); i != cs_end; ++i) {
        const Constraint& c = *i;
        if (c.is_equality()) {
          phi.add_constraint(c);
        }
      }
      x_ph.poly_hull_assign(phi);
    }
  }

  return x_ph.affine_dimension();
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::is_universe() const {
  const Pointset_Powerset& x = *this;
  // Exploit omega-reduction, if already computed.
  if (x.is_omega_reduced()) {
    return x.size() == 1 && x.begin()->pointset().is_universe();
  }

  // A powerset is universe iff one of its disjuncts is.
  for (const_iterator x_i = x.begin(), x_end = x.end();
       x_i != x_end; ++x_i) {
    if (x_i->pointset().is_universe()) {
      // Speculative omega-reduction, if it is worth.
      if (x.size() > 1) {
        Pointset_Powerset<PSET> universe(x.space_dimension(), UNIVERSE);
        Pointset_Powerset& xx = const_cast<Pointset_Powerset&>(x);
        swap(xx, universe);
      }
      return true;
    }
  }
  return false;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::is_empty() const {
  const Pointset_Powerset& x = *this;
  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    if (!si->pointset().is_empty()) {
      return false;
    }
  }
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::is_discrete() const {
  const Pointset_Powerset& x = *this;
  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    if (!si->pointset().is_discrete()) {
      return false;
    }
  }
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::is_topologically_closed() const {
  const Pointset_Powerset& x = *this;
  // The powerset must be omega-reduced before checking
  // topological closure.
  x.omega_reduce();
  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    if (!si->pointset().is_topologically_closed()) {
      return false;
    }
  }
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::is_bounded() const {
  const Pointset_Powerset& x = *this;
  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    if (!si->pointset().is_bounded()) {
      return false;
    }
  }
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::constrains(Variable var) const {
  const Pointset_Powerset& x = *this;
  // `var' should be one of the dimensions of the powerset.
  const dimension_type var_space_dim = var.space_dimension();
  if (x.space_dimension() < var_space_dim) {
    std::ostringstream s;
    s << "PPL::Pointset_Powerset<PSET>::constrains(v):\n"
      << "this->space_dimension() == " << x.space_dimension() << ", "
      << "v.space_dimension() == " << var_space_dim << ".";
    throw std::invalid_argument(s.str());
  }
  // omega_reduction needed, since a redundant disjunct may constrain var.
  x.omega_reduce();
  // An empty powerset constrains all variables.
  if (x.is_empty()) {
    return true;
  }
  for (const_iterator x_i = x.begin(), x_end = x.end();
       x_i != x_end; ++x_i) {
    if (x_i->pointset().constrains(var)) {
      return true;
    }
  }
  return false;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::is_disjoint_from(const Pointset_Powerset& y) const {
  const Pointset_Powerset& x = *this;
  for (Sequence_const_iterator si = x.sequence.begin(),
         x_s_end = x.sequence.end(); si != x_s_end; ++si) {
    const PSET& pi = si->pointset();
    for (Sequence_const_iterator sj = y.sequence.begin(),
           y_s_end = y.sequence.end(); sj != y_s_end; ++sj) {
      const PSET& pj = sj->pointset();
      if (!pi.is_disjoint_from(pj)) {
        return false;
      }
    }
  }
  return true;
}

template <typename PSET>
void
Pointset_Powerset<PSET>
::drop_some_non_integer_points(const Variables_Set& vars,
                               Complexity_Class complexity) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().drop_some_non_integer_points(vars, complexity);
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>
::drop_some_non_integer_points(Complexity_Class complexity) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().drop_some_non_integer_points(complexity);
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::topological_closure_assign() {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().topological_closure_assign();
  }
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
bool
Pointset_Powerset<PSET>
::intersection_preserving_enlarge_element(PSET& dest) const {
  // FIXME: this is just an executable specification.
  const Pointset_Powerset& context = *this;
  PPL_ASSERT(context.space_dimension() == dest.space_dimension());
  bool nonempty_intersection = false;
  // TODO: maybe use a *sorted* constraint system?
  PSET enlarged(context.space_dimension(), UNIVERSE);
  for (Sequence_const_iterator si = context.sequence.begin(),
         s_end = context.sequence.end(); si != s_end; ++si) {
    PSET context_i(si->pointset());
    context_i.intersection_assign(enlarged);
    PSET enlarged_i(dest);
    if (enlarged_i.simplify_using_context_assign(context_i)) {
      nonempty_intersection = true;
    }
    // TODO: merge the sorted constraints of `enlarged' and `enlarged_i'?
    enlarged.intersection_assign(enlarged_i);
  }
  swap(dest, enlarged);
  return nonempty_intersection;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>
::simplify_using_context_assign(const Pointset_Powerset& y) {
  Pointset_Powerset& x = *this;

  // Omega reduction is required.
  // TODO: check whether it would be more efficient to Omega-reduce x
  // during the simplification process: when examining *si, we check
  // if it has been made redundant by any of the elements preceding it
  // (which have been already simplified).
  x.omega_reduce();
  if (x.is_empty()) {
    return false;
  }
  y.omega_reduce();
  if (y.is_empty()) {
    x = y;
    return false;
  }

  if (y.size() == 1) {
    // More efficient, special handling of the singleton context case.
    const PSET& y_i = y.sequence.begin()->pointset();
    for (Sequence_iterator si = x.sequence.begin(),
           s_end = x.sequence.end(); si != s_end; ) {
      PSET& x_i = si->pointset();
      if (x_i.simplify_using_context_assign(y_i)) {
        ++si;
      }
      else {
        // Intersection is empty: drop the disjunct.
        si = x.sequence.erase(si);
      }
    }
  }
  else {
    // The context is not a singleton.
    for (Sequence_iterator si = x.sequence.begin(),
           s_end = x.sequence.end(); si != s_end; ) {
      if (y.intersection_preserving_enlarge_element(si->pointset())) {
        ++si;
      }
      else {
        // Intersection with `*si' is empty: drop the disjunct.
        si = x.sequence.erase(si);
      }
    }
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
  return !x.sequence.empty();
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::contains(const Pointset_Powerset& y) const {
  const Pointset_Powerset& x = *this;
  for (Sequence_const_iterator si = y.sequence.begin(),
         y_s_end = y.sequence.end(); si != y_s_end; ++si) {
    const PSET& pi = si->pointset();
    bool pi_is_contained = false;
    for (Sequence_const_iterator sj = x.sequence.begin(),
           x_s_end = x.sequence.end();
         (sj != x_s_end && !pi_is_contained);
         ++sj) {
      const PSET& pj = sj->pointset();
      if (pj.contains(pi)) {
        pi_is_contained = true;
      }
    }
    if (!pi_is_contained) {
      return false;
    }
  }
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::strictly_contains(const Pointset_Powerset& y) const {
  /* omega reduction ensures that a disjunct of y cannot be strictly
     contained in one disjunct and also contained but not strictly
     contained in another disjunct of *this */
  const Pointset_Powerset& x = *this;
  x.omega_reduce();
  for (Sequence_const_iterator si = y.sequence.begin(),
         y_s_end = y.sequence.end(); si != y_s_end; ++si) {
    const PSET& pi = si->pointset();
    bool pi_is_strictly_contained = false;
    for (Sequence_const_iterator sj = x.sequence.begin(),
           x_s_end = x.sequence.end();
         (sj != x_s_end && !pi_is_strictly_contained); ++sj) {
      const PSET& pj = sj->pointset();
      if (pj.strictly_contains(pi)) {
        pi_is_strictly_contained = true;
      }
    }
    if (!pi_is_strictly_contained) {
      return false;
    }
  }
  return true;
}

template <typename PSET>
template <typename Cons_or_Congr>
Poly_Con_Relation
Pointset_Powerset<PSET>::relation_with_aux(const Cons_or_Congr& c) const {
  const Pointset_Powerset& x = *this;

  /* *this is included in c if every disjunct is included in c */
  bool is_included = true;
  /* *this is disjoint with c if every disjunct is disjoint with c */
  bool is_disjoint = true;
  /* *this strictly_intersects with c if:
       - some disjunct strictly intersects with c
     or
       - there exists two disjoints d1 and d2
         such that d1 is included in c and d2 is disjoint with c
  */
  bool is_strictly_intersecting = false;
  bool included_once = false;
  bool disjoint_once = false;
  /* *this saturates c if all disjuncts saturate c */
  bool saturates = true;
  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    Poly_Con_Relation relation_i = si->pointset().relation_with(c);
    if (relation_i.implies(Poly_Con_Relation::is_included())) {
      included_once = true;
    }
    else {
      is_included = false;
    }
    if (relation_i.implies(Poly_Con_Relation::is_disjoint())) {
      disjoint_once = true;
    }
    else {
      is_disjoint = false;
    }
    if (relation_i.implies(Poly_Con_Relation::strictly_intersects())) {
      is_strictly_intersecting = true;
    }
    if (!relation_i.implies(Poly_Con_Relation::saturates())) {
      saturates = false;
    }
  }

  Poly_Con_Relation result = Poly_Con_Relation::nothing();
  if (is_included) {
    result = result && Poly_Con_Relation::is_included();
  }
  if (is_disjoint) {
    result = result && Poly_Con_Relation::is_disjoint();
  }
  if (is_strictly_intersecting || (included_once && disjoint_once)) {
    result = result && Poly_Con_Relation::strictly_intersects();
  }
  if (saturates) {
    result = result && Poly_Con_Relation::saturates();
  }
  return result;
}

template <typename PSET>
Poly_Gen_Relation
Pointset_Powerset<PSET>::relation_with(const Generator& g) const {
  const Pointset_Powerset& x = *this;

  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    Poly_Gen_Relation relation_i = si->pointset().relation_with(g);
    if (relation_i.implies(Poly_Gen_Relation::subsumes())) {
      return Poly_Gen_Relation::subsumes();
    }
  }

  return Poly_Gen_Relation::nothing();
}

template <typename PSET>
bool
Pointset_Powerset<PSET>
::bounds_from_above(const Linear_Expression& expr) const {
  const Pointset_Powerset& x = *this;
  x.omega_reduce();
  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    if (!si->pointset().bounds_from_above(expr)) {
      return false;
    }
  }
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>
::bounds_from_below(const Linear_Expression& expr) const {
  const Pointset_Powerset& x = *this;
  x.omega_reduce();
  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    if (!si->pointset().bounds_from_below(expr)) {
      return false;
    }
  }
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::maximize(const Linear_Expression& expr,
                                  Coefficient& sup_n,
                                  Coefficient& sup_d,
                                  bool& maximum) const {
  const Pointset_Powerset& x = *this;
  x.omega_reduce();
  if (x.is_empty()) {
    return false;
  }

  bool first = true;

  PPL_DIRTY_TEMP_COEFFICIENT(best_sup_n);
  PPL_DIRTY_TEMP_COEFFICIENT(best_sup_d);
  best_sup_n = 0;
  best_sup_d = 1;
  bool best_max = false;

  PPL_DIRTY_TEMP_COEFFICIENT(iter_sup_n);
  PPL_DIRTY_TEMP_COEFFICIENT(iter_sup_d);
  iter_sup_n = 0;
  iter_sup_d = 1;
  bool iter_max = false;

  PPL_DIRTY_TEMP_COEFFICIENT(tmp);

  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    if (!si->pointset().maximize(expr, iter_sup_n, iter_sup_d, iter_max)) {
      return false;
    }
    else
      if (first) {
        first = false;
        best_sup_n = iter_sup_n;
        best_sup_d = iter_sup_d;
        best_max = iter_max;
      }
      else {
        tmp = (best_sup_n * iter_sup_d) - (iter_sup_n * best_sup_d);
        if (tmp < 0) {
          best_sup_n = iter_sup_n;
          best_sup_d = iter_sup_d;
          best_max = iter_max;
        }
        else if (tmp == 0) {
          best_max = (best_max || iter_max);
        }
      }
  }
  sup_n = best_sup_n;
  sup_d = best_sup_d;
  maximum = best_max;
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::maximize(const Linear_Expression& expr,
                                  Coefficient& sup_n,
                                  Coefficient& sup_d,
                                  bool& maximum,
                                  Generator& g) const {
  const Pointset_Powerset& x = *this;
  x.omega_reduce();
  if (x.is_empty()) {
    return false;
  }

  bool first = true;

  PPL_DIRTY_TEMP_COEFFICIENT(best_sup_n);
  PPL_DIRTY_TEMP_COEFFICIENT(best_sup_d);
  best_sup_n = 0;
  best_sup_d = 1;
  bool best_max = false;
  Generator best_g = point();

  PPL_DIRTY_TEMP_COEFFICIENT(iter_sup_n);
  PPL_DIRTY_TEMP_COEFFICIENT(iter_sup_d);
  iter_sup_n = 0;
  iter_sup_d = 1;
  bool iter_max = false;
  Generator iter_g = point();

  PPL_DIRTY_TEMP_COEFFICIENT(tmp);

  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    if (!si->pointset().maximize(expr,
                                 iter_sup_n, iter_sup_d, iter_max, iter_g)) {
      return false;
    }
    else {
      if (first) {
        first = false;
        best_sup_n = iter_sup_n;
        best_sup_d = iter_sup_d;
        best_max = iter_max;
        best_g = iter_g;
      }
      else {
        tmp = (best_sup_n * iter_sup_d) - (iter_sup_n * best_sup_d);
        if (tmp < 0) {
          best_sup_n = iter_sup_n;
          best_sup_d = iter_sup_d;
          best_max = iter_max;
          best_g = iter_g;
        }
        else if (tmp == 0) {
          best_max = (best_max || iter_max);
          best_g = iter_g;
        }
      }
    }
  }
  sup_n = best_sup_n;
  sup_d = best_sup_d;
  maximum = best_max;
  g = best_g;
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::minimize(const Linear_Expression& expr,
                                  Coefficient& inf_n,
                                  Coefficient& inf_d,
                                  bool& minimum) const {
  const Pointset_Powerset& x = *this;
  x.omega_reduce();
  if (x.is_empty()) {
    return false;
  }

  bool first = true;

  PPL_DIRTY_TEMP_COEFFICIENT(best_inf_n);
  PPL_DIRTY_TEMP_COEFFICIENT(best_inf_d);
  best_inf_n = 0;
  best_inf_d = 1;
  bool best_min = false;

  PPL_DIRTY_TEMP_COEFFICIENT(iter_inf_n);
  PPL_DIRTY_TEMP_COEFFICIENT(iter_inf_d);
  iter_inf_n = 0;
  iter_inf_d = 1;
  bool iter_min = false;

  PPL_DIRTY_TEMP_COEFFICIENT(tmp);

  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    if (!si->pointset().minimize(expr, iter_inf_n, iter_inf_d, iter_min)) {
      return false;
    }
    else {
      if (first) {
        first = false;
        best_inf_n = iter_inf_n;
        best_inf_d = iter_inf_d;
        best_min = iter_min;
      }
      else {
        tmp = (best_inf_n * iter_inf_d) - (iter_inf_n * best_inf_d);
        if (tmp > 0) {
          best_inf_n = iter_inf_n;
          best_inf_d = iter_inf_d;
          best_min = iter_min;
        }
        else if (tmp == 0) {
          best_min = (best_min || iter_min);
        }
      }
    }
  }
  inf_n = best_inf_n;
  inf_d = best_inf_d;
  minimum = best_min;
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::minimize(const Linear_Expression& expr,
                                  Coefficient& inf_n,
                                  Coefficient& inf_d,
                                  bool& minimum,
                                  Generator& g) const {
  const Pointset_Powerset& x = *this;
  x.omega_reduce();
  if (x.is_empty()) {
    return false;
  }

  bool first = true;

  PPL_DIRTY_TEMP_COEFFICIENT(best_inf_n);
  PPL_DIRTY_TEMP_COEFFICIENT(best_inf_d);
  best_inf_n = 0;
  best_inf_d = 1;
  bool best_min = false;
  Generator best_g = point();

  PPL_DIRTY_TEMP_COEFFICIENT(iter_inf_n);
  PPL_DIRTY_TEMP_COEFFICIENT(iter_inf_d);
  iter_inf_n = 0;
  iter_inf_d = 1;
  bool iter_min = false;
  Generator iter_g = point();

  PPL_DIRTY_TEMP_COEFFICIENT(tmp);

  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    if (!si->pointset().minimize(expr,
                                 iter_inf_n, iter_inf_d, iter_min, iter_g)) {
      return false;
    }
    else {
      if (first) {
        first = false;
        best_inf_n = iter_inf_n;
        best_inf_d = iter_inf_d;
        best_min = iter_min;
        best_g = iter_g;
      }
      else {
        tmp = (best_inf_n * iter_inf_d) - (iter_inf_n * best_inf_d);
        if (tmp > 0) {
          best_inf_n = iter_inf_n;
          best_inf_d = iter_inf_d;
          best_min = iter_min;
          best_g = iter_g;
        }
        else if (tmp == 0) {
          best_min = (best_min || iter_min);
          best_g = iter_g;
        }
      }
    }
  }
  inf_n = best_inf_n;
  inf_d = best_inf_d;
  minimum = best_min;
  g = best_g;
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::contains_integer_point() const {
  const Pointset_Powerset& x = *this;
  for (Sequence_const_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    if (si->pointset().contains_integer_point()) {
      return true;
    }
  }
  return false;
}

template <typename PSET>
void
Pointset_Powerset<PSET>::wrap_assign(const Variables_Set& vars,
                                     Bounded_Integer_Type_Width w,
                                     Bounded_Integer_Type_Representation r,
                                     Bounded_Integer_Type_Overflow o,
                                     const Constraint_System* cs_p,
                                     unsigned complexity_threshold,
                                     bool wrap_individually) {
  Pointset_Powerset& x = *this;
  for (Sequence_iterator si = x.sequence.begin(),
         s_end = x.sequence.end(); si != s_end; ++si) {
    si->pointset().wrap_assign(vars, w, r, o, cs_p,
                               complexity_threshold, wrap_individually);
  }
  x.reduced = false;
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
void
Pointset_Powerset<PSET>::pairwise_reduce() {
  Pointset_Powerset& x = *this;
  // It is wise to omega-reduce before pairwise-reducing.
  x.omega_reduce();

  size_type n = x.size();
  size_type deleted;
  do {
    Pointset_Powerset new_x(x.space_dim, EMPTY);
    std::deque<bool> marked(n, false);
    deleted = 0;
    Sequence_iterator s_begin = x.sequence.begin();
    Sequence_iterator s_end = x.sequence.end();
    unsigned si_index = 0;
    for (Sequence_iterator si = s_begin; si != s_end; ++si, ++si_index) {
      if (marked[si_index]) {
        continue;
      }
      PSET& pi = si->pointset();
      Sequence_const_iterator sj = si;
      unsigned sj_index = si_index;
      for (++sj, ++sj_index; sj != s_end; ++sj, ++sj_index) {
        if (marked[sj_index]) {
          continue;
        }
        const PSET& pj = sj->pointset();
        if (pi.upper_bound_assign_if_exact(pj)) {
          marked[si_index] = true;
          marked[sj_index] = true;
          new_x.add_non_bottom_disjunct_preserve_reduction(pi);
          ++deleted;
          goto next;
        }
      }
    next:
      ;
    }
    iterator new_x_begin = new_x.begin();
    iterator new_x_end = new_x.end();
    unsigned xi_index = 0;
    for (const_iterator xi = x.begin(),
           x_end = x.end(); xi != x_end; ++xi, ++xi_index) {
      if (!marked[xi_index]) {
        new_x_begin
          = new_x.add_non_bottom_disjunct_preserve_reduction(*xi,
                                                             new_x_begin,
                                                             new_x_end);
      }
    }
    using std::swap;
    swap(x.sequence, new_x.sequence);
    n -= deleted;
  } while (deleted > 0);
  PPL_ASSERT_HEAVY(x.OK());
}

template <typename PSET>
template <typename Widening>
void
Pointset_Powerset<PSET>::
BGP99_heuristics_assign(const Pointset_Powerset& y, Widening widen_fun) {
  // `x' is the current iteration value.
  Pointset_Powerset& x = *this;

#ifndef NDEBUG
  {
    // We assume that `y' entails `x'.
    const Pointset_Powerset<PSET> x_copy = x;
    const Pointset_Powerset<PSET> y_copy = y;
    PPL_ASSERT_HEAVY(y_copy.definitely_entails(x_copy));
  }
#endif

  size_type n = x.size();
  Pointset_Powerset new_x(x.space_dim, EMPTY);
  std::deque<bool> marked(n, false);
  const_iterator x_begin = x.begin();
  const_iterator x_end = x.end();
  unsigned i_index = 0;
  for (const_iterator i = x_begin,
         y_begin = y.begin(), y_end = y.end();
         i != x_end; ++i, ++i_index) {
    for (const_iterator j = y_begin; j != y_end; ++j) {
      const PSET& pi = i->pointset();
      const PSET& pj = j->pointset();
      if (pi.contains(pj)) {
        PSET pi_copy = pi;
        widen_fun(pi_copy, pj);
        new_x.add_non_bottom_disjunct_preserve_reduction(pi_copy);
        marked[i_index] = true;
      }
    }
  }
  iterator new_x_begin = new_x.begin();
  iterator new_x_end = new_x.end();
  i_index = 0;
  for (const_iterator i = x_begin; i != x_end; ++i, ++i_index) {
    if (!marked[i_index]) {
      new_x_begin
        = new_x.add_non_bottom_disjunct_preserve_reduction(*i,
                                                           new_x_begin,
                                                           new_x_end);
    }
  }
  using std::swap;
  swap(x.sequence, new_x.sequence);
  PPL_ASSERT_HEAVY(x.OK());
  PPL_ASSERT(x.is_omega_reduced());
}

template <typename PSET>
template <typename Widening>
void
Pointset_Powerset<PSET>::
BGP99_extrapolation_assign(const Pointset_Powerset& y,
                           Widening widen_fun,
                           unsigned max_disjuncts) {
  // `x' is the current iteration value.
  Pointset_Powerset& x = *this;

#ifndef NDEBUG
  {
    // We assume that `y' entails `x'.
    const Pointset_Powerset<PSET> x_copy = x;
    const Pointset_Powerset<PSET> y_copy = y;
    PPL_ASSERT_HEAVY(y_copy.definitely_entails(x_copy));
  }
#endif

  x.pairwise_reduce();
  if (max_disjuncts != 0) {
    x.collapse(max_disjuncts);
  }
  x.BGP99_heuristics_assign(y, widen_fun);
}

template <typename PSET>
template <typename Cert>
void
Pointset_Powerset<PSET>::
collect_certificates(std::map<Cert, size_type,
                     typename Cert::Compare>& cert_ms) const {
  const Pointset_Powerset& x = *this;
  PPL_ASSERT(x.is_omega_reduced());
  PPL_ASSERT(cert_ms.size() == 0);
  for (const_iterator i = x.begin(), end = x.end(); i != end; ++i) {
    Cert ph_cert(i->pointset());
    ++cert_ms[ph_cert];
  }
}

template <typename PSET>
template <typename Cert>
bool
Pointset_Powerset<PSET>::
is_cert_multiset_stabilizing(const std::map<Cert, size_type,
                             typename Cert::Compare>& y_cert_ms) const {
  typedef std::map<Cert, size_type, typename Cert::Compare> Cert_Multiset;
  Cert_Multiset x_cert_ms;
  collect_certificates(x_cert_ms);
  typename Cert_Multiset::const_iterator xi = x_cert_ms.begin();
  typename Cert_Multiset::const_iterator x_cert_ms_end = x_cert_ms.end();
  typename Cert_Multiset::const_iterator yi = y_cert_ms.begin();
  typename Cert_Multiset::const_iterator y_cert_ms_end = y_cert_ms.end();
  while (xi != x_cert_ms_end && yi != y_cert_ms_end) {
    const Cert& xi_cert = xi->first;
    const Cert& yi_cert = yi->first;
    switch (xi_cert.compare(yi_cert)) {
    case 0:
      // xi_cert == yi_cert: check the number of multiset occurrences.
      {
        const size_type& xi_count = xi->second;
        const size_type& yi_count = yi->second;
        if (xi_count == yi_count) {
          // Same number of occurrences: compare the next pair.
          ++xi;
          ++yi;
        }
        else {
          // Different number of occurrences: can decide ordering.
          return xi_count < yi_count;
        }
        break;
      }
    case 1:
      // xi_cert > yi_cert: it is not stabilizing.
      return false;

    case -1:
      // xi_cert < yi_cert: it is stabilizing.
      return true;
    }
  }
  // Here xi == x_cert_ms_end or yi == y_cert_ms_end.
  // Stabilization is achieved if `y_cert_ms' still has other elements.
  return yi != y_cert_ms_end;
}

template <typename PSET>
template <typename Cert, typename Widening>
void
Pointset_Powerset<PSET>::BHZ03_widening_assign(const Pointset_Powerset& y,
                                               Widening widen_fun) {
  // `x' is the current iteration value.
  Pointset_Powerset& x = *this;

#ifndef NDEBUG
  {
    // We assume that `y' entails `x'.
    const Pointset_Powerset<PSET> x_copy = x;
    const Pointset_Powerset<PSET> y_copy = y;
    PPL_ASSERT_HEAVY(y_copy.definitely_entails(x_copy));
  }
#endif

  // First widening technique: do nothing.

  // If `y' is the empty collection, do nothing.
  PPL_ASSERT(x.size() > 0);
  if (y.size() == 0) {
    return;
  }

  // Compute the poly-hull of `x'.
  PSET x_hull(x.space_dim, EMPTY);
  for (const_iterator i = x.begin(), x_end = x.end(); i != x_end; ++i) {
    x_hull.upper_bound_assign(i->pointset());
  }

  // Compute the poly-hull of `y'.
  PSET y_hull(y.space_dim, EMPTY);
  for (const_iterator i = y.begin(), y_end = y.end(); i != y_end; ++i) {
    y_hull.upper_bound_assign(i->pointset());
  }
  // Compute the certificate for `y_hull'.
  const Cert y_hull_cert(y_hull);

  // If the hull is stabilizing, do nothing.
  int hull_stabilization = y_hull_cert.compare(x_hull);
  if (hull_stabilization == 1) {
    return;
  }

  // Multiset ordering is only useful when `y' is not a singleton.
  const bool y_is_not_a_singleton = y.size() > 1;

  // The multiset certificate for `y':
  // we want to be lazy about its computation.
  typedef std::map<Cert, size_type, typename Cert::Compare> Cert_Multiset;
  Cert_Multiset y_cert_ms;
  bool y_cert_ms_computed = false;

  if (hull_stabilization == 0 && y_is_not_a_singleton) {
    // Collect the multiset certificate for `y'.
    y.collect_certificates(y_cert_ms);
    y_cert_ms_computed = true;
    // If multiset ordering is stabilizing, do nothing.
    if (x.is_cert_multiset_stabilizing(y_cert_ms)) {
      return;
    }
  }

  // Second widening technique: try the BGP99 powerset heuristics.
  Pointset_Powerset<PSET> bgp99_heuristics = x;
  bgp99_heuristics.BGP99_heuristics_assign(y, widen_fun);

  // Compute the poly-hull of `bgp99_heuristics'.
  PSET bgp99_heuristics_hull(x.space_dim, EMPTY);
  for (const_iterator i = bgp99_heuristics.begin(),
         b_h_end = bgp99_heuristics.end(); i != b_h_end; ++i) {
    bgp99_heuristics_hull.upper_bound_assign(i->pointset());
  }

  // Check for stabilization and, if successful,
  // commit to the result of the extrapolation.
  hull_stabilization = y_hull_cert.compare(bgp99_heuristics_hull);
  if (hull_stabilization == 1) {
    // The poly-hull is stabilizing.
    swap(x, bgp99_heuristics);
    return;
  }
  else if (hull_stabilization == 0 && y_is_not_a_singleton) {
    // If not already done, compute multiset certificate for `y'.
    if (!y_cert_ms_computed) {
      y.collect_certificates(y_cert_ms);
      y_cert_ms_computed = true;
    }
    if (bgp99_heuristics.is_cert_multiset_stabilizing(y_cert_ms)) {
      swap(x, bgp99_heuristics);
      return;
    }
    // Third widening technique: pairwise-reduction on `bgp99_heuristics'.
    // Note that pairwise-reduction does not affect the computation
    // of the poly-hulls, so that we only have to check the multiset
    // certificate relation.
    Pointset_Powerset<PSET> reduced_bgp99_heuristics(bgp99_heuristics);
    reduced_bgp99_heuristics.pairwise_reduce();
    if (reduced_bgp99_heuristics.is_cert_multiset_stabilizing(y_cert_ms)) {
      swap(x, reduced_bgp99_heuristics);
      return;
    }
  }

  // Fourth widening technique: this is applicable only when
  // `y_hull' is a proper subset of `bgp99_heuristics_hull'.
  if (bgp99_heuristics_hull.strictly_contains(y_hull)) {
    // Compute (y_hull \widen bgp99_heuristics_hull).
    PSET ph = bgp99_heuristics_hull;
    widen_fun(ph, y_hull);
    // Compute the difference between `ph' and `bgp99_heuristics_hull'.
    ph.difference_assign(bgp99_heuristics_hull);
    x.add_disjunct(ph);
    return;
  }

  // Fall back to the computation of the poly-hull.
  Pointset_Powerset<PSET> x_hull_singleton(x.space_dim, EMPTY);
  x_hull_singleton.add_disjunct(x_hull);
  swap(x, x_hull_singleton);
}

template <typename PSET>
void
Pointset_Powerset<PSET>::ascii_dump(std::ostream& s) const {
  const Pointset_Powerset& x = *this;
  s << "size " << x.size()
    << "\nspace_dim " << x.space_dim
    << "\n";
  for (const_iterator xi = x.begin(), x_end = x.end();
       xi != x_end; ++xi) {
    xi->pointset().ascii_dump(s);
  }
}

PPL_OUTPUT_TEMPLATE_DEFINITIONS(PSET, Pointset_Powerset<PSET>)

template <typename PSET>
bool
Pointset_Powerset<PSET>::ascii_load(std::istream& s) {
  Pointset_Powerset& x = *this;
  std::string str;

  if (!(s >> str) || str != "size") {
    return false;
  }

  size_type sz;

  if (!(s >> sz)) {
    return false;
  }

  if (!(s >> str) || str != "space_dim") {
    return false;
  }

  if (!(s >> x.space_dim)) {
    return false;
  }

  Pointset_Powerset new_x(x.space_dim, EMPTY);
  while (sz-- > 0) {
    PSET ph;
    if (!ph.ascii_load(s)) {
      return false;
    }
    new_x.add_disjunct(ph);
  }
  swap(x, new_x);

  // Check invariants.
  PPL_ASSERT_HEAVY(x.OK());
  return true;
}

template <typename PSET>
bool
Pointset_Powerset<PSET>::OK() const {
  const Pointset_Powerset& x = *this;
  for (const_iterator xi = x.begin(), x_end = x.end(); xi != x_end; ++xi) {
    const PSET& pi = xi->pointset();
    if (pi.space_dimension() != x.space_dim) {
#ifndef NDEBUG
      std::cerr << "Space dimension mismatch: is " << pi.space_dimension()
                << " in an element of the sequence,\nshould be "
                << x.space_dim << "."
                << std::endl;
#endif
      return false;
    }
  }
  return x.Base::OK();
}

namespace Implementation {

namespace Pointset_Powersets {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
//! Partitions polyhedron \p pset according to constraint \p c.
/*! \relates Parma_Polyhedra_Library::Pointset_Powerset
  On exit, the intersection of \p pset and constraint \p c is stored
  in \p pset, whereas the intersection of \p pset with the negation of \p c
  is added as a new disjunct of the powerset \p r.
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename PSET>
void
linear_partition_aux(const Constraint& c,
                     PSET& pset,
                     Pointset_Powerset<NNC_Polyhedron>& r) {
  const Linear_Expression le(c.expression());
  const Constraint& neg_c = c.is_strict_inequality() ? (le <= 0) : (le < 0);
  NNC_Polyhedron nnc_ph_pset(pset);
  nnc_ph_pset.add_constraint(neg_c);
  if (!nnc_ph_pset.is_empty()) {
    r.add_disjunct(nnc_ph_pset);
  }
  pset.add_constraint(c);
}

} // namespace Pointset_Powersets

} // namespace Implementation


/*! \relates Pointset_Powerset */
template <typename PSET>
std::pair<PSET, Pointset_Powerset<NNC_Polyhedron> >
linear_partition(const PSET& p, const PSET& q) {
  using Implementation::Pointset_Powersets::linear_partition_aux;

  Pointset_Powerset<NNC_Polyhedron> r(p.space_dimension(), EMPTY);
  PSET pset = q;
  const Constraint_System& p_constraints = p.constraints();
  for (Constraint_System::const_iterator i = p_constraints.begin(),
         p_constraints_end = p_constraints.end();
       i != p_constraints_end;
       ++i) {
    const Constraint& c = *i;
    if (c.is_equality()) {
      const Linear_Expression le(c.expression());
      linear_partition_aux(le <= 0, pset, r);
      linear_partition_aux(le >= 0, pset, r);
    }
    else {
      linear_partition_aux(c, pset, r);
    }
  }
  return std::make_pair(pset, r);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Pointset_Powerset_defs.hh line 1455. */

/* Automatically generated from PPL source file ../src/algorithms.hh line 29. */
#include <utility>

namespace Parma_Polyhedra_Library {

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \brief
  If the poly-hull of \p p and \p q is exact it is assigned
  to \p p and <CODE>true</CODE> is returned,
  otherwise <CODE>false</CODE> is returned.

  \relates Polyhedron
*/
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename PH>
bool
poly_hull_assign_if_exact(PH& p, const PH& q);

#ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
/*! \relates Polyhedron */
#endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS)
template <typename PH>
bool
poly_hull_assign_if_exact(PH& p, const PH& q) {
  PH poly_hull = p;
  NNC_Polyhedron nnc_p(p);
  poly_hull.poly_hull_assign(q);
  std::pair<PH, Pointset_Powerset<NNC_Polyhedron> >
    partition = linear_partition(q, poly_hull);
  const Pointset_Powerset<NNC_Polyhedron>& s = partition.second;
  typedef Pointset_Powerset<NNC_Polyhedron>::const_iterator iter;
  for (iter i = s.begin(), s_end = s.end(); i != s_end; ++i) {
    // The polyhedral hull is exact if and only if all the elements
    // of the partition of the polyhedral hull of `p' and `q' with
    // respect to `q' are included in `p'
    if (!nnc_p.contains(i->pointset())) {
      return false;
    }
  }
  p = poly_hull;
  return true;
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/termination_defs.hh line 1. */
/* Utilities for termination analysis: declarations.
*/


/* Automatically generated from PPL source file ../src/termination_defs.hh line 28. */

/* Automatically generated from PPL source file ../src/termination_defs.hh line 33. */

namespace Parma_Polyhedra_Library {

class Termination_Helpers {
public:
  static void
  all_affine_ranking_functions_PR(const Constraint_System& cs_before,
                                  const Constraint_System& cs_after,
                                  NNC_Polyhedron& mu_space);
  static bool
  one_affine_ranking_function_PR(const Constraint_System& cs_before,
                                 const Constraint_System& cs_after,
                                 Generator& mu);
  static bool
  one_affine_ranking_function_PR_original(const Constraint_System& cs,
                                          Generator& mu);
  static void
  all_affine_ranking_functions_PR_original(const Constraint_System& cs,
                                           NNC_Polyhedron& mu_space);

  template <typename PSET>
  static void
  assign_all_inequalities_approximation(const PSET& pset_before,
                                        const PSET& pset_after,
                                        Constraint_System& cs);
}; // class Termination_Helpers

//! \name Functions for the Synthesis of Linear Rankings
//@{

/*! \ingroup PPL_CXX_interface \brief
  Termination test using an improvement of the method by Mesnard and
  Serebrenik \ref BMPZ10 "[BMPZ10]".

  \tparam PSET
  Any pointset supported by the PPL that provides the
  <CODE>minimized_constraints()</CODE> method.

  \param pset
  A pointset approximating the behavior of a loop whose termination
  is being analyzed.  The variables indices are allocated as follows:
  - \f$ x'_1, \ldots, x'_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$,
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ n, \ldots, 2n-1 \f$,
  .
  where unprimed variables represent the values of the loop-relevant
  program variables before the update performed in the loop body,
  and primed variables represent the values of those program variables
  after the update.

  \return
  <CODE>true</CODE> if any loop approximated by \p pset definitely
  terminates; <CODE>false</CODE> if the test is inconclusive.
  However, if \p pset <EM>precisely</EM> characterizes the effect
  of the loop body onto the loop-relevant program variables,
  then <CODE>true</CODE> is returned <EM>if and only if</EM>
  the loop terminates.
*/
template <typename PSET>
bool
termination_test_MS(const PSET& pset);

/*! \ingroup PPL_CXX_interface \brief
  Termination test using an improvement of the method by Mesnard and
  Serebrenik \ref BMPZ10 "[BMPZ10]".

  \tparam PSET
  Any pointset supported by the PPL that provides the
  <CODE>minimized_constraints()</CODE> method.

  \param pset_before
  A pointset approximating the values of loop-relevant variables
  <EM>before</EM> the update performed in the loop body that is being
  analyzed.  The variables indices are allocated as follows:
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$.

  \param pset_after
  A pointset approximating the values of loop-relevant variables
  <EM>after</EM> the update performed in the loop body that is being
  analyzed.  The variables indices are allocated as follows:
  - \f$ x'_1, \ldots, x'_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$,
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ n, \ldots, 2n-1 \f$,

  Note that unprimed variables represent the values of the loop-relevant
  program variables before the update performed in the loop body,
  and primed variables represent the values of those program variables
  after the update.  Note also that unprimed variables are assigned
  to different space dimensions in \p pset_before and \p pset_after.

  \return
  <CODE>true</CODE> if any loop approximated by \p pset definitely
  terminates; <CODE>false</CODE> if the test is inconclusive.
  However, if \p pset_before and \p pset_after <EM>precisely</EM>
  characterize the effect of the loop body onto the loop-relevant
  program variables, then <CODE>true</CODE> is returned
  <EM>if and only if</EM> the loop terminates.
*/
template <typename PSET>
bool
termination_test_MS_2(const PSET& pset_before, const PSET& pset_after);

/*! \ingroup PPL_CXX_interface \brief
  Termination test with witness ranking function using an improvement
  of the method by Mesnard and Serebrenik \ref BMPZ10 "[BMPZ10]".

  \tparam PSET
  Any pointset supported by the PPL that provides the
  <CODE>minimized_constraints()</CODE> method.

  \param pset
  A pointset approximating the behavior of a loop whose termination
  is being analyzed.  The variables indices are allocated as follows:
  - \f$ x'_1, \ldots, x'_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$,
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ n, \ldots, 2n-1 \f$,
  .
  where unprimed variables represent the values of the loop-relevant
  program variables before the update performed in the loop body,
  and primed variables represent the values of those program variables
  after the update.

  \param mu
  When <CODE>true</CODE> is returned, this is assigned a point
  of space dimension \f$ n+1 \f$ encoding one (not further specified)
  affine ranking function for the loop being analyzed.
  The ranking function is of the form \f$ \mu_0 + \sum_{i=1}^n \mu_i x_i \f$
  where \f$ \mu_0, \mu_1, \ldots, \mu_n \f$ are the coefficients
  of \p mu corresponding to the space dimensions \f$ n, 0, \ldots, n-1 \f$,
  respectively.

  \return
  <CODE>true</CODE> if any loop approximated by \p pset definitely
  terminates; <CODE>false</CODE> if the test is inconclusive.
  However, if \p pset <EM>precisely</EM> characterizes the effect
  of the loop body onto the loop-relevant program variables,
  then <CODE>true</CODE> is returned <EM>if and only if</EM>
  the loop terminates.
*/
template <typename PSET>
bool
one_affine_ranking_function_MS(const PSET& pset, Generator& mu);

/*! \ingroup PPL_CXX_interface \brief
  Termination test with witness ranking function using an improvement
  of the method by Mesnard and Serebrenik \ref BMPZ10 "[BMPZ10]".

  \tparam PSET
  Any pointset supported by the PPL that provides the
  <CODE>minimized_constraints()</CODE> method.

  \param pset_before
  A pointset approximating the values of loop-relevant variables
  <EM>before</EM> the update performed in the loop body that is being
  analyzed.  The variables indices are allocated as follows:
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$.

  \param pset_after
  A pointset approximating the values of loop-relevant variables
  <EM>after</EM> the update performed in the loop body that is being
  analyzed.  The variables indices are allocated as follows:
  - \f$ x'_1, \ldots, x'_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$,
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ n, \ldots, 2n-1 \f$,

  Note that unprimed variables represent the values of the loop-relevant
  program variables before the update performed in the loop body,
  and primed variables represent the values of those program variables
  after the update.  Note also that unprimed variables are assigned
  to different space dimensions in \p pset_before and \p pset_after.

  \param mu
  When <CODE>true</CODE> is returned, this is assigned a point
  of space dimension \f$ n+1 \f$ encoding one (not further specified)
  affine ranking function for the loop being analyzed.
  The ranking function is of the form \f$ \mu_0 + \sum_{i=1}^n \mu_i x_i \f$
  where \f$ \mu_0, \mu_1, \ldots, \mu_n \f$ are the coefficients
  of \p mu corresponding to the space dimensions \f$ n, 0, \ldots, n-1 \f$,
  respectively.

  \return
  <CODE>true</CODE> if any loop approximated by \p pset definitely
  terminates; <CODE>false</CODE> if the test is inconclusive.
  However, if \p pset_before and \p pset_after <EM>precisely</EM>
  characterize the effect of the loop body onto the loop-relevant
  program variables, then <CODE>true</CODE> is returned
  <EM>if and only if</EM> the loop terminates.
*/
template <typename PSET>
bool
one_affine_ranking_function_MS_2(const PSET& pset_before,
                                 const PSET& pset_after,
                                 Generator& mu);

/*! \ingroup PPL_CXX_interface \brief
  Termination test with ranking function space using an improvement
  of the method by Mesnard and Serebrenik \ref BMPZ10 "[BMPZ10]".

  \tparam PSET
  Any pointset supported by the PPL that provides the
  <CODE>minimized_constraints()</CODE> method.

  \param pset
  A pointset approximating the behavior of a loop whose termination
  is being analyzed.  The variables indices are allocated as follows:
  - \f$ x'_1, \ldots, x'_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$,
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ n, \ldots, 2n-1 \f$,
  .
  where unprimed variables represent the values of the loop-relevant
  program variables before the update performed in the loop body,
  and primed variables represent the values of those program variables
  after the update.

  \param mu_space
  This is assigned a closed polyhedron of space dimension \f$ n+1 \f$
  representing the space of all the affine ranking functions for the loops
  that are precisely characterized by \p pset.
  These ranking functions are of the form
  \f$ \mu_0 + \sum_{i=1}^n \mu_i x_i \f$
  where \f$ \mu_0, \mu_1, \ldots, \mu_n \f$ identify any point of the
  \p mu_space polyhedron.
  The variables \f$ \mu_0, \mu_1, \ldots, \mu_n \f$
  correspond to the space dimensions of \p mu_space
  \f$ n, 0, \ldots, n-1 \f$, respectively.
  When \p mu_space is empty, it means that the test is inconclusive.
  However, if \p pset <EM>precisely</EM> characterizes the effect
  of the loop body onto the loop-relevant program variables,
  then \p mu_space is empty <EM>if and only if</EM>
  the loop does <EM>not</EM> terminate.
*/
template <typename PSET>
void
all_affine_ranking_functions_MS(const PSET& pset, C_Polyhedron& mu_space);

/*! \ingroup PPL_CXX_interface \brief
  Termination test with ranking function space using an improvement
  of the method by Mesnard and Serebrenik \ref BMPZ10 "[BMPZ10]".

  \tparam PSET
  Any pointset supported by the PPL that provides the
  <CODE>minimized_constraints()</CODE> method.

  \param pset_before
  A pointset approximating the values of loop-relevant variables
  <EM>before</EM> the update performed in the loop body that is being
  analyzed.  The variables indices are allocated as follows:
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$.

  \param pset_after
  A pointset approximating the values of loop-relevant variables
  <EM>after</EM> the update performed in the loop body that is being
  analyzed.  The variables indices are allocated as follows:
  - \f$ x'_1, \ldots, x'_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$,
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ n, \ldots, 2n-1 \f$,

  Note that unprimed variables represent the values of the loop-relevant
  program variables before the update performed in the loop body,
  and primed variables represent the values of those program variables
  after the update.  Note also that unprimed variables are assigned
  to different space dimensions in \p pset_before and \p pset_after.

  \param mu_space
  This is assigned a closed polyhedron of space dimension \f$ n+1 \f$
  representing the space of all the affine ranking functions for the loops
  that are precisely characterized by \p pset.
  These ranking functions are of the form
  \f$ \mu_0 + \sum_{i=1}^n \mu_i x_i \f$
  where \f$ \mu_0, \mu_1, \ldots, \mu_n \f$ identify any point of the
  \p mu_space polyhedron.
  The variables \f$ \mu_0, \mu_1, \ldots, \mu_n \f$
  correspond to the space dimensions of \p mu_space
  \f$ n, 0, \ldots, n-1 \f$, respectively.
  When \p mu_space is empty, it means that the test is inconclusive.
  However, if \p pset_before and \p pset_after <EM>precisely</EM>
  characterize the effect of the loop body onto the loop-relevant
  program variables, then \p mu_space is empty <EM>if and only if</EM>
  the loop does <EM>not</EM> terminate.
*/
template <typename PSET>
void
all_affine_ranking_functions_MS_2(const PSET& pset_before,
                                  const PSET& pset_after,
                                  C_Polyhedron& mu_space);

/*! \ingroup PPL_CXX_interface \brief
  Computes the spaces of affine \e quasi ranking functions
  using an improvement of the method by Mesnard and Serebrenik
  \ref BMPZ10 "[BMPZ10]".

  \tparam PSET
  Any pointset supported by the PPL that provides the
  <CODE>minimized_constraints()</CODE> method.

  \param pset
  A pointset approximating the behavior of a loop whose termination
  is being analyzed.  The variables indices are allocated as follows:
  - \f$ x'_1, \ldots, x'_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$,
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ n, \ldots, 2n-1 \f$,
  .
  where unprimed variables represent the values of the loop-relevant
  program variables before the update performed in the loop body,
  and primed variables represent the values of those program variables
  after the update.

  \param decreasing_mu_space
  This is assigned a closed polyhedron of space dimension \f$ n+1 \f$
  representing the space of all the decreasing affine functions
  for the loops that are precisely characterized by \p pset.

  \param bounded_mu_space
  This is assigned a closed polyhedron of space dimension \f$ n+1 \f$
  representing the space of all the lower bounded affine functions
  for the loops that are precisely characterized by \p pset.

  These quasi-ranking functions are of the form
  \f$ \mu_0 + \sum_{i=1}^n \mu_i x_i \f$
  where \f$ \mu_0, \mu_1, \ldots, \mu_n \f$ identify any point of the
  \p decreasing_mu_space and \p bounded_mu_space polyhedrons.
  The variables \f$ \mu_0, \mu_1, \ldots, \mu_n \f$
  correspond to the space dimensions \f$ n, 0, \ldots, n-1 \f$, respectively.
  When \p decreasing_mu_space (resp., \p bounded_mu_space) is empty,
  it means that the test is inconclusive.
  However, if \p pset <EM>precisely</EM> characterizes the effect
  of the loop body onto the loop-relevant program variables,
  then \p decreasing_mu_space (resp., \p bounded_mu_space) will be empty
  <EM>if and only if</EM> there is no decreasing (resp., lower bounded)
  affine function, so that the loop does not terminate.
*/
template <typename PSET>
void
all_affine_quasi_ranking_functions_MS(const PSET& pset,
                                      C_Polyhedron& decreasing_mu_space,
                                      C_Polyhedron& bounded_mu_space);

/*! \ingroup PPL_CXX_interface \brief
  Computes the spaces of affine \e quasi ranking functions
  using an improvement of the method by Mesnard and Serebrenik
  \ref BMPZ10 "[BMPZ10]".

  \tparam PSET
  Any pointset supported by the PPL that provides the
  <CODE>minimized_constraints()</CODE> method.

  \param pset_before
  A pointset approximating the values of loop-relevant variables
  <EM>before</EM> the update performed in the loop body that is being
  analyzed.  The variables indices are allocated as follows:
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$.

  \param pset_after
  A pointset approximating the values of loop-relevant variables
  <EM>after</EM> the update performed in the loop body that is being
  analyzed.  The variables indices are allocated as follows:
  - \f$ x'_1, \ldots, x'_n \f$ go onto space dimensions
    \f$ 0, \ldots, n-1 \f$,
  - \f$ x_1, \ldots, x_n \f$ go onto space dimensions
    \f$ n, \ldots, 2n-1 \f$,

  Note that unprimed variables represent the values of the loop-relevant
  program variables before the update performed in the loop body,
  and primed variables represent the values of those program variables
  after the update.  Note also that unprimed variables are assigned
  to different space dimensions in \p pset_before and \p pset_after.

  \param decreasing_mu_space
  This is assigned a closed polyhedron of space dimension \f$ n+1 \f$
  representing the space of all the decreasing affine functions
  for the loops that are precisely characterized by \p pset.

  \param bounded_mu_space
  This is assigned a closed polyhedron of space dimension \f$ n+1 \f$
  representing the space of all the lower bounded affine functions
  for the loops that are precisely characterized by \p pset.

  These ranking functions are of the form
  \f$ \mu_0 + \sum_{i=1}^n \mu_i x_i \f$
  where \f$ \mu_0, \mu_1, \ldots, \mu_n \f$ identify any point of the
  \p decreasing_mu_space and \p bounded_mu_space polyhedrons.
  The variables \f$ \mu_0, \mu_1, \ldots, \mu_n \f$
  correspond to the space dimensions \f$ n, 0, \ldots, n-1 \f$, respectively.
  When \p decreasing_mu_space (resp., \p bounded_mu_space) is empty,
  it means that the test is inconclusive.
  However, if \p pset_before and \p pset_after <EM>precisely</EM>
  characterize the effect of the loop body onto the loop-relevant
  program variables, then \p decreasing_mu_space (resp., \p bounded_mu_space)
  will be empty <EM>if and only if</EM> there is no decreasing
  (resp., lower bounded) affine function, so that the loop does not terminate.
*/
template <typename PSET>
void
all_affine_quasi_ranking_functions_MS_2(const PSET& pset_before,
                                        const PSET& pset_after,
                                        C_Polyhedron& decreasing_mu_space,
                                        C_Polyhedron& bounded_mu_space);

/*! \ingroup PPL_CXX_interface \brief
  Like termination_test_MS() but using the method by Podelski and
  Rybalchenko \ref BMPZ10 "[BMPZ10]".
*/
template <typename PSET>
bool
termination_test_PR(const PSET& pset);

/*! \ingroup PPL_CXX_interface \brief
  Like termination_test_MS_2() but using an alternative formalization
  of the method by Podelski and Rybalchenko \ref BMPZ10 "[BMPZ10]".
*/
template <typename PSET>
bool
termination_test_PR_2(const PSET& pset_before, const PSET& pset_after);

/*! \ingroup PPL_CXX_interface \brief
  Like one_affine_ranking_function_MS() but using the method by Podelski
  and Rybalchenko \ref BMPZ10 "[BMPZ10]".
*/
template <typename PSET>
bool
one_affine_ranking_function_PR(const PSET& pset, Generator& mu);

/*! \ingroup PPL_CXX_interface \brief
  Like one_affine_ranking_function_MS_2() but using an alternative
  formalization of the method by Podelski and Rybalchenko
  \ref BMPZ10 "[BMPZ10]".
*/
template <typename PSET>
bool
one_affine_ranking_function_PR_2(const PSET& pset_before,
                                 const PSET& pset_after,
                                 Generator& mu);

/*! \ingroup PPL_CXX_interface \brief
  Like all_affine_ranking_functions_MS() but using the method by Podelski
  and Rybalchenko \ref BMPZ10 "[BMPZ10]".
*/
template <typename PSET>
void
all_affine_ranking_functions_PR(const PSET& pset, NNC_Polyhedron& mu_space);

/*! \ingroup PPL_CXX_interface \brief
  Like all_affine_ranking_functions_MS_2() but using an alternative
  formalization of the method by Podelski and Rybalchenko
  \ref BMPZ10 "[BMPZ10]".
*/
template <typename PSET>
void
all_affine_ranking_functions_PR_2(const PSET& pset_before,
                                  const PSET& pset_after,
                                  NNC_Polyhedron& mu_space);

//@} // Functions for the Synthesis of Linear Rankings

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/termination_templates.hh line 1. */
/* Utilities for termination analysis: template functions.
*/


/* Automatically generated from PPL source file ../src/termination_templates.hh line 33. */

#include <stdexcept>

#define PRINT_DEBUG_INFO 0

#if PRINT_DEBUG_INFO
#include <iostream>
#endif

namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Termination {

#if PRINT_DEBUG_INFO
static dimension_type output_function_MS_n;
static dimension_type output_function_MS_m;

/* Encodes which object are we printing:

   0 means input constraint system;
   1 means first output constraint system;
   2 means second output constraint system;
   3 means only output constraint system
     (i.e., when first and second are the same);
   4 means mu space.
*/
static int output_function_MS_which = -1;

/*
  Debugging output function.  See the documentation of
  fill_constraint_systems_MS() for the allocation of variable indices.
*/
inline void
output_function_MS(std::ostream& s, const Variable v) {
  dimension_type id = v.id();
  switch (output_function_MS_which) {
  case 0:
    if (id < output_function_MS_n) {
      s << "x'" << id + 1;
    }
    else if (id < 2*output_function_MS_n) {
      s << "x" << id - output_function_MS_n + 1;
    }
    else {
      s << "WHAT?";
    }
    break;
  case 1:
    if (id < output_function_MS_n) {
      s << "mu" << id + 1;
    }
    else if (id == output_function_MS_n) {
      s << "WHAT?";
    }
    else if (id <= output_function_MS_n + output_function_MS_m) {
      s << "y" << id - output_function_MS_n;
    }
    else {
      s << "WHAT?";
    }
    break;
  case 2:
  case 4:
    if (id < output_function_MS_n) {
      s << "mu" << id + 1;
    }
    else if (id == output_function_MS_n) {
      s << "mu0";
    }
    else if (output_function_MS_which == 2
             && id <= output_function_MS_n + output_function_MS_m + 2) {
      s << "z" << id - output_function_MS_n;
    }
    else {
      s << "WHAT?";
    }
    break;
  case 3:
    if (id < output_function_MS_n) {
      s << "mu" << id + 1;
    }
    else if (id == output_function_MS_n) {
      s << "mu0";
    }
    else if (id <= output_function_MS_n + output_function_MS_m) {
      s << "y" << id - output_function_MS_n;
    }
    else if (id <= output_function_MS_n + 2*output_function_MS_m + 2) {
      s << "z" << id - (output_function_MS_n + output_function_MS_m);
    }
    else {
      s << "WHAT?";
    }
    break;
  default:
    abort();
    break;
  }
}

static dimension_type output_function_PR_s;
static dimension_type output_function_PR_r;

/*
  Debugging output function.  See the documentation of
  fill_constraint_system_PR() for the allocation of variable indices.
*/
inline void
output_function_PR(std::ostream& s, const Variable v) {
  dimension_type id = v.id();
  if (id < output_function_PR_s) {
    s << "u3_" << id + 1;
  }
  else if (id < output_function_PR_s + output_function_PR_r) {
    s << "u2_" << id - output_function_PR_s + 1;
  }
  else if (id < output_function_PR_s + 2*output_function_PR_r) {
    s << "u1_" << id - (output_function_PR_s + output_function_PR_r) + 1;
  }
  else {
    s << "WHAT?";
  }
}
#endif

void
assign_all_inequalities_approximation(const Constraint_System& cs_in,
                                      Constraint_System& cs_out);

template <typename PSET>
inline void
assign_all_inequalities_approximation(const PSET& pset,
                                      Constraint_System& cs) {
  assign_all_inequalities_approximation(pset.minimized_constraints(), cs);
}

template <>
void
assign_all_inequalities_approximation(const C_Polyhedron& ph,
                                      Constraint_System& cs);

bool
termination_test_MS(const Constraint_System& cs);

bool
one_affine_ranking_function_MS(const Constraint_System& cs,
                               Generator& mu);

void
all_affine_ranking_functions_MS(const Constraint_System& cs,
                                C_Polyhedron& mu_space);

void
all_affine_quasi_ranking_functions_MS(const Constraint_System& cs,
                                      C_Polyhedron& decreasing_mu_space,
                                      C_Polyhedron& bounded_mu_space);

bool
termination_test_PR(const Constraint_System& cs_before,
                    const Constraint_System& cs_after);

bool
one_affine_ranking_function_PR(const Constraint_System& cs_before,
                               const Constraint_System& cs_after,
                               Generator& mu);

void
all_affine_ranking_functions_PR(const Constraint_System& cs_before,
                                const Constraint_System& cs_after,
                                NNC_Polyhedron& mu_space);

bool
termination_test_PR_original(const Constraint_System& cs);

bool
one_affine_ranking_function_PR_original(const Constraint_System& cs,
                                        Generator& mu);

void
all_affine_ranking_functions_PR_original(const Constraint_System& cs,
                                         NNC_Polyhedron& mu_space);

} // namespace Termination

} // namespace Implementation

template <typename PSET>
void
Termination_Helpers
::assign_all_inequalities_approximation(const PSET& pset_before,
                                        const PSET& pset_after,
                                        Constraint_System& cs) {
  Implementation::Termination
    ::assign_all_inequalities_approximation(pset_before, cs);
  cs.shift_space_dimensions(Variable(0), cs.space_dimension());
  Constraint_System cs_after;
  Implementation::Termination
    ::assign_all_inequalities_approximation(pset_after, cs_after);
  // FIXME: provide an "append" for constraint systems.
  for (Constraint_System::const_iterator i = cs_after.begin(),
         cs_after_end = cs_after.end(); i != cs_after_end; ++i) {
    cs.insert(*i);
  }
}

template <typename PSET>
bool
termination_test_MS(const PSET& pset) {
  const dimension_type space_dim = pset.space_dimension();
  if (space_dim % 2 != 0) {
    std::ostringstream s;
    s << "PPL::termination_test_MS(pset):\n"
         "pset.space_dimension() == " << space_dim
      << " is odd.";
    throw std::invalid_argument(s.str());
  }

  using namespace Implementation::Termination;
  Constraint_System cs;
  assign_all_inequalities_approximation(pset, cs);
  return termination_test_MS(cs);
}

template <typename PSET>
bool
termination_test_MS_2(const PSET& pset_before, const PSET& pset_after) {
  const dimension_type before_space_dim = pset_before.space_dimension();
  const dimension_type after_space_dim = pset_after.space_dimension();
  if (after_space_dim != 2*before_space_dim) {
    std::ostringstream s;
    s << "PPL::termination_test_MS_2(pset_before, pset_after):\n"
         "pset_before.space_dimension() == " << before_space_dim
      << ", pset_after.space_dimension() == " << after_space_dim
      << ";\nthe latter should be twice the former.";
    throw std::invalid_argument(s.str());
  }

  using namespace Implementation::Termination;
  Constraint_System cs;
  Termination_Helpers
    ::assign_all_inequalities_approximation(pset_before, pset_after, cs);
  return termination_test_MS(cs);
}

template <typename PSET>
bool
one_affine_ranking_function_MS(const PSET& pset, Generator& mu) {
  const dimension_type space_dim = pset.space_dimension();
  if (space_dim % 2 != 0) {
    std::ostringstream s;
    s << "PPL::one_affine_ranking_function_MS(pset, mu):\n"
         "pset.space_dimension() == " << space_dim
      << " is odd.";
    throw std::invalid_argument(s.str());
  }

  using namespace Implementation::Termination;
  Constraint_System cs;
  assign_all_inequalities_approximation(pset, cs);
  return one_affine_ranking_function_MS(cs, mu);
}

template <typename PSET>
bool
one_affine_ranking_function_MS_2(const PSET& pset_before,
                                 const PSET& pset_after,
                                 Generator& mu) {
  const dimension_type before_space_dim = pset_before.space_dimension();
  const dimension_type after_space_dim = pset_after.space_dimension();
  if (after_space_dim != 2*before_space_dim) {
    std::ostringstream s;
    s << "PPL::one_affine_ranking_function_MS_2(pset_before, pset_after, mu):\n"
         "pset_before.space_dimension() == " << before_space_dim
      << ", pset_after.space_dimension() == " << after_space_dim
      << ";\nthe latter should be twice the former.";
    throw std::invalid_argument(s.str());
  }

  using namespace Implementation::Termination;
  Constraint_System cs;
  Termination_Helpers
    ::assign_all_inequalities_approximation(pset_before, pset_after, cs);
  return one_affine_ranking_function_MS(cs, mu);
}

template <typename PSET>
void
all_affine_ranking_functions_MS(const PSET& pset, C_Polyhedron& mu_space) {
  const dimension_type space_dim = pset.space_dimension();
  if (space_dim % 2 != 0) {
    std::ostringstream s;
    s << "PPL::all_affine_ranking_functions_MS(pset, mu_space):\n"
         "pset.space_dimension() == " << space_dim
      << " is odd.";
    throw std::invalid_argument(s.str());
  }

  if (pset.is_empty()) {
    mu_space = C_Polyhedron(1 + space_dim/2, UNIVERSE);
    return;
  }

  using namespace Implementation::Termination;
  Constraint_System cs;
  assign_all_inequalities_approximation(pset, cs);
  all_affine_ranking_functions_MS(cs, mu_space);
}

template <typename PSET>
void
all_affine_ranking_functions_MS_2(const PSET& pset_before,
                                  const PSET& pset_after,
                                  C_Polyhedron& mu_space) {
  const dimension_type before_space_dim = pset_before.space_dimension();
  const dimension_type after_space_dim = pset_after.space_dimension();
  if (after_space_dim != 2*before_space_dim) {
    std::ostringstream s;
    s << "PPL::all_affine_ranking_functions_MS_2"
      << "(pset_before, pset_after, mu_space):\n"
      << "pset_before.space_dimension() == " << before_space_dim
      << ", pset_after.space_dimension() == " << after_space_dim
      << ";\nthe latter should be twice the former.";
    throw std::invalid_argument(s.str());
  }

  if (pset_before.is_empty()) {
    mu_space = C_Polyhedron(1 + before_space_dim, UNIVERSE);
    return;
  }

  using namespace Implementation::Termination;
  Constraint_System cs;
  Termination_Helpers
    ::assign_all_inequalities_approximation(pset_before, pset_after, cs);
  all_affine_ranking_functions_MS(cs, mu_space);
}

template <typename PSET>
void
all_affine_quasi_ranking_functions_MS(const PSET& pset,
                                      C_Polyhedron& decreasing_mu_space,
                                      C_Polyhedron& bounded_mu_space) {
  const dimension_type space_dim = pset.space_dimension();
  if (space_dim % 2 != 0) {
    std::ostringstream s;
    s << "PPL::all_affine_quasi_ranking_functions_MS"
      << "(pset, decr_space, bounded_space):\n"
      << "pset.space_dimension() == " << space_dim
      << " is odd.";
    throw std::invalid_argument(s.str());
  }

  if (pset.is_empty()) {
    decreasing_mu_space = C_Polyhedron(1 + space_dim/2, UNIVERSE);
    bounded_mu_space = decreasing_mu_space;
    return;
  }

  using namespace Implementation::Termination;
  Constraint_System cs;
  assign_all_inequalities_approximation(pset, cs);
  all_affine_quasi_ranking_functions_MS(cs,
                                        decreasing_mu_space,
                                        bounded_mu_space);
}

template <typename PSET>
void
all_affine_quasi_ranking_functions_MS_2(const PSET& pset_before,
                                        const PSET& pset_after,
                                        C_Polyhedron& decreasing_mu_space,
                                        C_Polyhedron& bounded_mu_space) {
  const dimension_type before_space_dim = pset_before.space_dimension();
  const dimension_type after_space_dim = pset_after.space_dimension();
  if (after_space_dim != 2*before_space_dim) {
    std::ostringstream s;
    s << "PPL::all_affine_quasi_ranking_functions_MS_2"
      << "(pset_before, pset_after, decr_space, bounded_space):\n"
      << "pset_before.space_dimension() == " << before_space_dim
      << ", pset_after.space_dimension() == " << after_space_dim
      << ";\nthe latter should be twice the former.";
    throw std::invalid_argument(s.str());
  }

  if (pset_before.is_empty()) {
    decreasing_mu_space = C_Polyhedron(1 + before_space_dim, UNIVERSE);
    bounded_mu_space = decreasing_mu_space;
    return;
  }

  using namespace Implementation::Termination;
  Constraint_System cs;
  Termination_Helpers
    ::assign_all_inequalities_approximation(pset_before, pset_after, cs);
  all_affine_quasi_ranking_functions_MS(cs,
                                        decreasing_mu_space,
                                        bounded_mu_space);
}

template <typename PSET>
bool
termination_test_PR_2(const PSET& pset_before, const PSET& pset_after) {
  const dimension_type before_space_dim = pset_before.space_dimension();
  const dimension_type after_space_dim = pset_after.space_dimension();
  if (after_space_dim != 2*before_space_dim) {
    std::ostringstream s;
    s << "PPL::termination_test_PR_2(pset_before, pset_after):\n"
      << "pset_before.space_dimension() == " << before_space_dim
      << ", pset_after.space_dimension() == " << after_space_dim
      << ";\nthe latter should be twice the former.";
    throw std::invalid_argument(s.str());
  }

  using namespace Implementation::Termination;
  Constraint_System cs_before;
  Constraint_System cs_after;
  assign_all_inequalities_approximation(pset_before, cs_before);
  assign_all_inequalities_approximation(pset_after, cs_after);
  return termination_test_PR(cs_before, cs_after);
}

template <typename PSET>
bool
termination_test_PR(const PSET& pset) {
  const dimension_type space_dim = pset.space_dimension();
  if (space_dim % 2 != 0) {
    std::ostringstream s;
    s << "PPL::termination_test_PR(pset):\n"
      << "pset.space_dimension() == " << space_dim
      << " is odd.";
    throw std::invalid_argument(s.str());
  }

  using namespace Implementation::Termination;
  Constraint_System cs;
  assign_all_inequalities_approximation(pset, cs);
  return termination_test_PR_original(cs);
}

template <typename PSET>
bool
one_affine_ranking_function_PR_2(const PSET& pset_before,
                                 const PSET& pset_after,
                                 Generator& mu) {
  const dimension_type before_space_dim = pset_before.space_dimension();
  const dimension_type after_space_dim = pset_after.space_dimension();
  if (after_space_dim != 2*before_space_dim) {
    std::ostringstream s;
    s << "PPL::one_affine_ranking_function_PR_2"
      << "(pset_before, pset_after, mu):\n"
      << "pset_before.space_dimension() == " << before_space_dim
      << ", pset_after.space_dimension() == " << after_space_dim
      << ";\nthe latter should be twice the former.";
    throw std::invalid_argument(s.str());
  }

  using namespace Implementation::Termination;
  Constraint_System cs_before;
  Constraint_System cs_after;
  assign_all_inequalities_approximation(pset_before, cs_before);
  assign_all_inequalities_approximation(pset_after, cs_after);
  return one_affine_ranking_function_PR(cs_before, cs_after, mu);
}

template <typename PSET>
bool
one_affine_ranking_function_PR(const PSET& pset, Generator& mu) {
  const dimension_type space_dim = pset.space_dimension();
  if (space_dim % 2 != 0) {
    std::ostringstream s;
    s << "PPL::one_affine_ranking_function_PR(pset, mu):\n"
      << "pset.space_dimension() == " << space_dim
      << " is odd.";
    throw std::invalid_argument(s.str());
  }

  using namespace Implementation::Termination;
  Constraint_System cs;
  assign_all_inequalities_approximation(pset, cs);
  return one_affine_ranking_function_PR_original(cs, mu);
}

template <typename PSET>
void
all_affine_ranking_functions_PR_2(const PSET& pset_before,
                                  const PSET& pset_after,
                                  NNC_Polyhedron& mu_space) {
  const dimension_type before_space_dim = pset_before.space_dimension();
  const dimension_type after_space_dim = pset_after.space_dimension();
  if (after_space_dim != 2*before_space_dim) {
    std::ostringstream s;
    s << "PPL::all_affine_ranking_functions_MS_2"
      << "(pset_before, pset_after, mu_space):\n"
      << "pset_before.space_dimension() == " << before_space_dim
      << ", pset_after.space_dimension() == " << after_space_dim
      << ";\nthe latter should be twice the former.";
    throw std::invalid_argument(s.str());
  }

  if (pset_before.is_empty()) {
    mu_space = NNC_Polyhedron(1 + before_space_dim);
    return;
  }

  using namespace Implementation::Termination;
  Constraint_System cs_before;
  Constraint_System cs_after;
  assign_all_inequalities_approximation(pset_before, cs_before);
  assign_all_inequalities_approximation(pset_after, cs_after);
  all_affine_ranking_functions_PR(cs_before, cs_after, mu_space);
}

template <typename PSET>
void
all_affine_ranking_functions_PR(const PSET& pset,
                                NNC_Polyhedron& mu_space) {
  const dimension_type space_dim = pset.space_dimension();
  if (space_dim % 2 != 0) {
    std::ostringstream s;
    s << "PPL::all_affine_ranking_functions_PR(pset, mu_space):\n"
      << "pset.space_dimension() == " << space_dim
      << " is odd.";
    throw std::invalid_argument(s.str());
  }

  if (pset.is_empty()) {
    mu_space = NNC_Polyhedron(1 + space_dim/2);
    return;
  }

  using namespace Implementation::Termination;
  Constraint_System cs;
  assign_all_inequalities_approximation(pset, cs);
  all_affine_ranking_functions_PR_original(cs, mu_space);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/termination_defs.hh line 501. */

/* Automatically generated from PPL source file ../src/wrap_string.hh line 1. */
/* Declaration of string wrapping function.
*/


/* Automatically generated from PPL source file ../src/wrap_string.hh line 28. */

namespace Parma_Polyhedra_Library {

namespace IO_Operators {

//! Utility function for the wrapping of lines of text.
/*!
  \param src_string
  The source string holding the lines to wrap.

  \param indent_depth
  The indentation depth.

  \param preferred_first_line_length
  The preferred length for the first line of text.

  \param preferred_line_length
  The preferred length for all the lines but the first one.

  \return
  The wrapped string.
*/
std::string
wrap_string(const std::string& src_string,
            unsigned indent_depth,
            unsigned preferred_first_line_length,
            unsigned preferred_line_length);

} // namespace IO_Operators

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Cast_Floating_Point_Expression_defs.hh line 1. */
/* Declarations for the Cast_Floating_Point_Expression class and
   its constituents.
*/


/* Automatically generated from PPL source file ../src/Cast_Floating_Point_Expression_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
class Cast_Floating_Point_Expression;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Cast_Floating_Point_Expression_defs.hh line 31. */
#include <map>

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Cast_Floating_Point_Expression */
template<typename FP_Interval_Type, typename FP_Format>
void
swap(Cast_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
     Cast_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y);

/*! \brief
  A generic Cast Floating Point Expression.

  \ingroup PPL_CXX_interface

  \par Template type parameters

  - The class template type parameter \p FP_Interval_Type represents the type
  of the intervals used in the abstract domain.
  - The class template type parameter \p FP_Format represents the floating
  point format used in the concrete domain.

  \par Linearization of floating-point cast expressions

  Let \f$i + \sum_{v \in \cV}i_{v}v \f$ and
  \f$i' + \sum_{v \in \cV}i'_{v}v \f$
  be two linear forms and \f$\aslf\f$ a sound abstract operator on linear
  forms such that:

  \f[
  \left(i + \sum_{v \in \cV}i_{v}v \right)
  \aslf
  \left(i' + \sum_{v \in \cV}i'_{v}v \right)
  =
  \left(i \asifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \asifp i'_{v} \right)v.
  \f]

  Given a floating point expression \f$e\f$ and a composite abstract store
  \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right \rrbracket\f$,
  we construct the interval linear form
  \f$\linexprenv{cast(e)}{\rho^{\#}}{\rho^{\#}_l}\f$ as follows:
  \f[
  \linexprenv{cast(e)}{\rho^{\#}}{\rho^{\#}_l}
  =
  \linexprenv{e}{\rho^{\#}}{\rho^{\#}_l}
  \aslf
  \varepsilon_{\mathbf{f}}\left(\linexprenv{e}{\rho^{\#}}{\rho^{\#}_l}
  \right)
  \aslf
  mf_{\mathbf{f}}[-1, 1]
  \f]
  where \f$\varepsilon_{\mathbf{f}}(l)\f$ is the linear form computed by
  calling method <CODE>Floating_Point_Expression::relative_error</CODE>
  on \f$l\f$ and \f$mf_{\mathbf{f}}\f$ is a rounding error defined in
  <CODE>Floating_Point_Expression::absolute_error</CODE>.
*/
template <typename FP_Interval_Type, typename FP_Format>
class Cast_Floating_Point_Expression
  : public Floating_Point_Expression<FP_Interval_Type, FP_Format> {

public:

  /*! \brief
     Alias for the Linear_Form<FP_Interval_Type> from
     Floating_Point_Expression
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Linear_Form FP_Linear_Form;

  /*! \brief
     Alias for the Box<FP_Interval_Type> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Interval_Abstract_Store FP_Interval_Abstract_Store;

  /*! \brief
     Alias for the std::map<dimension_type, FP_Linear_Form> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Linear_Form_Abstract_Store FP_Linear_Form_Abstract_Store;

  //! \name Constructors and Destructor
  //@{
  /*! \brief
    Builds a cast floating point expression with the value
    expressed by \p expr.
  */
  Cast_Floating_Point_Expression(
    Floating_Point_Expression<FP_Interval_Type, FP_Format>* const expr);

  //! Destructor.
  ~Cast_Floating_Point_Expression();

  //@} // Constructors and Destructor

  /*! \brief
    Linearizes the expression in a given astract store.

    Makes \p result become the linearization of \p *this in the given
    composite abstract store.

    \param int_store The interval abstract store.
    \param lf_store The linear form abstract store.
    \param result The modified linear form.

    \return <CODE>true</CODE> if the linearization succeeded,
    <CODE>false</CODE> otherwise.

    See the class description for an explanation of how \p result is computed.
  */
  bool linearize(const FP_Interval_Abstract_Store& int_store,
                 const FP_Linear_Form_Abstract_Store& lf_store,
                 FP_Linear_Form& result) const;

  //! Swaps \p *this with \p y.
  void m_swap(Cast_Floating_Point_Expression& y);

private:

  //! Pointer to the casted expression.
  Floating_Point_Expression<FP_Interval_Type, FP_Format>* expr;

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited copy constructor.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Cast_Floating_Point_Expression(
                          const Cast_Floating_Point_Expression& y);

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited assignment operator.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAIL
  Cast_Floating_Point_Expression& operator=(
                          const Cast_Floating_Point_Expression& y);

}; // class Cast_Floating_Point_Expression

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Cast_Floating_Point_Expression_inlines.hh line 1. */
/* Cast_Floating_Point_Expression class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Cast_Floating_Point_Expression_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
inline
Cast_Floating_Point_Expression<FP_Interval_Type, FP_Format>::
Cast_Floating_Point_Expression(
Floating_Point_Expression<FP_Interval_Type, FP_Format>* const e)
  : expr(e) {
  assert(e != 0);
}

template <typename FP_Interval_Type, typename FP_Format>
inline
Cast_Floating_Point_Expression<FP_Interval_Type, FP_Format>::
~Cast_Floating_Point_Expression() {
  delete expr;
}

template <typename FP_Interval_Type, typename FP_Format>
inline void
Cast_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::m_swap(Cast_Floating_Point_Expression& y) {
  swap(expr, y.expr);
}

/*! \relates Cast_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
inline void
swap(Cast_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
     Cast_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Cast_Floating_Point_Expression_defs.hh line 181. */

/* Automatically generated from PPL source file ../src/Cast_Floating_Point_Expression_templates.hh line 1. */
/* Cast_Floating_Point_Expression class implementation:
   non-inline template functions.
*/


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
bool Cast_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::linearize(const FP_Interval_Abstract_Store& int_store,
            const FP_Linear_Form_Abstract_Store& lf_store,
            FP_Linear_Form& result) const {
  if (!expr->linearize(int_store, lf_store, result)) {
    return false;
  }
  FP_Linear_Form rel_error;
  relative_error(result, rel_error);
  result += rel_error;
  result += this->absolute_error;
  return !this->overflows(result);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Constant_Floating_Point_Expression_defs.hh line 1. */
/* Declarations for the Constant_Floating_Point_Expression class and
   its constituents.
*/


/* Automatically generated from PPL source file ../src/Constant_Floating_Point_Expression_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
class Constant_Floating_Point_Expression;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Constant_Floating_Point_Expression_defs.hh line 31. */
#include <map>

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Constant_Floating_Point_Expression */
template<typename FP_Interval_Type, typename FP_Format>
void swap(Constant_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
          Constant_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y);

/*! \brief
  A generic Constant Floating Point Expression.

  \ingroup PPL_CXX_interface

  \par Template type parameters

  - The class template type parameter \p FP_Interval_Type represents the type
  of the intervals used in the abstract domain.
  - The class template type parameter \p FP_Format represents the floating
  point format used in the concrete domain.

  \par Linearization of floating-point constant expressions

  The linearization of a constant floating point expression results in a
  linear form consisting of only the inhomogeneous term
  \f$[l, u]\f$, where \f$l\f$ and \f$u\f$ are the lower
  and upper bounds of the constant value given to the class constructor.
*/
template <typename FP_Interval_Type, typename FP_Format>
class Constant_Floating_Point_Expression
  : public Floating_Point_Expression<FP_Interval_Type, FP_Format> {

public:

  /*! \brief
     Alias for the Linear_Form<FP_Interval_Type> from
     Floating_Point_Expression
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Linear_Form FP_Linear_Form;

  /*! \brief
     Alias for the Box<FP_Interval_Type> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Interval_Abstract_Store FP_Interval_Abstract_Store;

  /*! \brief
     Alias for the std::map<dimension_type, FP_Linear_Form> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Linear_Form_Abstract_Store FP_Linear_Form_Abstract_Store;

  /*! \brief
     Alias for the FP_Interval_Type::boundary_type from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::boundary_type
  boundary_type;

  /*! \brief
     Alias for the FP_Interval_Type::info_type from Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::info_type info_type;

  //! \name Constructors and Destructor
  //@{
  /*! \brief
    Constructor with two parameters: builds the constant floating point
    expression from a \p lower_bound and an \p upper_bound of its
    value in the concrete domain.
  */
  Constant_Floating_Point_Expression(const boundary_type lower_bound,
                                     const boundary_type upper_bound);

  /*! \brief
    Builds a constant floating point expression with the value
    expressed by the string \p str_value.
  */
  Constant_Floating_Point_Expression(const char* str_value);

  //! Destructor.
  ~Constant_Floating_Point_Expression();

  //@} // Constructors and Destructor

  /*! \brief
    Linearizes the expression in a given astract store.

    Makes \p result become the linearization of \p *this in the given
    composite abstract store.

    \param int_store The interval abstract store.
    \param lf_store The linear form abstract store.
    \param result The modified linear form.

    \return <CODE>true</CODE> if the linearization succeeded,
    <CODE>false</CODE> otherwise.

    See the class description for an explanation of how \p result is computed.
  */
  bool linearize(const FP_Interval_Abstract_Store& int_store,
                 const FP_Linear_Form_Abstract_Store& lf_store,
                 FP_Linear_Form& result) const;

  //! Swaps \p *this with \p y.
  void m_swap(Constant_Floating_Point_Expression& y);

private:

  FP_Interval_Type value;

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited copy constructor.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Constant_Floating_Point_Expression(
                          const Constant_Floating_Point_Expression& y);

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited assignment operator.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAIL
  Constant_Floating_Point_Expression& operator=(
                          const Constant_Floating_Point_Expression& y);

}; // class Constant_Floating_Point_Expression

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Constant_Floating_Point_Expression_inlines.hh line 1. */
/* Constant_Floating_Point_Expression class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Constant_Floating_Point_Expression_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
inline
Constant_Floating_Point_Expression<FP_Interval_Type, FP_Format>::
Constant_Floating_Point_Expression(const char* str_value)
  : value(str_value) {}

template <typename FP_Interval_Type, typename FP_Format>
inline
Constant_Floating_Point_Expression<FP_Interval_Type, FP_Format>::
Constant_Floating_Point_Expression(const boundary_type lb,
                                   const boundary_type ub) {
  assert(lb <= ub);
  value.build(i_constraint(GREATER_OR_EQUAL, lb),
              i_constraint(LESS_OR_EQUAL, ub));
}

template <typename FP_Interval_Type, typename FP_Format>
inline
Constant_Floating_Point_Expression<FP_Interval_Type, FP_Format>::
~Constant_Floating_Point_Expression() {}

template <typename FP_Interval_Type, typename FP_Format>
inline void
Constant_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::m_swap(Constant_Floating_Point_Expression& y) {
  using std::swap;
  swap(value, y.value);
}

template <typename FP_Interval_Type, typename FP_Format>
inline bool
Constant_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::linearize(const FP_Interval_Abstract_Store&,
            const FP_Linear_Form_Abstract_Store&,
            FP_Linear_Form& result) const {
  result = FP_Linear_Form(value);
  return true;
}

/*! \relates Constant_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
inline void
swap(Constant_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
     Constant_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Constant_Floating_Point_Expression_defs.hh line 172. */

/* Automatically generated from PPL source file ../src/Variable_Floating_Point_Expression_defs.hh line 1. */
/* Declarations for the Variable_Floating_Point_Expression class and
   its constituents.
*/


/* Automatically generated from PPL source file ../src/Variable_Floating_Point_Expression_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
class Variable_Floating_Point_Expression;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Variable_Floating_Point_Expression_defs.hh line 31. */
#include <map>
#include <utility>

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Variable_Floating_Point_Expression */
template<typename FP_Interval_Type, typename FP_Format>
void swap(Variable_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
          Variable_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y);

/*! \brief
  A generic Variable Floating Point Expression.

  \ingroup PPL_CXX_interface

  \par Template type parameters

  - The class template type parameter \p FP_Interval_Type represents the type
  of the intervals used in the abstract domain.
  - The class template type parameter \p FP_Format represents the floating
  point format used in the concrete domain.

  \par Linearization of floating-point variable expressions

  Given a variable expression \f$v\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval
  linear form \f$\linexprenv{v}{\rho^{\#}}{\rho^{\#}_l}\f$ as
  \f$\rho^{\#}_l(v)\f$ if it is defined; otherwise we construct it as
  \f$[-1, 1]v\f$.
*/
template <typename FP_Interval_Type, typename FP_Format>
class Variable_Floating_Point_Expression
: public Floating_Point_Expression<FP_Interval_Type, FP_Format> {

public:

  /*! \brief
     Alias for the Linear_Form<FP_Interval_Type> from
     Floating_Point_Expression
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Linear_Form FP_Linear_Form;

  /*! \brief
     Alias for the Box<FP_Interval_Type> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Interval_Abstract_Store FP_Interval_Abstract_Store;

  /*! \brief
     Alias for the std::map<dimension_type, FP_Linear_Form> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Linear_Form_Abstract_Store FP_Linear_Form_Abstract_Store;

  /*! \brief
     Alias for the FP_Interval_Type::boundary_type from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::boundary_type
  boundary_type;

  /*! \brief
     Alias for the FP_Interval_Type::info_type from Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::info_type info_type;

  //! \name Constructors and Destructor
  //@{
  /*! \brief
    Constructor with a parameter: builds the variable floating point
    expression corresponding to the variable having \p v_index as its index.
  */
  explicit Variable_Floating_Point_Expression(const dimension_type v_index);

  //! Destructor.
  ~Variable_Floating_Point_Expression();

  //@} // Constructors and Destructor

  /*! \brief
    Linearizes the expression in a given abstract store.

    Makes \p result become the linearization of \p *this in the given
    composite abstract store.

    \param int_store The interval abstract store.
    \param lf_store The linear form abstract store.
    \param result The modified linear form.

    \return <CODE>true</CODE> if the linearization succeeded,
    <CODE>false</CODE> otherwise.

    Note that the variable in the expression MUST have an associated value
    in \p int_store. If this precondition is not met, calling the method
    causes an undefined behavior.

    See the class description for a detailed explanation of how \p result is
    computed.
  */
  bool linearize(const FP_Interval_Abstract_Store& int_store,
                 const FP_Linear_Form_Abstract_Store& lf_store,
                 FP_Linear_Form& result) const;

  /*! \brief
    Assigns a linear form to the variable with the same index of
    \p *this in a given linear form abstract store.

    \param lf The linear form assigned to the variable.
    \param lf_store The linear form abstract store.

    Note that once \p lf is assigned to a variable, all the other entries
    of \p lf_store which contain that variable are discarded.
  */
  void linear_form_assign(const FP_Linear_Form& lf,
                                FP_Linear_Form_Abstract_Store& lf_store) const;

  //! Swaps \p *this with \p y.
  void m_swap(Variable_Floating_Point_Expression& y);

private:

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited copy constructor.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Variable_Floating_Point_Expression(
                          const Variable_Floating_Point_Expression& y);

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited assignment operator.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Variable_Floating_Point_Expression& operator=(
                          const Variable_Floating_Point_Expression& y);

  //! The index of the variable.
  dimension_type variable_index;

}; // class Variable_Floating_Point_Expression

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Variable_Floating_Point_Expression_inlines.hh line 1. */
/* Variable_Floating_Point_Expression class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Variable_Floating_Point_Expression_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
inline
Variable_Floating_Point_Expression<FP_Interval_Type, FP_Format>::
Variable_Floating_Point_Expression(const dimension_type v_index)
  : variable_index(v_index) {}

template <typename FP_Interval_Type, typename FP_Format>
inline
Variable_Floating_Point_Expression<FP_Interval_Type, FP_Format>::
~Variable_Floating_Point_Expression() {}

template <typename FP_Interval_Type, typename FP_Format>
inline void
Variable_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::m_swap(Variable_Floating_Point_Expression& y) {
  using std::swap;
  swap(variable_index, y.variable_index);
}

template <typename FP_Interval_Type, typename FP_Format>
inline bool
Variable_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::linearize(const FP_Interval_Abstract_Store&,
            const FP_Linear_Form_Abstract_Store& lf_store,
            FP_Linear_Form& result) const {
  typename FP_Linear_Form_Abstract_Store::const_iterator
           variable_value = lf_store.find(variable_index);

  if (variable_value == lf_store.end()) {
    result = FP_Linear_Form(Variable(variable_index));
    return true;
  }

  result = FP_Linear_Form(variable_value->second);
  return !this->overflows(result);
}

template <typename FP_Interval_Type, typename FP_Format>
inline void
Variable_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::linear_form_assign(const FP_Linear_Form& lf,
                           FP_Linear_Form_Abstract_Store& lf_store) const {
  for (typename FP_Linear_Form_Abstract_Store::iterator
         i = lf_store.begin(); i != lf_store.end(); ) {
    if ((i->second).coefficient(Variable(variable_index)) != 0) {
      i = lf_store.erase(i);
    }
    else {
      ++i;
    }
  }
  lf_store[variable_index] = lf;
  return;
}

/*! \relates Variable_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
inline void
swap(Variable_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
     Variable_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Variable_Floating_Point_Expression_defs.hh line 186. */

/* Automatically generated from PPL source file ../src/Sum_Floating_Point_Expression_defs.hh line 1. */
/* Declarations for the Sum_Floating_Point_Expression class and
   its   constituents.
*/


/* Automatically generated from PPL source file ../src/Sum_Floating_Point_Expression_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
class Sum_Floating_Point_Expression;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Sum_Floating_Point_Expression_defs.hh line 31. */
#include <map>

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Sum_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
void swap(Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
          Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y);

/*! \brief
  A generic Sum Floating Point Expression.

  \ingroup PPL_CXX_interface

  \par Template type parameters

  - The class template type parameter \p FP_Interval_Type represents the type
  of the intervals used in the abstract domain.
  - The class template type parameter \p FP_Format represents the floating
  point format used in the concrete domain.

  \par Linearization of sum floating-point expressions

  Let \f$i + \sum_{v \in \cV}i_{v}v \f$ and
  \f$i' + \sum_{v \in \cV}i'_{v}v \f$
  be two linear forms and \f$\aslf\f$ a sound abstract operator on linear
  forms such that:

  \f[
  \left(i + \sum_{v \in \cV}i_{v}v \right)
  \aslf
  \left(i' + \sum_{v \in \cV}i'_{v}v \right)
  =
  \left(i \asifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \asifp i'_{v} \right)v.
  \f]

  Given an expression \f$e_{1} \oplus e_{2}\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval linear form
  \f$\linexprenv{e_{1} \oplus e_{2}}{\rho^{\#}}{\rho^{\#}_l}\f$
  as follows:
  \f[
  \linexprenv{e_{1} \oplus e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  =
  \linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \aslf
  \linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \aslf
  \varepsilon_{\mathbf{f}}\left(\linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \right)
  \aslf
  \varepsilon_{\mathbf{f}}\left(\linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \right)
  \aslf
  mf_{\mathbf{f}}[-1, 1]
  \f]
  where \f$\varepsilon_{\mathbf{f}}(l)\f$ is the linear form computed by
  calling method <CODE>Floating_Point_Expression::relative_error</CODE>
  on \f$l\f$ and \f$mf_{\mathbf{f}}\f$ is a rounding error defined in
  <CODE>Floating_Point_Expression::absolute_error</CODE>.
*/
template <typename FP_Interval_Type, typename FP_Format>
class Sum_Floating_Point_Expression
  : public Floating_Point_Expression<FP_Interval_Type, FP_Format> {

public:

  /*! \brief
     Alias for the Linear_Form<FP_Interval_Type> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>
  ::FP_Linear_Form FP_Linear_Form;

  /*! \brief
     Alias for the Box<FP_Interval_Type> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>
  ::FP_Interval_Abstract_Store FP_Interval_Abstract_Store;

  /*! \brief
     Alias for the std::map<dimension_type, FP_Linear_Form> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Linear_Form_Abstract_Store FP_Linear_Form_Abstract_Store;

  /*! \brief
     Alias for the FP_Interval_Type::boundary_type from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::boundary_type
  boundary_type;

  /*! \brief
     Alias for the FP_Interval_Type::info_type from Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::info_type info_type;

  //! \name Constructors and Destructor
  //@{
  /*! \brief
    Constructor with two parameters: builds the sum floating point expression
    corresponding to \p x \f$\oplus\f$ \p y.
  */
  Sum_Floating_Point_Expression(
           Floating_Point_Expression<FP_Interval_Type, FP_Format>* const x,
           Floating_Point_Expression<FP_Interval_Type, FP_Format>* const y);

  //! Destructor.
  ~Sum_Floating_Point_Expression();

  //@} // Constructors and Destructor

  /*! \brief
    Linearizes the expression in a given astract store.

    Makes \p result become the linearization of \p *this in the given
    composite abstract store.

    \param int_store The interval abstract store.
    \param lf_store The linear form abstract store.
    \param result The modified linear form.

    \return <CODE>true</CODE> if the linearization succeeded,
    <CODE>false</CODE> otherwise.

    Note that all variables occuring in the expressions represented
    by \p first_operand and \p second_operand MUST have an associated value in
    \p int_store. If this precondition is not met, calling the method
    causes an undefined behavior.

    See the class description for a detailed explanation of how \p result
    is computed.
  */
  bool linearize(const FP_Interval_Abstract_Store& int_store,
                 const FP_Linear_Form_Abstract_Store& lf_store,
                 FP_Linear_Form& result) const;

  //! Swaps \p *this with \p y.
  void m_swap(Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y);

private:

  //! Pointer to the first operand.
  Floating_Point_Expression<FP_Interval_Type, FP_Format>* first_operand;
  //! Pointer to the second operand.
  Floating_Point_Expression<FP_Interval_Type, FP_Format>* second_operand;

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited copy constructor.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Sum_Floating_Point_Expression(
         const Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>& e);

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited assignment operator.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>&
  operator=(const Sum_Floating_Point_Expression<FP_Interval_Type,
            FP_Format>& e);


}; // class Sum_Floating_Point_Expression

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Sum_Floating_Point_Expression_inlines.hh line 1. */
/* Sum_Floating_Point_Expression class implementation: inline
   functions.
*/


/* Automatically generated from PPL source file ../src/Sum_Floating_Point_Expression_inlines.hh line 29. */

namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
inline
Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::Sum_Floating_Point_Expression(
         Floating_Point_Expression<FP_Interval_Type, FP_Format>* const x,
         Floating_Point_Expression<FP_Interval_Type, FP_Format>* const y)
  : first_operand(x), second_operand(y) {
  assert(x != 0);
  assert(y != 0);
}

template <typename FP_Interval_Type, typename FP_Format>
inline
Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::~Sum_Floating_Point_Expression() {
  delete first_operand;
  delete second_operand;
}

template <typename FP_Interval_Type, typename FP_Format>
inline void
Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::m_swap(Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y) {
  using std::swap;
  swap(first_operand, y.first_operand);
  swap(second_operand, y.second_operand);
}

/*! \relates Sum_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
inline void
swap(Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
     Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Sum_Floating_Point_Expression_templates.hh line 1. */
/* Sum_Floating_Point_Expression class implementation:
   non-inline template functions.
*/


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
bool Sum_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::linearize(const FP_Interval_Abstract_Store& int_store,
            const FP_Linear_Form_Abstract_Store& lf_store,
            FP_Linear_Form& result) const {
  if (!first_operand->linearize(int_store, lf_store, result)) {
    return false;
  }
  FP_Linear_Form rel_error;
  relative_error(result, rel_error);
  result += rel_error;
  FP_Linear_Form linearized_second_operand;
  if (!second_operand->linearize(int_store, lf_store,
                                linearized_second_operand)) {
    return false;
  }
  result += linearized_second_operand;
  relative_error(linearized_second_operand, rel_error);
  result += rel_error;
  result += this->absolute_error;
  return !this->overflows(result);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Sum_Floating_Point_Expression_defs.hh line 212. */

/* Automatically generated from PPL source file ../src/Difference_Floating_Point_Expression_defs.hh line 1. */
/* Declarations for the Difference_Floating_Point_Expression class and
   its constituents.
*/


/* Automatically generated from PPL source file ../src/Difference_Floating_Point_Expression_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
class Difference_Floating_Point_Expression;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Difference_Floating_Point_Expression_defs.hh line 31. */
#include <map>

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Difference_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
void
swap(Difference_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
     Difference_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y);

/*! \brief
  A generic Difference Floating Point Expression.
  \ingroup PPL_CXX_interface

  \par Template type parameters

  - The class template type parameter \p FP_Interval_Type represents the type
  of the intervals used in the abstract domain.
  - The class template type parameter \p FP_Format represents the floating
  point format used in the concrete domain.

  \par Linearization of difference floating-point expressions

  Let \f$i + \sum_{v \in \cV}i_{v}v \f$ and
  \f$i' + \sum_{v \in \cV}i'_{v}v \f$
  be two linear forms, \f$\aslf\f$ and \f$\adlf\f$ two sound abstract
  operators on linear form such that:
  \f[
  \left(i + \sum_{v \in \cV}i_{v}v\right)
  \aslf
  \left(i' + \sum_{v \in \cV}i'_{v}v\right)
  =
  \left(i \asifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \asifp i'_{v}\right)v,
  \f]
  \f[
  \left(i + \sum_{v \in \cV}i_{v}v\right)
  \adlf
  \left(i' + \sum_{v \in \cV}i'_{v}v\right)
  =
  \left(i \adifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \adifp i'_{v}\right)v.
  \f]
  Given an expression \f$e_{1} \ominus e_{2}\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$,  we construct the interval linear form
  \f$\linexprenv{e_{1} \ominus e_{2}}{\rho^{\#}}{\rho^{\#}_l}\f$
  on \f$\cV\f$ as follows:
  \f[
  \linexprenv{e_{1} \ominus e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  =
  \linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \adlf
  \linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \aslf
  \varepsilon_{\mathbf{f}}\left(\linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \right)
  \aslf
  \varepsilon_{\mathbf{f}}\left(\linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \right)
  \aslf
  mf_{\mathbf{f}}[-1, 1]
  \f]
  where \f$\varepsilon_{\mathbf{f}}(l)\f$ is the linear form computed by
  calling method <CODE>Floating_Point_Expression::relative_error</CODE>
  on \f$l\f$ and \f$mf_{\mathbf{f}}\f$ is a rounding error defined in
  <CODE>Floating_Point_Expression::absolute_error</CODE>.
*/
template <typename FP_Interval_Type, typename FP_Format>
class Difference_Floating_Point_Expression
  : public Floating_Point_Expression<FP_Interval_Type, FP_Format> {

public:

  /*! \brief
     Alias for the Linear_Form<FP_Interval_Type> from
     Floating_Point_Expression
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>
  ::FP_Linear_Form FP_Linear_Form;

  /*! \brief
     Alias for the Box<FP_Interval_Type> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>
  ::FP_Interval_Abstract_Store FP_Interval_Abstract_Store;

  /*! \brief
     Alias for the std::map<dimension_type, FP_Linear_Form> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>
  ::FP_Linear_Form_Abstract_Store FP_Linear_Form_Abstract_Store;

  /*! \brief
     Alias for the FP_Interval_Type::boundary_type from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::boundary_type
  boundary_type;

  /*! \brief
     Alias for the FP_Interval_Type::info_type from Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::info_type info_type;

  //! \name Constructors and Destructor
  //@{
  /*! \brief
    Constructor with two parameters: builds the difference floating point
    expression corresponding to \p x \f$\ominus\f$ \p y.
  */
  Difference_Floating_Point_Expression(
           Floating_Point_Expression<FP_Interval_Type, FP_Format>* const x,
           Floating_Point_Expression<FP_Interval_Type, FP_Format>* const y);

  //! Destructor.
  ~Difference_Floating_Point_Expression();

  //@} // Constructors and Destructor

  /*! \brief
    Linearizes the expression in a given astract store.

    Makes \p result become the linearization of \p *this in the given
    composite abstract store.

    \param int_store The interval abstract store.
    \param lf_store The linear form abstract store.
    \param result The modified linear form.

    \return <CODE>true</CODE> if the linearization succeeded,
    <CODE>false</CODE> otherwise.

    Note that all variables occuring in the expressions represented
    by \p first_operand and \p second_operand MUST have an associated value in
    \p int_store. If this precondition is not met, calling the method
    causes an undefined behavior.

    See the class description for a detailed explanation of how \p result
    is computed.
  */
  bool linearize(const FP_Interval_Abstract_Store& int_store,
                 const FP_Linear_Form_Abstract_Store& lf_store,
                 FP_Linear_Form& result) const;

  //! Swaps \p *this with \p y.
  void m_swap(Difference_Floating_Point_Expression<FP_Interval_Type,
                                                   FP_Format>& y);

private:

  //! Pointer to the first operand.
  Floating_Point_Expression<FP_Interval_Type, FP_Format>* first_operand;
  //! Pointer to the second operand.
  Floating_Point_Expression<FP_Interval_Type, FP_Format>* second_operand;

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited copy constructor.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Difference_Floating_Point_Expression(
         const Difference_Floating_Point_Expression<FP_Interval_Type,
                                                    FP_Format>& e);

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited asssignment operator.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Difference_Floating_Point_Expression<FP_Interval_Type, FP_Format>&
  operator=(const Difference_Floating_Point_Expression<FP_Interval_Type,
                                                       FP_Format>& e);


}; // class Difference_Floating_Point_Expression

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Difference_Floating_Point_Expression_inlines.hh line 1. */
/* Difference_Floating_Point_Expression class implementation: inline
   functions.
*/


/* Automatically generated from PPL source file ../src/Difference_Floating_Point_Expression_inlines.hh line 29. */

namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
inline
Difference_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::Difference_Floating_Point_Expression(
         Floating_Point_Expression<FP_Interval_Type, FP_Format>* const x,
         Floating_Point_Expression<FP_Interval_Type, FP_Format>* const y)
  : first_operand(x), second_operand(y) {
  assert(x != 0);
  assert(y != 0);
}

template <typename FP_Interval_Type, typename FP_Format>
inline
Difference_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::~Difference_Floating_Point_Expression() {
  delete first_operand;
  delete second_operand;
}

template <typename FP_Interval_Type, typename FP_Format>
inline void
Difference_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::m_swap(Difference_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y) {
  using std::swap;
  swap(first_operand, y.first_operand);
  swap(second_operand, y.second_operand);
}

/*! \relates Difference_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
inline void
swap(Difference_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
     Difference_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Difference_Floating_Point_Expression_templates.hh line 1. */
/* Difference_Floating_Point_Expression class implementation:
   non-inline template functions.
*/


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
bool Difference_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::linearize(const FP_Interval_Abstract_Store& int_store,
            const FP_Linear_Form_Abstract_Store& lf_store,
            FP_Linear_Form& result) const {
  if (!first_operand->linearize(int_store, lf_store, result)) {
    return false;
  }
  FP_Linear_Form rel_error;
  relative_error(result, rel_error);
  result += rel_error;
  FP_Linear_Form linearized_second_operand;
  if (!second_operand->linearize(int_store, lf_store,
                      linearized_second_operand)) {
    return false;
  }
  result -= linearized_second_operand;
  relative_error(linearized_second_operand, rel_error);
  result += rel_error;
  result += this->absolute_error;
  return !this->overflows(result);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Difference_Floating_Point_Expression_defs.hh line 220. */

/* Automatically generated from PPL source file ../src/Multiplication_Floating_Point_Expression_defs.hh line 1. */
/* Declarations for the Multiplication_Floating_Point_Expression class and
   its constituents.
*/


/* Automatically generated from PPL source file ../src/Multiplication_Floating_Point_Expression_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
class Multiplication_Floating_Point_Expression;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Multiplication_Floating_Point_Expression_defs.hh line 31. */
#include <map>

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Multiplication_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
void
swap(Multiplication_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
     Multiplication_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y);

/*! \brief
  A generic Multiplication Floating Point Expression.

  \ingroup PPL_CXX_interface

  \par Template type parameters

  - The class template type parameter \p FP_Interval_Type represents the type
  of the intervals used in the abstract domain.
  - The class template type parameter \p FP_Format represents the floating
  point format used in the concrete domain.

  \par Linearization of multiplication floating-point expressions

  Let \f$i + \sum_{v \in \cV}i_{v}v \f$ and
  \f$i' + \sum_{v \in \cV}i'_{v}v \f$
  be two linear forms, \f$\aslf\f$ and \f$\amlf\f$ two sound abstract
  operators on linear forms such that:
  \f[
  \left(i + \sum_{v \in \cV}i_{v}v\right)
  \aslf
  \left(i' + \sum_{v \in \cV}i'_{v}v\right)
  =
  \left(i \asifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \asifp i'_{v}\right)v,
  \f]
  \f[
  i
  \amlf
  \left(i' + \sum_{v \in \cV}i'_{v}v\right)
  =
  \left(i \amifp i'\right)
  + \sum_{v \in \cV}\left(i \amifp i'_{v}\right)v.
  \f]
  Given an expression \f$[a, b] \otimes e_{2}\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval linear form
  \f$\linexprenv{[a, b] \otimes e_{2}}{\rho^{\#}}{\rho^{\#}_l}\f$
  as follows:
  \f[
  \linexprenv{[a, b] \otimes e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  =
  \left([a, b]
  \amlf
  \linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}\right)
  \aslf
  \left([a, b]
  \amlf
  \varepsilon_{\mathbf{f}}\left(\linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \right)\right)
  \aslf
  mf_{\mathbf{f}}[-1, 1].
  \f].

  Given an expression \f$e_{1} \otimes [a, b]\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval linear form
  \f$\linexprenv{e_{1} \otimes [a, b]}{\rho^{\#}}{\rho^{\#}_l}\f$
  as follows:
  \f[
  \linexprenv{e_{1} \otimes [a, b]}{\rho^{\#}}{\rho^{\#}_l}
  =
  \linexprenv{[a, b] \otimes e_{1}}{\rho^{\#}}{\rho^{\#}_l}.
  \f]

  Given an expression \f$e_{1} \otimes e_{2}\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval linear form
  \f$\linexprenv{e_{1} \otimes e_{2}}{\rho^{\#}}{\rho^{\#}_l}\f$
  as follows:
  \f[
  \linexprenv{e_{1} \otimes e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  =
  \linexprenv{\iota\left(\linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \right)\rho^{\#}
  \otimes e_{2}}{\rho^{\#}}{\rho^{\#}_l},
  \f]
  where \f$\varepsilon_{\mathbf{f}}(l)\f$ is the linear form computed by
  calling method <CODE>Floating_Point_Expression::relative_error</CODE>
  on \f$l\f$, \f$\iota(l)\rho^{\#}\f$ is the linear form computed by calling
  method <CODE>Floating_Point_Expression::intervalize</CODE> on \f$l\f$
  and \f$\rho^{\#}\f$, and \f$mf_{\mathbf{f}}\f$ is a rounding error defined in
  <CODE>Floating_Point_Expression::absolute_error</CODE>.

  Even though we intervalize the first operand in the above example, the
  actual implementation utilizes an heuristics for choosing which of the two
  operands must be intervalized in order to obtain the most precise result.
*/
template <typename FP_Interval_Type, typename FP_Format>
class Multiplication_Floating_Point_Expression
  : public Floating_Point_Expression<FP_Interval_Type, FP_Format> {

public:

  /*! \brief
     Alias for the Linear_Form<FP_Interval_Type> from
     Floating_Point_Expression
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>
  ::FP_Linear_Form FP_Linear_Form;

  /*! \brief
     Alias for the Box<FP_Interval_Type> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>
  ::FP_Interval_Abstract_Store FP_Interval_Abstract_Store;

  /*! \brief
     Alias for the std::map<dimension_type, FP_Linear_Form> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Linear_Form_Abstract_Store FP_Linear_Form_Abstract_Store;

  /*! \brief
     Alias for the FP_Interval_Type::boundary_type from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::boundary_type
  boundary_type;

  /*! \brief
     Alias for the FP_Interval_Type::info_type from Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::info_type info_type;

  //! \name Constructors and Destructor
  //@{
  /*! \brief
    Constructor with two parameters: builds the multiplication floating point
    expression corresponding to \p x \f$\otimes\f$ \p y.
  */
  Multiplication_Floating_Point_Expression(
           Floating_Point_Expression<FP_Interval_Type, FP_Format>* const x,
           Floating_Point_Expression<FP_Interval_Type, FP_Format>* const y);

  //! Destructor.
  ~Multiplication_Floating_Point_Expression();

  //@} // Constructors and Destructor.

  /*! \brief
    Linearizes the expression in a given astract store.

    Makes \p result become the linearization of \p *this in the given
    composite abstract store.

    \param int_store The interval abstract store.
    \param lf_store The linear form abstract store.
    \param result The modified linear form.

    \return <CODE>true</CODE> if the linearization succeeded,
    <CODE>false</CODE> otherwise.

    Note that all variables occuring in the expressions represented
    by \p first_operand and \p second_operand MUST have an associated value in
    \p int_store. If this precondition is not met, calling the method
    causes an undefined behavior.

    See the class description for a detailed explanation of how \p result
    is computed.
  */
  bool linearize(const FP_Interval_Abstract_Store& int_store,
                         const FP_Linear_Form_Abstract_Store& lf_store,
                       FP_Linear_Form& result) const;

  //! Swaps \p *this with \p y.
  void m_swap(Multiplication_Floating_Point_Expression<FP_Interval_Type,
                                                       FP_Format>& y);

private:

  //! Pointer to the first operand.
  Floating_Point_Expression<FP_Interval_Type, FP_Format>* first_operand;
  //! Pointer to the second operand.
  Floating_Point_Expression<FP_Interval_Type, FP_Format>* second_operand;

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited copy constructor.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Multiplication_Floating_Point_Expression(
         const Multiplication_Floating_Point_Expression<FP_Interval_Type,
                                                        FP_Format>& e);

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited assignment operator.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Multiplication_Floating_Point_Expression<FP_Interval_Type, FP_Format>&
  operator=(const Multiplication_Floating_Point_Expression<FP_Interval_Type,
            FP_Format>& e);


}; // class Multiplication_Floating_Point_Expression

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Multiplication_Floating_Point_Expression_inlines.hh line 1. */
/* Multiplication_Floating_Point_Expression class implementation: inline
   functions.
*/


/* Automatically generated from PPL source file ../src/Multiplication_Floating_Point_Expression_inlines.hh line 29. */

namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
inline
Multiplication_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::Multiplication_Floating_Point_Expression(
         Floating_Point_Expression<FP_Interval_Type, FP_Format>* const x,
         Floating_Point_Expression<FP_Interval_Type, FP_Format>* const y)
  : first_operand(x), second_operand(y) {
  assert(x != 0);
  assert(y != 0);
}

template <typename FP_Interval_Type, typename FP_Format>
inline
Multiplication_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::~Multiplication_Floating_Point_Expression() {
  delete first_operand;
  delete second_operand;
}

template <typename FP_Interval_Type, typename FP_Format>
inline void
Multiplication_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::m_swap(Multiplication_Floating_Point_Expression<FP_Interval_Type,
                                                  FP_Format>& y) {
  using std::swap;
  swap(first_operand, y.first_operand);
  swap(second_operand, y.second_operand);
}

/*! \relates Multiplication_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
inline void
swap(Multiplication_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
     Multiplication_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Multiplication_Floating_Point_Expression_templates.hh line 1. */
/* Multiplication_Floating_Point_Expression class implementation:
   non-inline template functions.
*/


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
bool Multiplication_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::linearize(const FP_Interval_Abstract_Store& int_store,
            const FP_Linear_Form_Abstract_Store& lf_store,
            FP_Linear_Form& result) const {
  /*
    FIXME: We currently adopt the "Interval-Size Local" strategy in order to
    decide which of the two linear forms must be intervalized, as described
    in Section 6.2.4 ("Multiplication Strategies") of Antoine Mine's Ph.D.
    thesis "Weakly Relational Numerical Abstract Domains".
    In this Section are also described other multiplication strategies, such
    as All-Cases, Relative-Size Local, Simplification-Driven Global and
    Homogeneity Global.
  */

  // Here we choose which of the two linear forms must be intervalized.

  // true if we intervalize the first form, false if we intervalize the second.
  bool intervalize_first;
  FP_Linear_Form linearized_first_operand;
  if (!first_operand->linearize(int_store, lf_store,
                               linearized_first_operand)) {
    return false;
  }
  FP_Interval_Type intervalized_first_operand;
  this->intervalize(linearized_first_operand, int_store,
                    intervalized_first_operand);
  FP_Linear_Form linearized_second_operand;
  if (!second_operand->linearize(int_store, lf_store,
                                linearized_second_operand)) {
    return false;
  }
  FP_Interval_Type intervalized_second_operand;
  this->intervalize(linearized_second_operand, int_store,
                    intervalized_second_operand);

  // FIXME: we are not sure that what we do here is policy-proof.
  if (intervalized_first_operand.is_bounded()) {
    if (intervalized_second_operand.is_bounded()) {
      boundary_type first_interval_size
        = intervalized_first_operand.upper()
        - intervalized_first_operand.lower();
      boundary_type second_interval_size
        = intervalized_second_operand.upper()
        - intervalized_second_operand.lower();
      if (first_interval_size <= second_interval_size) {
        intervalize_first = true;
      }
      else {
        intervalize_first = false;
      }
    }
    else {
      intervalize_first = true;
    }
  }
  else {
    if (intervalized_second_operand.is_bounded()) {
      intervalize_first = false;
    }
    else {
      return false;
    }
  }

  // Here we do the actual computation.
  // For optimizing, we store the relative error directly into result.
  if (intervalize_first) {
    relative_error(linearized_second_operand, result);
    linearized_second_operand *= intervalized_first_operand;
    result *= intervalized_first_operand;
    result += linearized_second_operand;
  }
  else {
    relative_error(linearized_first_operand, result);
    linearized_first_operand *= intervalized_second_operand;
    result *= intervalized_second_operand;
    result += linearized_first_operand;
  }

  result += this->absolute_error;
  return !this->overflows(result);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Multiplication_Floating_Point_Expression_defs.hh line 250. */

/* Automatically generated from PPL source file ../src/Division_Floating_Point_Expression_defs.hh line 1. */
/* Declarations for the Division_Floating_Point_Expression class and its
   constituents.
*/


/* Automatically generated from PPL source file ../src/Division_Floating_Point_Expression_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
class Division_Floating_Point_Expression;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Division_Floating_Point_Expression_defs.hh line 31. */
#include <map>

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Division_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
void swap(Division_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
          Division_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y);

/*! \brief
  A generic Division Floating Point Expression.

  \ingroup PPL_CXX_interface

  \par Template type parameters

  - The class template type parameter \p FP_Interval_Type represents the type
  of the intervals used in the abstract domain.
  - The class template type parameter \p FP_Format represents the floating
  point format used in the concrete domain.

  \par Linearizationd of division floating-point expressions

  Let \f$i + \sum_{v \in \cV}i_{v}v \f$ and
  \f$i' + \sum_{v \in \cV}i'_{v}v \f$
  be two linear forms, \f$\aslf\f$ and \f$\adivlf\f$ two sound abstract
  operator on linear forms such that:
  \f[
  \left(i + \sum_{v \in \cV}i_{v}v\right)
  \aslf
  \left(i' + \sum_{v \in \cV}i'_{v}v\right)
  =
  \left(i \asifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \asifp i'_{v}\right)v,
  \f]
  \f[
  \left(i + \sum_{v \in \cV}i_{v}v\right)
  \adivlf
  i'
  =
  \left(i \adivifp i'\right)
  + \sum_{v \in \cV}\left(i_{v} \adivifp i'\right)v.
  \f]
  Given an expression \f$e_{1} \oslash [a, b]\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$,
  we construct the interval linear form
  \f$
  \linexprenv{e_{1} \oslash [a, b]}{\rho^{\#}}{\rho^{\#}_l}
  \f$
  as follows:
  \f[
  \linexprenv{e_{1} \oslash [a, b]}{\rho^{\#}}{\rho^{\#}_l}
  =
  \left(\linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \adivlf
  [a, b]\right)
  \aslf
  \left(\varepsilon_{\mathbf{f}}\left(
  \linexprenv{e_{1}}{\rho^{\#}}{\rho^{\#}_l}
  \right)
  \adivlf
  [a, b]\right)
  \aslf
  mf_{\mathbf{f}}[-1, 1],
  \f]
  given an expression \f$e_{1} \oslash e_{2}\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval linear form
  \f$\linexprenv{e_{1} \oslash e_{2}}{\rho^{\#}}{\rho^{\#}_l}\f$
  as follows:
  \f[
  \linexprenv{e_{1} \oslash e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  =
  \linexprenv{e_{1} \oslash \iota\left(
  \linexprenv{e_{2}}{\rho^{\#}}{\rho^{\#}_l}
  \right)\rho^{\#}}{\rho^{\#}}{\rho^{\#}_l},
  \f]
  where \f$\varepsilon_{\mathbf{f}}(l)\f$ is the linear form computed by
  calling method <CODE>Floating_Point_Expression::relative_error</CODE>
  on \f$l\f$, \f$\iota(l)\rho^{\#}\f$ is the linear form computed by calling
  method <CODE>Floating_Point_Expression::intervalize</CODE> on \f$l\f$
  and \f$\rho^{\#}\f$, and \f$mf_{\mathbf{f}}\f$ is a rounding error defined in
  <CODE>Floating_Point_Expression::absolute_error</CODE>.
*/
template <typename FP_Interval_Type, typename FP_Format>
class Division_Floating_Point_Expression
  : public Floating_Point_Expression<FP_Interval_Type, FP_Format> {

public:

  /*! \brief
     Alias for the Linear_Form<FP_Interval_Type> from
     Floating_Point_Expression
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>
  ::FP_Linear_Form FP_Linear_Form;

  /*! \brief
     Alias for the Box<FP_Interval_Type> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>
  ::FP_Interval_Abstract_Store FP_Interval_Abstract_Store;

  /*! \brief
     Alias for the std::map<dimension_type, FP_Linear_Form> from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Linear_Form_Abstract_Store FP_Linear_Form_Abstract_Store;

  /*! \brief
     Alias for the FP_Interval_Type::boundary_type from
     Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::boundary_type
  boundary_type;

  /*! \brief
     Alias for the FP_Interval_Type::info_type from Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::info_type info_type;

  //! \name Constructors and Destructor
  //@{
  /*! \brief
    Constructor with two parameters: builds the division floating point
    expression corresponding to \p num \f$\oslash\f$ \p den.
  */
  Division_Floating_Point_Expression(
           Floating_Point_Expression<FP_Interval_Type, FP_Format>* const num,
           Floating_Point_Expression<FP_Interval_Type, FP_Format>* const den);

  //! Destructor.
  ~Division_Floating_Point_Expression();

  //@} // Constructors and Destructor

  /*! \brief
    Linearizes the expression in a given astract store.

    Makes \p result become the linearization of \p *this in the given
    composite abstract store.

    \param int_store The interval abstract store.
    \param lf_store The linear form abstract store.
    \param result The modified linear form.

    \return <CODE>true</CODE> if the linearization succeeded,
    <CODE>false</CODE> otherwise.

    Note that all variables occuring in the expressions represented
    by \p first_operand and \p second_operand MUST have an associated value in
    \p int_store. If this precondition is not met, calling the method
    causes an undefined behavior.

    See the class description for a detailed explanation of how \p result
    is computed.
  */
  bool linearize(const FP_Interval_Abstract_Store& int_store,
                 const FP_Linear_Form_Abstract_Store& lf_store,
                 FP_Linear_Form& result) const;

  //! Swaps \p *this with \p y.
  void m_swap(Division_Floating_Point_Expression<FP_Interval_Type,
                                                 FP_Format>& y);

private:

  //! Pointer to the first operand.
  Floating_Point_Expression<FP_Interval_Type, FP_Format>* first_operand;
  //! Pointer to the second operand.
  Floating_Point_Expression<FP_Interval_Type, FP_Format>* second_operand;

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Copy constructor: temporary inhibited.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Division_Floating_Point_Expression(
         const Division_Floating_Point_Expression<FP_Interval_Type,
                                                  FP_Format>& e);

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Assignment operator: temporary inhibited.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Division_Floating_Point_Expression<FP_Interval_Type, FP_Format>&
  operator=(const Division_Floating_Point_Expression<FP_Interval_Type,
            FP_Format>& e);

}; // class Division_Floating_Point_Expression

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Division_Floating_Point_Expression_inlines.hh line 1. */
/* Division_Floating_Point_Expression class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Division_Floating_Point_Expression_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
inline
Division_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::Division_Floating_Point_Expression(
         Floating_Point_Expression<FP_Interval_Type, FP_Format>* const num,
         Floating_Point_Expression<FP_Interval_Type, FP_Format>* const den)
  : first_operand(num), second_operand(den) {
  assert(num != 0);
  assert(den != 0);
}

template <typename FP_Interval_Type, typename FP_Format>
inline
Division_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::~Division_Floating_Point_Expression() {
  delete first_operand;
  delete second_operand;
}

template <typename FP_Interval_Type, typename FP_Format>
inline void
Division_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::m_swap(Division_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y) {
  using std::swap;
  swap(first_operand, y.first_operand);
  swap(second_operand, y.second_operand);
}

/*! \relates Division_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
inline void
swap(Division_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
     Division_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Division_Floating_Point_Expression_templates.hh line 1. */
/* Division_Floating_Point_Expression class implementation:
   non-inline template functions.
*/


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
bool Division_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::linearize(const FP_Interval_Abstract_Store& int_store,
            const FP_Linear_Form_Abstract_Store& lf_store,
            FP_Linear_Form& result) const {
  FP_Linear_Form linearized_second_operand;
  if (!second_operand->linearize(int_store, lf_store,
                                linearized_second_operand)) {
    return false;
  }
  FP_Interval_Type intervalized_second_operand;
  this->intervalize(linearized_second_operand, int_store,
                    intervalized_second_operand);

  // Check if we may divide by zero.
  if (intervalized_second_operand.lower() <= 0
      && intervalized_second_operand.upper() >= 0) {
    return false;
  }

  if (!first_operand->linearize(int_store, lf_store, result)) {
    return false;
  }
  FP_Linear_Form rel_error;
  relative_error(result, rel_error);
  result /= intervalized_second_operand;
  rel_error /= intervalized_second_operand;
  result += rel_error;
  result += this->absolute_error;
  return !this->overflows(result);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Division_Floating_Point_Expression_defs.hh line 236. */

/* Automatically generated from PPL source file ../src/Opposite_Floating_Point_Expression_defs.hh line 1. */
/* Declarations for the Opposite_Floating_Point_Expression class and
   its constituents.
*/


/* Automatically generated from PPL source file ../src/Opposite_Floating_Point_Expression_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
class Opposite_Floating_Point_Expression;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Opposite_Floating_Point_Expression_defs.hh line 31. */
#include <map>

namespace Parma_Polyhedra_Library {

//! Swaps \p x with \p y.
/*! \relates Opposite_Floating_Point_Expression */
template<typename FP_Interval_Type, typename FP_Format>
void swap(Opposite_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
          Opposite_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y);

/*! \brief
  A generic Opposite Floating Point Expression.

  \ingroup PPL_CXX_interface

  \par Template type parameters

  - The class template type parameter \p FP_Interval_Type represents the type
  of the intervals used in the abstract domain.
  - The class template type parameter \p FP_Format represents the floating
  point format used in the concrete domain.

  \par Linearization of opposite floating-point expressions

  Let \f$i + \sum_{v \in \cV}i_{v}v \f$ be an interval linear form and
  let \f$\adlf\f$ be a sound unary operator on linear forms such that:

  \f[
  \adlf
  \left(i + \sum_{v \in \cV}i_{v}v\right)
  =
  \left(\adifp i\right)
  + \sum_{v \in \cV}\left(\adifp i_{v} \right)v,
  \f]

  Given a floating point expression \f$\ominus e\f$ and a composite
  abstract store \f$\left \llbracket \rho^{\#}, \rho^{\#}_l \right
  \rrbracket\f$, we construct the interval linear form
  \f$\linexprenv{\ominus e}{\rho^{\#}}{\rho^{\#}_l}\f$
  as follows:
  \f[
  \linexprenv{\ominus e}{\rho^{\#}}{\rho^{\#}_l}
  =
  \adlf
  \left(
  \linexprenv{e}{\rho^{\#}}{\rho^{\#}_l}
  \right).
  \f]
*/
template <typename FP_Interval_Type, typename FP_Format>
class Opposite_Floating_Point_Expression
  : public Floating_Point_Expression<FP_Interval_Type, FP_Format> {

public:

  /*! \brief
    Alias for the Linear_Form<FP_Interval_Type> from
    Floating_Point_Expression
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Linear_Form FP_Linear_Form;

  /*! \brief
    Alias for the std::map<dimension_type, FP_Interval_Type> from
    Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Interval_Abstract_Store FP_Interval_Abstract_Store;

  /*! \brief
    Alias for the std::map<dimension_type, FP_Linear_Form> from
    Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::
  FP_Linear_Form_Abstract_Store FP_Linear_Form_Abstract_Store;

  /*! \brief
    Alias for the FP_Interval_Type::boundary_type from
    Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::boundary_type
  boundary_type;

  /*! \brief
    Alias for the FP_Interval_Type::info_type from Floating_Point_Expression.
  */
  typedef typename
  Floating_Point_Expression<FP_Interval_Type, FP_Format>::info_type info_type;

  //! \name Constructors and Destructor
  //@{
  /*! \brief
    Constructor with one parameter: builds the opposite floating point
    expression \f$\ominus\f$ \p op.
  */
  explicit Opposite_Floating_Point_Expression(
           Floating_Point_Expression<FP_Interval_Type, FP_Format>* const op);

  //! Destructor.
  ~Opposite_Floating_Point_Expression();

  //@} // Constructors and Destructor

  /*! \brief
    Linearizes the expression in a given astract store.

    Makes \p result become the linearization of \p *this in the given
    composite abstract store.

    \param int_store The interval abstract store.
    \param lf_store The linear form abstract store.
    \param result The modified linear form.

    \return <CODE>true</CODE> if the linearization succeeded,
    <CODE>false</CODE> otherwise.

    Note that all variables occuring in the expression represented
    by \p operand MUST have an associated value in \p int_store.
    If this precondition is not met, calling the method
    causes an undefined behavior.

    See the class description for a detailed explanation of how \p result
    is computed.
  */
  bool linearize(const FP_Interval_Abstract_Store& int_store,
                 const FP_Linear_Form_Abstract_Store& lf_store,
                 FP_Linear_Form& result) const;

  //! Swaps \p *this with \p y.
  void m_swap(Opposite_Floating_Point_Expression& y);

private:

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited copy constructor.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Opposite_Floating_Point_Expression(
                          const Opposite_Floating_Point_Expression& y);

  #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  /*! \brief
    Inhibited assignment operator.
  */
  #endif // PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS
  Opposite_Floating_Point_Expression& operator=(
                          const Opposite_Floating_Point_Expression& y);

  //! Pointer to the operand.
  Floating_Point_Expression<FP_Interval_Type, FP_Format>* operand;

}; // class Opposite_Floating_Point_Expression

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Opposite_Floating_Point_Expression_inlines.hh line 1. */
/* Opposite_Floating_Point_Expression class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Opposite_Floating_Point_Expression_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

template <typename FP_Interval_Type, typename FP_Format>
inline
Opposite_Floating_Point_Expression<FP_Interval_Type, FP_Format>::
Opposite_Floating_Point_Expression(
         Floating_Point_Expression<FP_Interval_Type, FP_Format>* const op)
  : operand(op)
{
  assert(op != 0);
}

template <typename FP_Interval_Type, typename FP_Format>
inline
Opposite_Floating_Point_Expression<FP_Interval_Type, FP_Format>::
~Opposite_Floating_Point_Expression() {
  delete operand;
}

template <typename FP_Interval_Type, typename FP_Format>
inline void
Opposite_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::m_swap(Opposite_Floating_Point_Expression& y) {
  using std::swap;
  swap(operand, y.operand);
}

template <typename FP_Interval_Type, typename FP_Format>
inline bool
Opposite_Floating_Point_Expression<FP_Interval_Type, FP_Format>
::linearize(const FP_Interval_Abstract_Store& int_store,
            const FP_Linear_Form_Abstract_Store& lf_store,
            FP_Linear_Form& result) const {
  if (!operand->linearize(int_store, lf_store, result)) {
    return false;
  }

  result.negate();
  return true;
}

/*! \relates Opposite_Floating_Point_Expression */
template <typename FP_Interval_Type, typename FP_Format>
inline void
swap(Opposite_Floating_Point_Expression<FP_Interval_Type, FP_Format>& x,
     Opposite_Floating_Point_Expression<FP_Interval_Type, FP_Format>& y) {
  x.m_swap(y);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Opposite_Floating_Point_Expression_defs.hh line 192. */

/* Automatically generated from PPL source file ../src/Watchdog_defs.hh line 1. */
/* Watchdog and associated classes' declaration and inline functions.
*/


/* Automatically generated from PPL source file ../src/Watchdog_types.hh line 1. */


namespace Parma_Polyhedra_Library {

class Watchdog;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Time_defs.hh line 1. */
/* Time class declaration.
*/


/* Automatically generated from PPL source file ../src/Time_types.hh line 1. */


namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Watchdog {

class Time;

} // namespace Watchdog

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Time_defs.hh line 28. */

namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Watchdog {

//! Returns <CODE>true</CODE> if and only if \p x and \p y are equal.
bool operator==(const Time& x, const Time& y);

//! Returns <CODE>true</CODE> if and only if \p x and \p y are different.
bool operator!=(const Time& x, const Time& y);

//! Returns <CODE>true</CODE> if and only if \p x is shorter than \p y.
bool operator<(const Time& x, const Time& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if \p x is shorter than
  or equal to \p y.
*/
bool operator<=(const Time& x, const Time& y);

//! Returns <CODE>true</CODE> if and only if \p x is longer than \p y.
bool operator>(const Time& x, const Time& y);

/*! \brief
  Returns <CODE>true</CODE> if and only if \p x is longer than
  or equal to \p y.
*/
bool operator>=(const Time& x, const Time& y);

//! Returns the sum of \p x and \p y.
Time operator+(const Time& x, const Time& y);

/*! \brief
  Returns the difference of \p x and \p y or the null interval,
  if \p x is shorter than \p y.
*/
Time operator-(const Time& x, const Time& y);

} // namespace Watchdog

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

//! A class for representing and manipulating positive time intervals.
class Parma_Polyhedra_Library::Implementation::Watchdog::Time {
public:
  //! Zero seconds.
  Time();

  //! Constructor taking a number of centiseconds.
  explicit Time(long centisecs);

  //! Constructor with seconds and microseconds.
  Time(long s, long m);

  /*! \brief
    Returns the number of whole seconds contained in the represented
    time interval.
  */
  long seconds() const;

  /*! \brief
    Returns the number of microseconds that, when added to the number
    of seconds returned by seconds(), give the represent time interval.
  */
  long microseconds() const;

  //! Adds \p y to \p *this.
  Time& operator+=(const Time& y);

  /*! \brief
    Subtracts \p y from \p *this; if \p *this is shorter than \p y,
    \p *this is set to the null interval.
  */
  Time& operator-=(const Time& y);

  //! Checks if all the invariants are satisfied.
  bool OK() const;

private:
  //! Number of microseconds in a second.
  static const long USECS_PER_SEC = 1000000L;

  //! Number of centiseconds in a second.
  static const long CSECS_PER_SEC = 100L;

  //! Number of seconds.
  long secs;

  //! Number of microseconds.
  long microsecs;
};

/* Automatically generated from PPL source file ../src/Time_inlines.hh line 1. */
/* Time class implementation: inline functions.
*/


#include <cassert>

namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Watchdog {

inline
Time::Time()
  : secs(0), microsecs(0) {
  assert(OK());
}

inline
Time::Time(long centisecs)
  : secs(centisecs / CSECS_PER_SEC),
    microsecs((centisecs % CSECS_PER_SEC) * (USECS_PER_SEC/CSECS_PER_SEC)) {
  assert(OK());
}

inline
Time::Time(long s, long m)
  : secs(s),
    microsecs(m) {
  if (microsecs >= USECS_PER_SEC) {
    secs += microsecs / USECS_PER_SEC;
    microsecs %= USECS_PER_SEC;
  }
  assert(OK());
}

inline long
Time::seconds() const {
  return secs;
}

inline long
Time::microseconds() const {
  return microsecs;
}

inline Time&
Time::operator+=(const Time& y) {
  long r_secs = secs + y.secs;
  long r_microsecs = microsecs + y.microsecs;
  if (r_microsecs >= USECS_PER_SEC) {
    ++r_secs;
    r_microsecs %= USECS_PER_SEC;
  }
  secs = r_secs;
  microsecs = r_microsecs;
  assert(OK());
  return *this;
}

inline Time&
Time::operator-=(const Time& y) {
  long r_secs = secs - y.secs;
  long r_microsecs = microsecs - y.microsecs;
  if (r_microsecs < 0) {
    --r_secs;
    r_microsecs += USECS_PER_SEC;
  }
  if (r_secs < 0) {
    r_secs = 0;
    r_microsecs = 0;
  }
  secs = r_secs;
  microsecs = r_microsecs;
  assert(OK());
  return *this;
}

inline Time
operator+(const Time& x, const Time& y) {
  Time z = x;
  z += y;
  return z;
}

inline Time
operator-(const Time& x, const Time& y) {
  Time z = x;
  z -= y;
  return z;
}

inline bool
operator==(const Time& x, const Time& y) {
  assert(x.OK() && y.OK());
  return x.seconds() == y.seconds() && y.microseconds() == y.microseconds();
}

inline bool
operator!=(const Time& x, const Time& y) {
  assert(x.OK() && y.OK());
  return !(x == y);
}

inline bool
operator<(const Time& x, const Time& y) {
  assert(x.OK() && y.OK());
  return x.seconds() < y.seconds()
    || (x.seconds() == y.seconds() && x.microseconds() < y.microseconds());
}

inline bool
operator<=(const Time& x, const Time& y) {
  return x < y || x == y;
}

inline bool
operator>(const Time& x, const Time& y) {
  return y < x;
}

inline bool
operator>=(const Time& x, const Time& y) {
  return y <= x;
}

} // namespace Watchdog

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Time_defs.hh line 125. */

/* Automatically generated from PPL source file ../src/Handler_types.hh line 1. */


namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Watchdog {

class Handler;

template <typename Flag_Base, typename Flag>
class Handler_Flag;

class Handler_Function;

} // namespace Watchdog

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Pending_List_defs.hh line 1. */
/* Pending_List class declaration.
*/


/* Automatically generated from PPL source file ../src/Pending_List_types.hh line 1. */


namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Watchdog {

template <typename Traits>
class Pending_List;

} // namespace Watchdog

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Pending_Element_defs.hh line 1. */
/* Pending_Element class declaration.
*/


/* Automatically generated from PPL source file ../src/Pending_Element_types.hh line 1. */


namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Watchdog {

template <class Threshold>
class Pending_Element;

} // namespace Watchdog

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Doubly_Linked_Object_defs.hh line 1. */
/* Doubly_Linked_Object class declaration.
*/


/* Automatically generated from PPL source file ../src/Doubly_Linked_Object_types.hh line 1. */


namespace Parma_Polyhedra_Library {

namespace Implementation {

class Doubly_Linked_Object;

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/EList_types.hh line 1. */


namespace Parma_Polyhedra_Library {

namespace Implementation {

template <typename T>
class EList;

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/EList_Iterator_types.hh line 1. */


namespace Parma_Polyhedra_Library {

namespace Implementation {

template <typename T>
class EList_Iterator;

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Doubly_Linked_Object_defs.hh line 30. */

//! A (base) class for doubly linked objects.
class Parma_Polyhedra_Library::Implementation::Doubly_Linked_Object {
public:
  //! Default constructor.
  Doubly_Linked_Object();

  //! Creates a chain element with forward link \p f and backward link \p b.
  Doubly_Linked_Object(Doubly_Linked_Object* f, Doubly_Linked_Object* b);

  //! Inserts \p y before \p *this.
  void insert_before(Doubly_Linked_Object& y);

  //! Inserts \p y after \p *this.
  void insert_after(Doubly_Linked_Object& y);

  //! Erases \p *this from the chain and returns a pointer to the next element.
  Doubly_Linked_Object* erase();

  //! Erases \p *this from the chain.
  ~Doubly_Linked_Object();

private:
  //! Forward link.
  Doubly_Linked_Object* next;

  //! Backward link.
  Doubly_Linked_Object* prev;

  template <typename T> friend class EList;
  template <typename T> friend class EList_Iterator;
};

/* Automatically generated from PPL source file ../src/Doubly_Linked_Object_inlines.hh line 1. */
/* Doubly_Linked_Object class implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

namespace Implementation {

inline
Doubly_Linked_Object::Doubly_Linked_Object() {
}

inline
Doubly_Linked_Object::Doubly_Linked_Object(Doubly_Linked_Object* f,
                                           Doubly_Linked_Object* b)
  : next(f),
    prev(b) {
}

inline void
Doubly_Linked_Object::insert_before(Doubly_Linked_Object& y) {
  y.next = this;
  y.prev = prev;
  prev->next = &y;
  prev = &y;
}

inline void
Doubly_Linked_Object::insert_after(Doubly_Linked_Object& y) {
  y.next = next;
  y.prev = this;
  next->prev = &y;
  next = &y;
}

inline Doubly_Linked_Object*
Doubly_Linked_Object::erase() {
  next->prev = prev;
  prev->next = next;
  return next;
}

inline
Doubly_Linked_Object::~Doubly_Linked_Object() {
  erase();
}

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Doubly_Linked_Object_defs.hh line 64. */

/* Automatically generated from PPL source file ../src/Pending_Element_defs.hh line 30. */

//! A class for pending watchdog events with embedded links.
/*!
  Each pending watchdog event is characterized by a deadline (a positive
  time interval), an associated handler that will be invoked upon event
  expiration, and a Boolean flag that indicates whether the event has already
  expired or not.
*/
template <typename Threshold>
class Parma_Polyhedra_Library::Implementation::Watchdog::Pending_Element
  : public Doubly_Linked_Object {
public:
  //! Constructs an element with the given attributes.
  Pending_Element(const Threshold& deadline,
                  const Handler& handler,
                  bool& expired_flag);

  //! Modifies \p *this so that it has the given attributes.
  void assign(const Threshold& deadline,
              const Handler& handler,
              bool& expired_flag);

  //! Returns the deadline of the event.
  const Threshold& deadline() const;

  //! Returns the handler associated to the event.
  const Handler& handler() const;

  //! Returns a reference to the "event-expired" flag.
  bool& expired_flag() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;

private:
  //! The deadline of the event.
  Threshold d;

  //! A pointer to the handler associated to the event.
  const Handler* p_h;

  //! A pointer to a flag saying whether the event has already expired or not.
  bool* p_f;
};

/* Automatically generated from PPL source file ../src/Pending_Element_inlines.hh line 1. */
/* Pending_Element class implementation: inline functions.
*/


#include <cassert>

namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Watchdog {

template <typename Threshold>
inline bool
Pending_Element<Threshold>::OK() const {
  return true;
}

template <typename Threshold>
inline
Pending_Element<Threshold>::Pending_Element(const Threshold& deadline,
                                            const Handler& handler,
                                            bool& expired_flag)
  : d(deadline), p_h(&handler), p_f(&expired_flag) {
  assert(OK());
}

template <typename Threshold>
inline void
Pending_Element<Threshold>::assign(const Threshold& deadline,
                                   const Handler& handler,
                                   bool& expired_flag) {
  d = deadline;
  p_h = &handler;
  p_f = &expired_flag;
  assert(OK());
}

template <typename Threshold>
inline const Threshold&
Pending_Element<Threshold>::deadline() const {
  return d;
}

template <typename Threshold>
inline const Handler&
Pending_Element<Threshold>::handler() const {
  return *p_h;
}

template <typename Threshold>
inline bool&
Pending_Element<Threshold>::expired_flag() const {
  return *p_f;
}

} // namespace Watchdog

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Pending_Element_defs.hh line 76. */

/* Automatically generated from PPL source file ../src/EList_defs.hh line 1. */
/* EList class declaration.
*/


/* Automatically generated from PPL source file ../src/EList_Iterator_defs.hh line 1. */
/* EList_Iterator class declaration.
*/


/* Automatically generated from PPL source file ../src/EList_Iterator_defs.hh line 29. */

namespace Parma_Polyhedra_Library {

namespace Implementation {

//! Returns <CODE>true</CODE> if and only if \p x and \p y are equal.
template <typename T>
bool operator==(const EList_Iterator<T>& x, const EList_Iterator<T>& y);

//! Returns <CODE>true</CODE> if and only if \p x and \p y are different.
template <typename T>
bool operator!=(const EList_Iterator<T>& x, const EList_Iterator<T>& y);

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

//! A class providing iterators for embedded lists.
template <typename T>
class Parma_Polyhedra_Library::Implementation::EList_Iterator {
public:
  //! Constructs an iterator pointing to nothing.
  EList_Iterator();

  //! Constructs an iterator pointing to \p p.
  explicit EList_Iterator(Doubly_Linked_Object* p);

  //! Changes \p *this so that it points to \p p.
  EList_Iterator& operator=(Doubly_Linked_Object* p);

  //! Indirect member selector.
  T* operator->();

  //! Dereference operator.
  T& operator*();

  //! Preincrement operator.
  EList_Iterator& operator++();

  //! Postincrement operator.
  EList_Iterator operator++(int);

  //! Predecrement operator.
  EList_Iterator& operator--();

  //! Postdecrement operator.
  EList_Iterator operator--(int);

private:
  //! Embedded pointer.
  Doubly_Linked_Object* ptr;

  friend bool operator==<T>(const EList_Iterator& x, const EList_Iterator& y);

  friend bool operator!=<T>(const EList_Iterator& x, const EList_Iterator& y);
};

/* Automatically generated from PPL source file ../src/EList_Iterator_inlines.hh line 1. */
/* EList_Iterator class implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/EList_Iterator_inlines.hh line 28. */

namespace Parma_Polyhedra_Library {

namespace Implementation {

template <typename T>
inline
EList_Iterator<T>::EList_Iterator() {
}

template <typename T>
inline
EList_Iterator<T>::EList_Iterator(Doubly_Linked_Object* p)
  : ptr(p) {
}

template <typename T>
inline EList_Iterator<T>&
EList_Iterator<T>::operator=(Doubly_Linked_Object* p) {
  ptr = p;
  return *this;
}

template <typename T>
inline T*
EList_Iterator<T>::operator->() {
  return static_cast<T*>(ptr);
}

template <typename T>
inline T&
EList_Iterator<T>::operator*() {
  return *operator->();
}

template <typename T>
inline EList_Iterator<T>&
EList_Iterator<T>::operator++() {
  ptr = ptr->next;
  return *this;
}

template <typename T>
inline EList_Iterator<T>
EList_Iterator<T>::operator++(int) {
  EList_Iterator tmp = *this;
  ++*this;
  return tmp;
}

template <typename T>
inline EList_Iterator<T>&
EList_Iterator<T>::operator--() {
  ptr = ptr->prev;
  return *this;
}

template <typename T>
inline EList_Iterator<T>
EList_Iterator<T>::operator--(int) {
  EList_Iterator tmp = *this;
  --*this;
  return tmp;
}

template <typename T>
inline bool
operator==(const EList_Iterator<T>& x, const EList_Iterator<T>& y) {
  return x.ptr == y.ptr;
}

template <typename T>
inline bool
operator!=(const EList_Iterator<T>& x, const EList_Iterator<T>& y) {
  return x.ptr != y.ptr;
}

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/EList_Iterator_defs.hh line 87. */

/* Automatically generated from PPL source file ../src/EList_defs.hh line 30. */

/*! \brief
  A simple kind of embedded list (i.e., a doubly linked objects
  where the links are embedded in the objects themselves).
*/
template <typename T>
class Parma_Polyhedra_Library::Implementation::EList
  : private Doubly_Linked_Object {
public:
  //! A const iterator to traverse the list.
  typedef EList_Iterator<const T> const_iterator;

  //! A non-const iterator to traverse the list.
  typedef EList_Iterator<T> iterator;

  //! Constructs an empty list.
  EList();

  //! Destructs the list and all the elements in it.
  ~EList();

  //! Pushes \p obj to the front of the list.
  void push_front(T& obj);

  //! Pushes \p obj to the back of the list.
  void push_back(T& obj);

  /*! \brief
    Inserts \p obj just before \p position and returns an iterator
    that points to the inserted object.
  */
  iterator insert(iterator position, T& obj);

  /*! \brief
    Removes the element pointed to by \p position, returning
    an iterator pointing to the next element, if any, or end(), otherwise.
  */
  iterator erase(iterator position);

  //! Returns <CODE>true</CODE> if and only if the list is empty.
  bool empty() const;

  //! Returns an iterator pointing to the beginning of the list.
  iterator begin();

  //! Returns an iterator pointing one past the last element in the list.
  iterator end();

  //! Returns a const iterator pointing to the beginning of the list.
  const_iterator begin() const;

  //! Returns a const iterator pointing one past the last element in the list.
  const_iterator end() const;

  //! Checks if all the invariants are satisfied.
  bool OK() const;
};

/* Automatically generated from PPL source file ../src/EList_inlines.hh line 1. */
/* EList class implementation: inline functions.
*/


#include <cassert>

namespace Parma_Polyhedra_Library {

namespace Implementation {

template <typename T>
inline
EList<T>::EList()
  : Doubly_Linked_Object(this, this) {
}

template <typename T>
inline void
EList<T>::push_front(T& obj) {
  next->insert_before(obj);
}

template <typename T>
inline void
EList<T>::push_back(T& obj) {
  prev->insert_after(obj);
}

template <typename T>
inline typename EList<T>::iterator
EList<T>::insert(iterator position, T& obj) {
  position->insert_before(obj);
  return iterator(&obj);
}

template <typename T>
inline typename EList<T>::iterator
EList<T>::begin() {
  return iterator(next);
}

template <typename T>
inline typename EList<T>::iterator
EList<T>::end() {
  return iterator(this);
}

template <typename T>
inline typename EList<T>::const_iterator
EList<T>::begin() const {
  return const_iterator(next);
}

template <typename T>
inline typename EList<T>::const_iterator
EList<T>::end() const {
  return const_iterator(const_cast<EList<T>*>(this));
}

template <typename T>
inline bool
EList<T>::empty() const {
  return begin() == end();
}

template <typename T>
inline typename EList<T>::iterator
EList<T>::erase(iterator position) {
  assert(!empty());
  return iterator(position->erase());
}

template <typename T>
inline
EList<T>::~EList() {
  // Erase and deallocate all the elements.
  for (iterator i = begin(), lend = end(), next; i != lend; i = next) {
    next = erase(i);
    delete &*i;
  }
}

template <typename T>
inline bool
EList<T>::OK() const {
  for (const_iterator i = begin(), lend = end(); i != lend; ++i) {
    if (!i->OK()) {
      return false;
    }
  }

  return true;
}

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/EList_defs.hh line 89. */

/* Automatically generated from PPL source file ../src/Pending_List_defs.hh line 31. */

//! An ordered list for recording pending watchdog events.
template <typename Traits>
class Parma_Polyhedra_Library::Implementation::Watchdog::Pending_List {
public:
  //! A non-const iterator to traverse the list.
  typedef typename EList<Pending_Element<typename Traits::Threshold> >::iterator iterator;

  //! A const iterator to traverse the list.
  typedef typename EList<Pending_Element<typename Traits::Threshold> >::const_iterator const_iterator;

  //! Constructs an empty list.
  Pending_List();

  //! Destructor.
  ~Pending_List();

  //! Inserts a new Pending_Element object with the given attributes.
  iterator insert(const typename Traits::Threshold& deadline,
                  const Handler& handler,
                  bool& expired_flag);

  /*! \brief
    Removes the element pointed to by \p position, returning
    an iterator pointing to the next element, if any, or end(), otherwise.
  */
  iterator erase(iterator position);

  //! Returns <CODE>true</CODE> if and only if the list is empty.
  bool empty() const;

  //! Returns an iterator pointing to the beginning of the list.
  iterator begin();

  //! Returns an iterator pointing one past the last element in the list.
  iterator end();

  //! Checks if all the invariants are satisfied.
  bool OK() const;

private:
  EList<Pending_Element<typename Traits::Threshold> > active_list;
  EList<Pending_Element<typename Traits::Threshold> > free_list;
};

/* Automatically generated from PPL source file ../src/Pending_List_inlines.hh line 1. */
/* Pending_List class implementation: inline functions.
*/


#include <cassert>

namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Watchdog {

template <typename Traits>
inline
Pending_List<Traits>::Pending_List()
  : active_list(),
    free_list() {
  assert(OK());
}

template <typename Traits>
inline
Pending_List<Traits>::~Pending_List() {
}

template <typename Traits>
inline typename Pending_List<Traits>::iterator
Pending_List<Traits>::begin() {
  return active_list.begin();
}

template <typename Traits>
inline typename Pending_List<Traits>::iterator
Pending_List<Traits>::end() {
  return active_list.end();
}

template <typename Traits>
inline bool
Pending_List<Traits>::empty() const {
  return active_list.empty();
}

template <typename Traits>
inline typename Pending_List<Traits>::iterator
Pending_List<Traits>::erase(iterator position) {
  assert(!empty());
  iterator next = active_list.erase(position);
  free_list.push_back(*position);
  assert(OK());
  return next;
}

} // namespace Watchdog

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Pending_List_templates.hh line 1. */
/* Pending_List class implementation.
*/


#include <iostream>

namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Watchdog {

template <typename Traits>
typename Pending_List<Traits>::iterator
Pending_List<Traits>::insert(const typename Traits::Threshold& deadline,
                             const Handler& handler,
                             bool& expired_flag) {
  iterator position = active_list.begin();
  for (iterator active_list_end = active_list.end();
       position != active_list_end
         && Traits::less_than(position->deadline(), deadline);
       ++position) {
  }
  iterator pending_element_p;
  // Only allocate a new element if the free list is empty.
  if (free_list.empty()) {
    pending_element_p
      = new Pending_Element<typename Traits::Threshold>(deadline,
                                                        handler,
                                                        expired_flag);
  }
  else {
    pending_element_p = free_list.begin();
    free_list.erase(pending_element_p);
    pending_element_p->assign(deadline, handler, expired_flag);
  }
  iterator r = active_list.insert(position, *pending_element_p);
  assert(OK());
  return r;
}

template <typename Traits>
bool
Pending_List<Traits>::OK() const {
  if (!active_list.OK()) {
    return false;
  }
  if (!free_list.OK()) {
    return false;
  }

  const typename Traits::Threshold* old;
  const_iterator i = active_list.begin();
  old = &i->deadline();
  ++i;
  for (const_iterator active_list_end = active_list.end(); i != active_list_end; ++i) {
    const typename Traits::Threshold& t = i->deadline();
    if (Traits::less_than(t, *old)) {
#ifndef NDEBUG
      std::cerr << "The active list is not sorted!"
                << std::endl;
#endif
      return false;
    }
    old = &t;
  }
  return true;
}

} // namespace Watchdog

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Pending_List_defs.hh line 78. */

/* Automatically generated from PPL source file ../src/Watchdog_defs.hh line 31. */
#include <cassert>
#include <functional>

#ifdef PPL_HAVE_SYS_TIME_H
# include <sys/time.h>
#endif

namespace Parma_Polyhedra_Library {

// Set linkage now to declare it friend later.
extern "C" void PPL_handle_timeout(int signum);

struct Watchdog_Traits {
  typedef Implementation::Watchdog::Time Threshold;
  static bool less_than(const Threshold& a, const Threshold& b) {
    return a < b;
  }
};

//! A watchdog timer.
class Watchdog {
public:
  template <typename Flag_Base, typename Flag>
  Watchdog(long csecs, const Flag_Base* volatile& holder, Flag& flag);

  /*! \brief
    Constructor: if not reset, the watchdog will trigger after \p csecs
    centiseconds, invoking handler \p function.
  */
  Watchdog(long csecs, void (* const function)());

  //! Destructor.
  ~Watchdog();

#if PPL_HAVE_DECL_SETITIMER && PPL_HAVE_DECL_SIGACTION

  //! Static class initialization.
  static void initialize();
  //! Static class finalization.
  static void finalize();

private:
  //! Whether or not this watchdog has expired.
  bool expired;

  typedef Implementation::Watchdog::Pending_List<Watchdog_Traits>
  WD_Pending_List;

  typedef Implementation::Watchdog::Handler
  WD_Handler;

  const WD_Handler& handler;
  WD_Pending_List::iterator pending_position;

  // Private and not implemented: copy construction is not allowed.
  Watchdog(const Watchdog&);
  // Private and not implemented: copy assignment is not allowed.
  Watchdog& operator=(const Watchdog&);

  // Pass this to getitimer().
  static itimerval current_timer_status;

  //! Reads the timer value into \p time.
  static void get_timer(Implementation::Watchdog::Time& time);

  // Pass this to setitimer().
  static itimerval signal_once;

  // Last time value we set the timer to.
  static Implementation::Watchdog::Time last_time_requested;

  //! Sets the timer value to \p time.
  static void set_timer(const Implementation::Watchdog::Time& time);

  //! Stops the timer.
  static void stop_timer();

  //! Quick reschedule to avoid race conditions.
  static void reschedule();

  // Used by the above.
  static Implementation::Watchdog::Time reschedule_time;

  // Records the time elapsed since last fresh start.
  static Implementation::Watchdog::Time time_so_far;

  //! The ordered queue of pending watchdog events.
  static WD_Pending_List pending;

  //! The actual signal handler.
  static void handle_timeout(int);

  //! Handles the addition of a new watchdog event.
  static WD_Pending_List::iterator
  new_watchdog_event(long csecs,
                     const WD_Handler& handler,
                     bool& expired_flag);

  //! Handles the removal of the watchdog event referred by \p position.
  void remove_watchdog_event(WD_Pending_List::iterator position);

  //! Whether the alarm clock is running.
  static volatile bool alarm_clock_running;

  //! Whether we are changing data that is also changed by the signal handler.
  static volatile bool in_critical_section;

  friend void PPL_handle_timeout(int signum);

#endif // PPL_HAVE_DECL_SETITIMER && PPL_HAVE_DECL_SIGACTION
};

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Watchdog_inlines.hh line 1. */
/* Watchdog and associated classes' implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Handler_defs.hh line 1. */
/* Handler and derived classes' declaration.
*/


/* Automatically generated from PPL source file ../src/Handler_defs.hh line 28. */

//! Abstract base class for handlers of the watchdog events.
class Parma_Polyhedra_Library::Implementation::Watchdog::Handler {
public:
  //! Does the job.
  virtual void act() const = 0;

  //! Virtual destructor.
  virtual ~Handler();
};

//! A kind of Handler that installs a flag onto a flag-holder.
/*!
  The template class <CODE>Handler_Flag\<Flag_Base, Flag\></CODE>
  is an handler whose job is to install a flag onto an <EM>holder</EM>
  for the flag.
  The flag is of type \p Flag and the holder is a (volatile) pointer
  to \p Flag_Base.  Installing the flag onto the holder means making
  the holder point to the flag, so that it must be possible to assign
  a value of type <CODE>Flag*</CODE> to an entity of type
  <CODE>Flag_Base*</CODE>.
  The class \p Flag must provide the method

  \code
    int priority() const
  \endcode
  returning an integer priority associated to the flag.

  The handler will install its flag onto the holder only if the holder
  is empty, namely, it is the null pointer, or if the holder holds a
  flag of strictly lower priority.
 */
template <typename Flag_Base, typename Flag>
class Parma_Polyhedra_Library::Implementation::Watchdog::Handler_Flag
  : public Handler {
public:
  //! Constructor with a given function.
  Handler_Flag(const Flag_Base* volatile& holder, Flag& flag);

  /*! \brief
    Does its job: installs the flag onto the holder, if a flag with
    an higher priority has not already been installed.
  */
  virtual void act() const;

private:
  // Declare holder as reference to volatile pointer to const Flag_Base.
  const Flag_Base* volatile& h;
  Flag& f;
};

//! A kind of Handler calling a given function.
class Parma_Polyhedra_Library::Implementation::Watchdog::Handler_Function
  : public Handler {
public:
  //! Constructor with a given function.
  Handler_Function(void (* const function)());

  //! Does its job: calls the embedded function.
  virtual void act() const;

private:
  //! Pointer to the embedded function.
  void (* const f)();
};

/* Automatically generated from PPL source file ../src/Handler_inlines.hh line 1. */
/* Handler and derived classes' implementation: inline functions.
*/


namespace Parma_Polyhedra_Library {

namespace Implementation {

namespace Watchdog {

template <typename Flag_Base, typename Flag>
Handler_Flag<Flag_Base, Flag>::Handler_Flag(const Flag_Base* volatile& holder,
                                            Flag& flag)
  : h(holder), f(flag) {
}

template <typename Flag_Base, typename Flag>
void
Handler_Flag<Flag_Base, Flag>::act() const {
  if (h == 0 || static_cast<const Flag&>(*h).priority() < f.priority()) {
    h = &f;
  }
}

inline
Handler_Function::Handler_Function(void (* const function)())
  : f(function) {
}

} // namespace Watchdog

} // namespace Implementation

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Handler_defs.hh line 95. */

/* Automatically generated from PPL source file ../src/Watchdog_inlines.hh line 28. */
#include <stdexcept>

namespace Parma_Polyhedra_Library {

#if PPL_HAVE_DECL_SETITIMER && PPL_HAVE_DECL_SIGACTION

template <typename Flag_Base, typename Flag>
Watchdog::Watchdog(long csecs,
                   const Flag_Base* volatile& holder,
                   Flag& flag)
  : expired(false),
    handler(*new
            Implementation::Watchdog::Handler_Flag<Flag_Base, Flag>(holder,
                                                                    flag)) {
  if (csecs == 0) {
    throw std::invalid_argument("Watchdog constructor called with a"
                                " non-positive number of centiseconds");
  }
  in_critical_section = true;
  pending_position = new_watchdog_event(csecs, handler, expired);
  in_critical_section = false;
}

inline
Watchdog::Watchdog(long csecs, void (* const function)())
  : expired(false),
    handler(*new Implementation::Watchdog::Handler_Function(function)) {
  if (csecs == 0) {
    throw std::invalid_argument("Watchdog constructor called with a"
                                " non-positive number of centiseconds");
  }
  in_critical_section = true;
  pending_position = new_watchdog_event(csecs, handler, expired);
  in_critical_section = false;
}

inline
Watchdog::~Watchdog() {
  if (!expired) {
    in_critical_section = true;
    remove_watchdog_event(pending_position);
    in_critical_section = false;
  }
  delete &handler;
}

inline void
Watchdog::reschedule() {
  set_timer(reschedule_time);
}

#else // !PPL_HAVE_DECL_SETITIMER !! !PPL_HAVE_DECL_SIGACTION

template <typename Flag_Base, typename Flag>
Watchdog::Watchdog(long /* csecs */,
                   const Flag_Base* volatile& /* holder */,
                   Flag& /* flag */) {
  throw std::logic_error("PPL::Watchdog::Watchdog objects not supported:"
                         " system does not provide setitimer()");
}

inline
Watchdog::Watchdog(long /* csecs */, void (* /* function */)()) {
  throw std::logic_error("PPL::Watchdog::Watchdog objects not supported:"
                         " system does not provide setitimer()");
}

inline
Watchdog::~Watchdog() {
}

#endif // !PPL_HAVE_DECL_SETITIMER !! !PPL_HAVE_DECL_SIGACTION

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Watchdog_defs.hh line 146. */


/* Automatically generated from PPL source file ../src/Threshold_Watcher_defs.hh line 1. */
/* Threshold_Watcher and associated classes' declaration and inline functions.
*/


/* Automatically generated from PPL source file ../src/Threshold_Watcher_types.hh line 1. */


namespace Parma_Polyhedra_Library {

template <typename Traits>
class Threshold_Watcher;

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Threshold_Watcher_defs.hh line 30. */
#include <cassert>

/*! \brief
  A class of watchdogs controlling the exceeding of a threshold.

  \tparam Traits
  A class to set data types and functions for the threshold handling.
  See \c Parma_Polyhedra_Library::Weightwatch_Traits for an example.
*/
template <typename Traits>
class Parma_Polyhedra_Library::Threshold_Watcher {
public:
  template <typename Flag_Base, typename Flag>
  Threshold_Watcher(const typename Traits::Delta& delta,
                    const Flag_Base* volatile& holder,
                    Flag& flag);

  Threshold_Watcher(const typename Traits::Delta& delta,
                    void (*function)());

  ~Threshold_Watcher();

private:
  typedef Implementation::Watchdog::Pending_List<Traits> TW_Pending_List;
  typedef Implementation::Watchdog::Handler TW_Handler;

  bool expired;
  const TW_Handler& handler;
  typename TW_Pending_List::iterator pending_position;

  // Just to prevent their use.
  Threshold_Watcher(const Threshold_Watcher&);
  Threshold_Watcher& operator=(const Threshold_Watcher&);

  struct Initialize {
    //! The ordered queue of pending thresholds.
    TW_Pending_List pending;
  };
  static Initialize init;

  // Handle the addition of a new threshold.
  static typename TW_Pending_List::iterator
  add_threshold(typename Traits::Threshold threshold,
                const TW_Handler& handler,
                bool& expired_flag);

  // Handle the removal of a threshold.
  static typename TW_Pending_List::iterator
  remove_threshold(typename TW_Pending_List::iterator position);

  //! Check threshold reaching.
  static void check();

}; // class Parma_Polyhedra_Library::Threshold_Watcher


// Templatic initialization of static data member.
template <typename Traits>
typename
Parma_Polyhedra_Library::Threshold_Watcher<Traits>::Initialize
Parma_Polyhedra_Library::Threshold_Watcher<Traits>::init;

/* Automatically generated from PPL source file ../src/Threshold_Watcher_inlines.hh line 1. */
/* Threshold_Watcher and associated classes' implementation: inline functions.
*/


/* Automatically generated from PPL source file ../src/Threshold_Watcher_inlines.hh line 28. */
#include <stdexcept>

namespace Parma_Polyhedra_Library {

template <typename Traits>
template <typename Flag_Base, typename Flag>
Threshold_Watcher<Traits>
::Threshold_Watcher(const typename Traits::Delta& delta,
                    const Flag_Base* volatile& holder,
                    Flag& flag)
  : expired(false),
    handler(*new
            Implementation::Watchdog::Handler_Flag<Flag_Base, Flag>(holder,
                                                                    flag)) {
  typename Traits::Threshold threshold;
  Traits::from_delta(threshold, delta);
  if (!Traits::less_than(Traits::get(), threshold)) {
    throw std::invalid_argument("Threshold_Watcher constructor called with a"
                                " threshold already reached");
  }
  pending_position = add_threshold(threshold, handler, expired);
}

template <typename Traits>
inline
Threshold_Watcher<Traits>
::Threshold_Watcher(const typename Traits::Delta& delta, void (*function)())
  : expired(false),
    handler(*new Implementation::Watchdog::Handler_Function(function)) {
  typename Traits::Threshold threshold;
  Traits::from_delta(threshold, delta);
  if (!Traits::less_than(Traits::get(), threshold)) {
    throw std::invalid_argument("Threshold_Watcher constructor called with a"
                                " threshold already reached");
  }
  pending_position = add_threshold(threshold, handler, expired);
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Threshold_Watcher_templates.hh line 1. */
/* Threshold_Watcher and associated classes'.
*/


namespace Parma_Polyhedra_Library {

template <typename Traits>
typename Threshold_Watcher<Traits>::TW_Pending_List::iterator
Threshold_Watcher<Traits>::add_threshold(typename Traits::Threshold threshold,
                                         const TW_Handler& handler,
                                         bool& expired_flag) {
  Traits::check_function = Threshold_Watcher::check;
  return init.pending.insert(threshold, handler, expired_flag);
}

template <typename Traits>
typename Threshold_Watcher<Traits>::TW_Pending_List::iterator
Threshold_Watcher<Traits>
::remove_threshold(typename TW_Pending_List::iterator position) {
  typename TW_Pending_List::iterator i = init.pending.erase(position);
  if (init.pending.empty()) {
    Traits::check_function = 0;
  }
  return i;
}

template <typename Traits>
Threshold_Watcher<Traits>::~Threshold_Watcher() {
  if (!expired) {
    remove_threshold(pending_position);
  }
  delete &handler;
}

template <typename Traits>
void
Threshold_Watcher<Traits>::check() {
  typename TW_Pending_List::iterator i = init.pending.begin();
  assert(i != init.pending.end());
  const typename Traits::Threshold& current = Traits::get();
  while (!Traits::less_than(current, i->deadline())) {
    i->handler().act();
    i->expired_flag() = true;
    i = remove_threshold(i);
    if (i == init.pending.end()) {
      break;
    }
  }
}

} // namespace Parma_Polyhedra_Library

/* Automatically generated from PPL source file ../src/Threshold_Watcher_defs.hh line 94. */



//! Defined to 1 if PPL::Watchdog objects are supported, to 0 otherwise.
#define PPL_WATCHDOG_OBJECTS_ARE_SUPPORTED \
  (PPL_HAVE_DECL_SETITIMER && PPL_HAVE_DECL_SIGACTION)

#undef PPL_SPECIALIZE_ABS
#undef PPL_SPECIALIZE_ADD
#undef PPL_SPECIALIZE_ADD_MUL
#undef PPL_SPECIALIZE_ASSIGN
#undef PPL_SPECIALIZE_ASSIGN_SPECIAL
#undef PPL_SPECIALIZE_CEIL
#undef PPL_SPECIALIZE_CLASSIFY
#undef PPL_SPECIALIZE_CMP
#undef PPL_SPECIALIZE_CONSTRUCT
#undef PPL_SPECIALIZE_CONSTRUCT_SPECIAL
#undef PPL_SPECIALIZE_COPY
#undef PPL_SPECIALIZE_DIV
#undef PPL_SPECIALIZE_DIV2EXP
#undef PPL_SPECIALIZE_FLOOR
#undef PPL_SPECIALIZE_FUN1_0_0
#undef PPL_SPECIALIZE_FUN1_0_1
#undef PPL_SPECIALIZE_FUN1_0_2
#undef PPL_SPECIALIZE_FUN1_0_3
#undef PPL_SPECIALIZE_FUN1_1_1
#undef PPL_SPECIALIZE_FUN1_1_2
#undef PPL_SPECIALIZE_FUN1_2_2
#undef PPL_SPECIALIZE_FUN2_0_0
#undef PPL_SPECIALIZE_FUN2_0_1
#undef PPL_SPECIALIZE_FUN2_0_2
#undef PPL_SPECIALIZE_FUN3_0_1
#undef PPL_SPECIALIZE_FUN5_0_1
#undef PPL_SPECIALIZE_GCD
#undef PPL_SPECIALIZE_GCDEXT
#undef PPL_SPECIALIZE_IDIV
#undef PPL_SPECIALIZE_INPUT
#undef PPL_SPECIALIZE_IS_INT
#undef PPL_SPECIALIZE_IS_MINF
#undef PPL_SPECIALIZE_IS_NAN
#undef PPL_SPECIALIZE_IS_PINF
#undef PPL_SPECIALIZE_LCM
#undef PPL_SPECIALIZE_MUL
#undef PPL_SPECIALIZE_MUL2EXP
#undef PPL_SPECIALIZE_NEG
#undef PPL_SPECIALIZE_OUTPUT
#undef PPL_SPECIALIZE_REM
#undef PPL_SPECIALIZE_SGN
#undef PPL_SPECIALIZE_SQRT
#undef PPL_SPECIALIZE_SUB
#undef PPL_SPECIALIZE_SUB_MUL
#undef PPL_SPECIALIZE_TRUNC

#undef PPL_COMPILE_TIME_CHECK
#undef PPL_COMPILE_TIME_CHECK_AUX
#undef PPL_COMPILE_TIME_CHECK_NAME

#ifdef __STDC_LIMIT_MACROS
# undef __STDC_LIMIT_MACROS
#endif
#ifdef PPL_SAVE_STDC_LIMIT_MACROS
# define __STDC_LIMIT_MACROS PPL_SAVE_STDC_LIMIT_MACROS
# undef PPL_SAVE_STDC_LIMIT_MACROS
#endif

#ifdef PPL_SAVE_NDEBUG
# ifndef NDEBUG
#  define NDEBUG PPL_SAVE_NDEBUG
# endif
# undef PPL_SAVE_NDEBUG
#else
# ifdef NDEBUG
#  undef NDEBUG
# endif
#endif
// Must include <cassert> again in order to make the latest changes to
// NDEBUG effective.
#include <cassert>

#ifdef PPL_NO_AUTOMATIC_INITIALIZATION
 #undef PPL_NO_AUTOMATIC_INITIALIZATION
#endif

#endif // !defined(PPL_ppl_hh)
