/* This is the header file of the Parma Polyhedra Library. Copyright (C) 2001-2010 Roberto Bagnara Copyright (C) 2010-2011 BUGSENG srl (http://bugseng.com) This file is part of the Parma Polyhedra Library (PPL). The PPL is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. The PPL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA. For the most up-to-date information see the Parma Polyhedra Library site: http://www.cs.unipr.it/ppl/ . */ #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 */ /* 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 */ /* 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 `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 header file. */ #define PPL_HAVE_DLFCN_H 1 /* Define to 1 if you have the header file. */ #define PPL_HAVE_FENV_H 1 /* Define to 1 if you have the header file. */ #define PPL_HAVE_GETOPT_H 1 /* Define to 1 if you have the header file. */ /* #undef PPL_HAVE_GLPK_GLPK_H */ /* Define to 1 if you have the header file. */ #define PPL_HAVE_GLPK_H 1 /* Define to 1 if you have the header file. */ /* #undef PPL_HAVE_IEEEFP_H */ /* Define to 1 if you have the 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 you have the header file. */ #define PPL_HAVE_MEMORY_H 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 header file. */ #define PPL_HAVE_SIGNAL_H 1 /* Define to 1 if you have the header file. */ #define PPL_HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define PPL_HAVE_STDLIB_H 1 /* Define to 1 if you have the header file. */ #define PPL_HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define PPL_HAVE_STRING_H 1 /* Define to 1 if you have the header file. */ #define PPL_HAVE_SYS_RESOURCE_H 1 /* Define to 1 if you have the header file. */ #define PPL_HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define PPL_HAVE_SYS_TIME_H 1 /* Define to 1 if you have the 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 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 in which 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 0.11.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 "0.11.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 " '--with-java=/usr/lib/jvm/java-6-openjdk'" /* The unique code of the binary format of C++ doubles, if supported; undefined otherwise. */ #define PPL_CXX_DOUBLE_BINARY_FORMAT PPL_FLOAT_IEEE754_DOUBLE /* Not zero if C++ supports exact output for doubles. */ #define PPL_CXX_DOUBLE_EXACT_OUTPUT 1 /* The binary format of C++ floats, if supported; undefined otherwise. */ #define PPL_CXX_FLOAT_BINARY_FORMAT PPL_FLOAT_IEEE754_SINGLE /* Not zero if C++ supports exact output for floats. */ #define PPL_CXX_FLOAT_EXACT_OUTPUT 1 /* Not zero if the C++ compiler has the remainder bug. */ #define PPL_CXX_HAS_REMAINDER_BUG 1 /* 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 C++ supports exact output for long doubles. */ #define PPL_CXX_LONG_DOUBLE_EXACT_OUTPUT 1 /* 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 C++ compiler supports flexible arrays. */ #define PPL_CXX_SUPPORTS_FLEXIBLE_ARRAYS 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 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 /* Defined if the Parma Watchdog Library is enabled. */ #define PPL_WATCHDOG_LIBRARY_ENABLED 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 you have the ANSI C header files. */ #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 is included before is (even indirectly) included. Moreover we need to define __STDC_LIMIT_MACROS before the first inclusion of 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 #endif #ifdef PPL_HAVE_INTTYPES_H # include #endif /* 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 0 //! The minor number of the PPL version. /*! \ingroup PPL_CXX_interface */ #define PPL_VERSION_MINOR 11 //! The revision number of the PPL version. /*! \ingroup PPL_CXX_interface */ #define PPL_VERSION_REVISION 2 /*! \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 M and m denote the numbers associated to PPL_VERSION_MAJOR and PPL_VERSION_MINOR, respectively. The format of PPL_VERSION is M "." m if both PPL_VERSION_REVISION (r) and PPL_VERSION_BETA (b)are zero, M "." m "pre" b if PPL_VERSION_REVISION is zero and PPL_VERSION_BETA is not zero, M "." m "." r if PPL_VERSION_REVISION is not zero and PPL_VERSION_BETA is zero, M "." m "." r "pre" b if neither PPL_VERSION_REVISION nor PPL_VERSION_BETA are zero. */ #define PPL_VERSION "0.11.2" namespace Parma_Polyhedra_Library { //! 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(); } // 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(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 function templates swap() and iter_swap() (25.2.2, [lib.alg.swap]), and for the class template numeric_limits (18.2.1, [lib.limits]). \note The PPL provides the specializations of the class template numeric_limits not only for PPL-specific numeric types, but also for the GMP types mpz_class and mpq_class. 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 class Interval_Info_Null; template class Interval_Info_Bitset; } // namespace Parma_Polyhedra_Library /* 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 #include class Parma_Polyhedra_Library::stdiobuf : public std::basic_streambuf > { 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 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 ungetc_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), ungetc_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 class Parma_Polyhedra_Library::c_streambuf : public std::basic_streambuf > { 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. */ 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. */ int_type uflow(); /*! \brief Gets a sequence of characters. \remarks Specified by ISO/IEC 14882:1998: 27.5.2.4.3. */ 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. */ 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. */ 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. */ int_type overflow(int_type c); /*! \brief Synchronizes the stream buffer. Specified by ISO/IEC 14882:1998: 27.5.2.4.2. */ int sync(); private: //! Character type of the streambuf. typedef char char_type; //! Traits type of the streambuf. typedef std::char_traits traits_type; //! Integer type of the streambuf. typedef traits_type::int_type int_type; //! Buffer for the last character read. int_type ungetc_buf; //! Buffer for next character int_type nextc_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() : ungetc_buf(traits_type::eof()), nextc_buf(traits_type::eof()) { } inline c_streambuf::~c_streambuf() { } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/c_streambuf.defs.hh line 125. */ /* 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.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Init; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/fpu.types.hh line 1. */ #ifdef PPL_HAVE_IEEEFP_H #include #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/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. 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 Nifty Counter 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: //! 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/fpu.defs.hh line 1. */ /* Floating point unit related functions. */ /* Automatically generated from PPL source file ../src/compiler.hh line 1. */ /* C++ compiler related stuff. */ 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 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 inline void cc_flush(const T& x) { #if defined(__GNUC__) || defined(__INTEL_COMPILER) __asm__ __volatile__ ("" : "+m" (const_cast(x))); #else // FIXME: is it possible to achieve the same effect in a portable way? 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 struct Suppress_Uninitialized_Warnings_Type { typedef T synonym; }; #define PPL_UNINITIALIZED(type, name) \ type name = Suppress_Uninitialized_Warnings_Type::synonym () #else #define PPL_UNINITIALIZED(type, name) \ type name #endif } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/fpu.defs.hh line 29. */ namespace Parma_Polyhedra_Library { //! 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 inexact computation 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 inexact computation status. void fpu_reset_inexact(); /*! \brief Queries the inexact computation 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(); } // 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 #include #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(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(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_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(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 82. */ #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 #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(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(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 87. */ #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 #include #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_PPL_FPU_TOWARDZERO FE_TOWARDZERO #endif namespace Parma_Polyhedra_Library { inline void fpu_initialize_control_functions() { 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(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) { fpu_rounding_control_word_type old = static_cast(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; #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 89. */ #else #error "PPL_CAN_CONTROL_FPU evaluates to true, but 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 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 96. */ #endif // !PPL_CAN_CONTROL_FPU /* 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 = 0 << 4, //! \hideinitializer Negative infinity result class. VC_MINUS_INFINITY = 1 << 4, //! \hideinitializer Positive infinity result class. VC_PLUS_INFINITY = 2 << 4, //! \hideinitializer Not a number result class. VC_NAN = 3 << 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 = 0, //! \hideinitializer Equal. This need to be accompanied by a value. VR_EQ = 1, //! \hideinitializer Less than. This need to be accompanied by a value. VR_LT = 2, //! \hideinitializer Greater than. This need to be accompanied by a value. VR_GT = 4, //! \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 = VR_EQ, //! \hideinitializer The computed result is inexact and rounded up. V_LT = VR_LT, //! \hideinitializer The computed result is inexact and rounded down. V_GT = 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 = 1 << 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 | VC_PLUS_INFINITY, //! \hideinitializer A negative integer overflow occurred (rounding down). V_GT_MINUS_INFINITY = V_GT | VC_MINUS_INFINITY, //! \hideinitializer Negative infinity result. V_EQ_MINUS_INFINITY = V_EQ | VC_MINUS_INFINITY, //! \hideinitializer Positive infinity result. V_EQ_PLUS_INFINITY = V_EQ | VC_PLUS_INFINITY, //! \hideinitializer Not a number result. V_NAN = VC_NAN, //! \hideinitializer Converting from unknown string. V_CVT_STR_UNK = V_NAN | (1 << 8), //! \hideinitializer Dividing by zero. V_DIV_ZERO = V_NAN | (2 << 8), //! \hideinitializer Adding two infinities having opposite signs. V_INF_ADD_INF = V_NAN | (3 << 8), //! \hideinitializer Dividing two infinities. V_INF_DIV_INF = V_NAN | (4 << 8), //! \hideinitializer Taking the modulus of an infinity. V_INF_MOD = V_NAN | (5 << 8), //! \hideinitializer Multiplying an infinity by zero. V_INF_MUL_ZERO = V_NAN | (6 << 8), //! \hideinitializer Subtracting two infinities having the same sign. V_INF_SUB_INF = V_NAN | (7 << 8), //! \hideinitializer Computing a remainder modulo zero. V_MOD_ZERO = V_NAN | (8 << 8), //! \hideinitializer Taking the square root of a negative number. V_SQRT_NEG = V_NAN | (9 << 8), //! \hideinitializer Unknown result due to intermediate negative overflow. V_UNKNOWN_NEG_OVERFLOW = V_NAN | (10 << 8), //! \hideinitializer Unknown result due to intermediate positive overflow. V_UNKNOWN_POS_OVERFLOW = V_NAN | (11 << 8), //! \hideinitializer The computed result is not representable. V_UNREPRESENTABLE = 1 << 7 }; //! Extracts the value class part of \p r (representable number, unrepresentable minus/plus infinity or nan). Result_Class result_class(Result r); //! Extracts the relation part of \p r. Result_Relation result_relation(Result r); } // 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/assert.hh line 1. */ /* Implementation of PPL_ASSERT macro. */ #include /* Automatically generated from PPL source file ../src/globals.defs.hh line 1. */ /* Declarations of global objects. */ /* Automatically generated from PPL source file ../src/globals.types.hh line 1. */ #include 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 = 1, //! \hideinitializer Less than. LESS_THAN = 2, //! \hideinitializer Less than or equal to. LESS_OR_EQUAL = LESS_THAN | EQUAL, //! \hideinitializer Greater than. GREATER_THAN = 4, //! \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 }; /*! \brief \ingroup PPL_CXX_interface 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 }; /*! \brief \ingroup PPL_CXX_interface 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 }; /*! \brief \ingroup PPL_CXX_interface 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 }; struct Weightwatch_Traits; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/C_Integer.hh line 1. */ /* C integers info. */ /* Automatically generated from PPL source file ../src/meta_programming.hh line 1. */ /* Metaprogramming utilities. */ #include namespace Parma_Polyhedra_Library { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief Declares a per-class constant of type bool, 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 anonymous_enum_ ## name { name = (value) } #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief Declares a per-class constant of type int, 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 { 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 name(). \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 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 name(). \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& name() { \ static type name(value); \ return name; \ } #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief A class that is only defined if \p b evaluates to true. \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 struct Compile_Time_Check; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief A class that is only defined if \p b evaluates to true. \ingroup PPL_CXX_interface This is the specialized case with \p b equal to true, so the class is declared and (trivially) defined. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template <> struct Compile_Time_Check { }; #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 true \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 value that evaluates to \p b. \ingroup PPL_CXX_interface */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template struct Bool { enum anonymous_enum { value = b }; }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief A class holding a constant called value that evaluates to true. \ingroup PPL_CXX_interface */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) struct True : public Bool { }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief A class holding a constant called value that evaluates to false. \ingroup PPL_CXX_interface */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) struct False : public Bool { }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief A class holding a constant called value that evaluates to true 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 struct Is_Same : public False { }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief A class holding a constant called value that evaluates to true 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 struct Is_Same : public True { }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief A class holding a constant called value that evaluates to true 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 struct B; template struct D : public B; \endcode Of course, we cannot test if, for some type variable U, we have Is_Same_Or_Derived, Type>:: anonymous_enum:: value == true. But we can do as follows: \code struct B_Base { }; template struct B : public B_Base; \endcode This enables us to enquire Is_Same_Or_Derived:: anonymous_enum:: value. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template 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 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 anonymous_enum { /*! Assuming sizeof(char) != sizeof(double), the C++ overload resolution mechanism guarantees that value evaluates to true if and only if Base is the same type as Derived or Derived is a class derived from Base. */ value = (sizeof(func(derived_object())) == sizeof(char)) }; }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief A class that provides a type member called type equivalent to \p T if and only if \p b is true. \ingroup PPL_CXX_interface This is the non-specialized case, in which the type member is not present. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template struct Enable_If { }; template struct Enable_If_Is { typedef T type; }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief A class that provides a type member called type equivalent to \p T if and only if \p b is true. \ingroup PPL_CXX_interface This is the specialization in which the type member is present. \note Let T, T1 and T2 be any type expressions and suppose we have some template function T f(T1, T2). 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::type foo(T1 x, T2 y); \endcode For all the instantiations of the template parameters that cause condition to evaluate to false, the Enable_If::type member will not be defined. Hence, for that instantiations, the specialization will not be eligible. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template struct Enable_If { typedef T type; }; template struct Is_Native : public False { }; template <> struct Is_Native : public True { }; template <> struct Is_Native : public True { }; template <> struct Is_Native : public True { }; template <> struct Is_Native : public True { }; template <> struct Is_Native : public True { }; template <> struct Is_Native : public True { }; template <> struct Is_Native : public True { }; template <> struct Is_Native : public True { }; template <> struct Is_Native : public True { }; template <> struct Is_Native : public True { }; template <> struct Is_Native : public True { }; #if PPL_SUPPORTED_FLOAT template <> struct Is_Native : public True { }; #endif #if PPL_SUPPORTED_DOUBLE template <> struct Is_Native : public True { }; #endif #if PPL_SUPPORTED_LONG_DOUBLE template <> struct Is_Native : public True { }; #endif template <> struct Is_Native : public True { }; template <> struct Is_Native : public True { }; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/C_Integer.hh line 28. */ #include // 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 struct C_Integer : public False { }; template <> struct C_Integer : public True { enum anonymous_enum { #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 = CHAR_MIN; static const char max = CHAR_MAX; }; template <> struct C_Integer : public True { enum anonymous_enum { 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 = SCHAR_MIN; static const signed char max = SCHAR_MAX; }; template <> struct C_Integer : public True { enum anonymous_enum { 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 = SHRT_MIN; static const signed short max = SHRT_MAX; }; template <> struct C_Integer : public True { enum anonymous_enum { 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 : public True { enum anonymous_enum { 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 : public True { enum anonymous_enum { 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 : public True { enum anonymous_enum { 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 = 0; static const unsigned char max = UCHAR_MAX; }; template <> struct C_Integer : public True { enum anonymous_enum { 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 = 0; static const unsigned short max = USHRT_MAX; }; template <> struct C_Integer : public True { enum anonymous_enum { 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 = 0; static const unsigned int max = UINT_MAX; }; template <> struct C_Integer : public True { enum anonymous_enum { 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 = 0; static const unsigned long max = ULONG_MAX; }; template <> struct C_Integer : public True { enum anonymous_enum { 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 = 0; static const unsigned long long max = ULLONG_MAX; }; } // 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 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 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 : 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 : public True { }; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Temp.defs.hh line 1. */ /* Temp_* classes declarations. */ /* 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 class Temp_Item { public: //! Obtains a refeence 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; //! Head of the free list. static Temp_Item* free_list_head; //! 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 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& 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 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 handling temporaries with a global free list. #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template struct Temp_From_Free_List { //! The type of the temporaries. typedef T& type; //! The type of the holder. typedef Temp_Reference_Holder holder_type; }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! A structure for handling temporaries with local variables. #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template struct Temp_From_Local_Variable { //! The type of the temporaries. typedef T type; //! The type of the holder. typedef Temp_Value_Holder holder_type; }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! A structure for the efficient handling of temporaries. #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template 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 class Dirty_Temp::value>::type> : public Temp_From_Free_List { }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specialization for the handling of temporaries with local variables. #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template class Dirty_Temp::value>::type> : public Temp_From_Local_Variable { }; } // 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 inline Temp_Item::Temp_Item() : item_() { } template inline T& Temp_Item::item() { return item_; } template inline Temp_Item& Temp_Item::obtain() { if (free_list_head != 0) { Temp_Item* p = free_list_head; free_list_head = free_list_head->next; return *p; } else return *new Temp_Item(); } template inline void Temp_Item::release(Temp_Item& p) { p.next = free_list_head; free_list_head = &p; } template inline Temp_Reference_Holder::Temp_Reference_Holder() : held(Temp_Item::obtain()) { } template inline Temp_Reference_Holder::~Temp_Reference_Holder() { Temp_Item::release(held); } template inline T& Temp_Reference_Holder::item() { return held.item(); } template inline Temp_Value_Holder::Temp_Value_Holder() { } template inline T Temp_Value_Holder::item() { return item_; } } // namespace Parma_Polyhedra_Library #define PPL_DIRTY_TEMP(T, id) \ typename \ Parma_Polyhedra_Library::Dirty_Temp::holder_type holder_ ## id; \ typename \ Parma_Polyhedra_Library::Dirty_Temp::type id = holder_ ## id.item() #define PPL_DIRTY_TEMP0(T, id) \ Parma_Polyhedra_Library::Dirty_Temp::holder_type holder_ ## id; \ Parma_Polyhedra_Library::Dirty_Temp::type 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 Temp_Item* Temp_Item::free_list_head = 0; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Temp.defs.hh line 168. */ /* Automatically generated from PPL source file ../src/globals.defs.hh line 32. */ #include #include 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 /*! \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 inline typename Enable_If::value, void>::type swap(T&, T&) { PPL_COMPILE_TIME_CHECK(!Slow_Copy::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_TEMP0(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); //! Sets \p threshold to be \p delta units bigger than the current weigth. 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 namespace Implementation { //! Non zero during evaluation of PPL_ASSERT expression. extern unsigned int in_assert; } // namespace Implementation #endif #ifndef PPL_PROFILE_ADD_WEIGHT #define PPL_PROFILE_ADD_WEIGHT 0 #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(0) #define WEIGHT_ADD_MUL(delta, factor) \ do { \ static Weight_Profiler wp__(__FILE__, __LINE__, delta); \ wp__.end(factor); \ } while(0) #else #define WEIGHT_BEGIN() #define WEIGHT_ADD(delta) \ do { \ Weightwatch_Traits::weight += delta; \ } while(0) #define WEIGHT_ADD_MUL(delta, factor) \ do { \ Weightwatch_Traits::weight += delta * factor; \ } while(0) #endif #else #define WEIGHT_BEGIN() #define WEIGHT_ADD(delta) \ do { \ if (!Implementation::in_assert) \ Weightwatch_Traits::weight += delta; \ } while(0) #define WEIGHT_ADD_MUL(delta, factor) \ do { \ if (!Implementation::in_assert) \ Weightwatch_Traits::weight += delta * factor; \ } while(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 \ void \ class_prefix::ascii_dump() const { \ ascii_dump(std::cerr); \ } \ \ template \ 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 \ void \ class_prefix::ascii_dump() const { \ ascii_dump(std::cerr); \ } \ \ template \ void \ class_prefix::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 \ void \ class_prefix::ascii_dump() \ const { \ ascii_dump(std::cerr); \ } \ \ template \ void \ class_prefix::print() \ const { \ using IO_Operators::operator<<; \ std::cerr << *this; \ } #define PPL_OUTPUT_TEMPLATE_DEFINITIONS_ASCII_ONLY(type_symbol, class_prefix) \ template \ void \ class_prefix::ascii_dump() const { \ ascii_dump(std::cerr); \ } \ \ template \ void \ class_prefix::print() const { \ std::cerr << "No user level output operator defined " \ << "for " PPL_XSTR(class_prefix) << "." << std::endl; \ } template struct Fit : public False { }; template struct Fit::value>::type> { enum { value = (v >= static_cast(C_Integer::min) && v <= static_cast(C_Integer::max)) }; }; template struct TConstant { static const T value = v; }; template const T TConstant::value; template struct Constant_ : public TConstant { }; template struct Constant_::smaller_signed_type, v>::value && (prefer_signed || !Fit::smaller_unsigned_type, v>::value))>::type> : public Constant_::smaller_signed_type, v, prefer_signed> { }; template struct Constant_::smaller_unsigned_type, v>::value && (!prefer_signed || !Fit::smaller_signed_type, v>::value))>::type> : public Constant_::smaller_unsigned_type, v, prefer_signed> { }; template struct Constant : public Constant_ { }; //! \name Memory Size Inspection Functions //@{ #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 Enable_If::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 Enable_If::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); //@} // Memory Size Inspection Functions template struct Has_OK : public False { }; template struct Has_OK::type> : public True { }; template inline typename Enable_If::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) } // namespace Parma_Polyhedra_Library #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 namespace Parma_Polyhedra_Library { class Weight_Profiler { enum { DISCARDED = 0, VALID = 1 }; public: Weight_Profiler(const char* file, int line, Weightwatch_Traits::Delta delta, double tmin = 0, double tmax = 0) : file(file), line(line), delta(delta), tmin(tmin), tmax(tmax) { for (int i = 0; i < 2; ++i) { stat[i].samples = 0; stat[i].count = 0; stat[i].sum = 0; stat[i].ssum = 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 -= adj; double elapsed1 = elapsed / factor; int i = (elapsed1 < tmin || (tmax > 0 && elapsed1 > tmax)) ? 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].ssum += elapsed * elapsed1; stat[i].count += factor; } static double tune_adj(); private: const char *file; int line; Weightwatch_Traits::Delta delta; double tmin; double tmax; struct { unsigned int samples; unsigned int count; double sum; double ssum; double min; double max; } stat[2]; static struct timespec stamp; static double adj; }; } #endif // Weight_Profiler_defs_hh /* Automatically generated from PPL source file ../src/globals.defs.hh line 471. */ #endif /* Automatically generated from PPL source file ../src/globals.inlines.hh line 1. */ /* Implementation of global objects: inline functions. */ #include /* Automatically generated from PPL source file ../src/globals.inlines.hh line 29. */ namespace Parma_Polyhedra_Library { inline dimension_type not_a_dimension() { return std::numeric_limits::max(); } 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(Threshold)*8-1); } inline void Weightwatch_Traits::from_delta(Threshold& threshold, const Delta& delta) { threshold = weight + delta; } inline Throwable::~Throwable() { } inline void maybe_abandon() { #ifndef NDEBUG if (Implementation::in_assert) return; #endif if (Weightwatch_Traits::check_function) Weightwatch_Traits::check_function(); if (const Throwable* 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 inline typename Enable_If::value, memory_size_type>::type external_memory_in_bytes(const T&) { return 0; } template inline typename Enable_If::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 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); } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/globals.defs.hh line 474. */ /* Automatically generated from PPL source file ../src/assert.hh line 29. */ #if defined(NDEBUG) #define PPL_ASSERT(cond__) #define PPL_ASSERT_HEAVY(cond__) #else // Non zero to detect use of PPL_ASSERT instead of PPL_ASSERT_HEAVY #define PPL_DEBUG_PPL_ASSERT 1 #if !PPL_DEBUG_PPL_ASSERT #define PPL_ASSERT(cond__) assert(cond__) #else #define PPL_ASSERT(cond__) \ do { \ Parma_Polyhedra_Library::Weightwatch_Traits::Threshold \ old_weight__ \ = Parma_Polyhedra_Library::Weightwatch_Traits::weight; \ assert(cond__); \ assert(old_weight__ == Parma_Polyhedra_Library::Weightwatch_Traits::weight && \ "PPL_ASSERT_HEAVY have to be used here"); \ } while(0) #endif // The evaluation of asserted conditions could have a non zero // computational weight (i.e., the execution path could contain an // invocation of WEIGHT_ADD). #define PPL_ASSERT_HEAVY(cond__) \ do { \ ++Parma_Polyhedra_Library::Implementation::in_assert; \ assert(cond__); \ --Parma_Polyhedra_Library::Implementation::in_assert; \ } while (0) #endif /* Automatically generated from PPL source file ../src/Result.inlines.hh line 28. */ namespace Parma_Polyhedra_Library { /*! \relates Parma_Polyhedra_Library::Result */ inline Result_Class result_class(Result r) { return static_cast(r & VC_MASK); } /*! \relates Parma_Polyhedra_Library::Result */ inline Result_Relation result_relation(Result r) { return static_cast(r & VR_MASK); } /*! \relates Parma_Polyhedra_Library::Result */ inline Result result_relation_class(Result r) { return static_cast(r & (VR_MASK | 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); } inline Result operator|(Result a, Result b) { return static_cast((unsigned)a | (unsigned)b); } inline Result operator-(Result a, Result b) { return static_cast((unsigned)a & ~(unsigned)b); } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Result.defs.hh line 172. */ /* 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 = 0, /*! \hideinitializer Round toward \f$+\infty\f$. */ ROUND_UP = 1, /*! \hideinitializer Rounding is delegated to lower level. Result info is evaluated lazily. */ ROUND_IGNORE = 6, 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 = 7, ROUND_DIRECT = ROUND_UP, ROUND_INVERSE = ROUND_DOWN, ROUND_DIR_MASK = 7, /*! \hideinitializer The client code is willing to pay an extra price to know the exact relation beetwen the exact result and the computed one. */ ROUND_STRICT_RELATION = 8, ROUND_CHECK = ROUND_DIRECT | ROUND_STRICT_RELATION }; /*! \brief Returns the inverse rounding mode of \p dir, ROUND_IGNORE being the inverse of itself. */ Rounding_Dir inverse(Rounding_Dir dir); Rounding_Dir round_dir(Rounding_Dir dir); bool round_down(Rounding_Dir dir); bool round_up(Rounding_Dir dir); bool round_ignore(Rounding_Dir dir); bool round_not_needed(Rounding_Dir dir); bool round_not_requested(Rounding_Dir dir); bool round_direct(Rounding_Dir dir); bool round_inverse(Rounding_Dir dir); bool round_strict_relation(Rounding_Dir dir); fpu_rounding_direction_type round_fpu_dir(Rounding_Dir dir); } // 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 { inline Rounding_Dir round_dir(Rounding_Dir dir) { return static_cast(dir & ROUND_DIR_MASK); } inline bool round_down(Rounding_Dir dir) { return round_dir(dir) == ROUND_DOWN; } inline bool round_up(Rounding_Dir dir) { return round_dir(dir) == ROUND_UP; } inline bool round_ignore(Rounding_Dir dir) { return round_dir(dir) == ROUND_IGNORE; } inline bool round_not_needed(Rounding_Dir dir) { return round_dir(dir) == ROUND_NOT_NEEDED; } inline bool round_not_requested(Rounding_Dir dir) { return round_dir(dir) == ROUND_IGNORE || round_dir(dir) == ROUND_NOT_NEEDED; } inline bool round_direct(Rounding_Dir dir) { return round_dir(dir) == ROUND_DIRECT; } inline bool round_inverse(Rounding_Dir dir) { return round_dir(dir) == ROUND_INVERSE; } inline bool round_strict_relation(Rounding_Dir dir) { return dir & ROUND_STRICT_RELATION; } #if PPL_CAN_CONTROL_FPU inline fpu_rounding_direction_type round_fpu_dir(Rounding_Dir dir) { switch (round_dir(dir)) { case ROUND_UP: return static_cast(PPL_FPU_UPWARD); case ROUND_DOWN: return static_cast(PPL_FPU_DOWNWARD); default: PPL_ASSERT(false); return static_cast(PPL_FPU_UPWARD); } } #undef PPL_FPU_DOWNWARD #undef PPL_FPU_TONEAREST #undef PPL_FPU_TOWARDZERO #undef PPL_FPU_UPWARD #endif /*! \relates Parma_Polyhedra_Library::Rounding_Dir */ inline Rounding_Dir inverse(Rounding_Dir dir) { Rounding_Dir d = round_dir(dir); switch (d) { case ROUND_UP: d = ROUND_DOWN; break; case ROUND_DOWN: d = ROUND_UP; break; default: PPL_ASSERT(false); /* Fall through */ case ROUND_IGNORE: return dir; } return static_cast((dir & ~ROUND_DIR_MASK) | d); } inline Rounding_Dir operator|(Rounding_Dir x, Rounding_Dir y) { return static_cast((unsigned)x | (unsigned)y); } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Rounding_Dir.defs.hh line 94. */ /* 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 85. */ /* Automatically generated from PPL source file ../src/initializer.hh line 28. */ #ifndef PPL_NO_AUTOMATIC_INITIALIZATION namespace { Parma_Polyhedra_Library::Init Parma_Polyhedra_Library_initializer; } // namespace #else namespace { Parma_Polyhedra_Library::Init* Parma_Polyhedra_Library_initializer_p; } // namespace #endif namespace Parma_Polyhedra_Library { //! Initializes the library. inline void initialize() { #ifdef PPL_NO_AUTOMATIC_INITIALIZATION if (Parma_Polyhedra_Library_initializer_p == 0) Parma_Polyhedra_Library_initializer_p = new Init(); #endif } //! Finalizes the library. inline void finalize() { #ifdef PPL_NO_AUTOMATIC_INITIALIZATION PPL_ASSERT(Parma_Polyhedra_Library_initializer_p != 0); delete Parma_Polyhedra_Library_initializer_p; Parma_Polyhedra_Library_initializer_p = 0; #endif } } //namespace Parma_Polyhedra_Library /* 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.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/Linear_Row.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Linear_Row; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Coefficient.types.hh line 1. */ /* 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. */ namespace Parma_Polyhedra_Library { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Coefficient traits. /*! \ingroup PPL_CXX_interface */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template 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 class Checked_Number; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/checked.defs.hh line 1. */ /* Abstract checked arithmetic function container */ #include #include /* Automatically generated from PPL source file ../src/mp_std_bits.defs.hh line 1. */ /* Declarations of specializations of std:: objects for multi-precision types. This will become obsolete when GMP and MPFR will provide the specializations by themselves. */ #include #include #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); namespace std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specialization of std::numeric_limits. #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template <> class numeric_limits { 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 tininess_before = false; static const float_round_style round_style = round_toward_zero; static Type min() { return static_cast(0); } static Type max() { return static_cast(0); } static Type epsilon() { return static_cast(0); } static Type round_error() { return static_cast(0); } static Type infinity() { return static_cast(0); } static Type quiet_NaN() { return static_cast(0); } static Type denorm_min() { return static_cast(1); } }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specialization of std::numeric_limits. #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template <> class numeric_limits { 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 tininess_before = false; static const float_round_style round_style = round_toward_zero; static Type min() { return static_cast(0); } static Type max() { return static_cast(0); } static Type epsilon() { return static_cast(0); } static Type round_error() { return static_cast(0); } static Type infinity() { return static_cast(0); } static Type quiet_NaN() { return static_cast(0); } static Type denorm_min() { return static_cast(0); } }; } // namespace std /* 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. This will become obsolete when GMP and MPFR will provide the specializations by themselves. */ 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 169. */ /* 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/Float.defs.hh line 29. */ #include /* Automatically generated from PPL source file ../src/Float.defs.hh line 31. */ #include #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 = 0x8000; static const uint16_t EXP_MASK = 0xfc00; static const uint16_t POS_INF = 0xfc00; static const uint16_t NEG_INF = 0x7c00; static const uint16_t POS_ZERO = 0x0000; static const uint16_t NEG_ZERO = 0x8000; 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(MANTISSA_BITS); int is_inf() const; int is_nan() const; int is_zero() const; int 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 = 0x80000000; static const uint32_t EXP_MASK = 0x7f800000; static const uint32_t POS_INF = 0x7f800000; static const uint32_t NEG_INF = 0xff800000; static const uint32_t POS_ZERO = 0x00000000; static const uint32_t NEG_ZERO = 0x80000000; 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(MANTISSA_BITS); int is_inf() const; int is_nan() const; int is_zero() const; int 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 = 0x80000000; static const uint32_t MSP_POS_INF = 0x7ff00000; static const uint32_t MSP_NEG_INF = 0xfff00000; static const uint32_t MSP_POS_ZERO = 0x00000000; static const uint32_t MSP_NEG_ZERO = 0x80000000; static const uint32_t LSP_INF = 0; static const uint32_t LSP_ZERO = 0; static const uint32_t LSP_MAX = 0xffffffff; 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(MANTISSA_BITS); int is_inf() const; int is_nan() const; int is_zero() const; int 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 = 0x80000000; static const uint32_t EXP_MASK = 0x7f000000; static const uint32_t POS_INF = 0x7f000000; static const uint32_t NEG_INF = 0xff000000; static const uint32_t POS_ZERO = 0x00000000; static const uint32_t NEG_ZERO = 0x80000000; 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(MANTISSA_BITS); int is_inf() const; int is_nan() const; int is_zero() const; int 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 = 0x00008000; static const uint32_t MSP_POS_INF = 0x00007fff; static const uint32_t MSP_NEG_INF = 0x0000ffff; static const uint32_t MSP_POS_ZERO = 0x00000000; static const uint32_t MSP_NEG_ZERO = 0x00008000; static const uint64_t LSP_INF = 0x8000000000000000ULL; static const uint64_t LSP_ZERO = 0; static const uint64_t LSP_DMAX = 0x7fffffffffffffffULL; static const uint64_t LSP_NMAX = 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(MANTISSA_BITS); int is_inf() const; int is_nan() const; int is_zero() const; int 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 = 0x8000000000000000ULL; static const uint64_t MSP_POS_INF = 0x7fff000000000000ULL; static const uint64_t MSP_NEG_INF = 0xffff000000000000ULL; static const uint64_t MSP_POS_ZERO = 0x0000000000000000ULL; static const uint64_t MSP_NEG_ZERO = 0x8000000000000000ULL; static const uint64_t LSP_INF = 0; static const uint64_t LSP_ZERO = 0; static const uint64_t LSP_MAX = 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(MANTISSA_BITS); int is_inf() const; int is_nan() const; int is_zero() const; int 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 class Float : public False { }; #if PPL_SUPPORTED_FLOAT template <> class 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 : 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 : 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 } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Float.inlines.hh line 1. */ /* IEC 559 floating point format related functions. */ #include namespace Parma_Polyhedra_Library { inline int float_ieee754_half::is_inf() const { if (word == NEG_INF) return -1; if (word == POS_INF) return 1; return 0; } inline int float_ieee754_half::is_nan() const { return (word & ~SGN_MASK) > POS_INF; } inline int float_ieee754_half::is_zero() 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 int float_ieee754_half::sign_bit() const { return !!(word & SGN_MASK); } inline void float_ieee754_half::dec() { --word; } inline void float_ieee754_half::inc() { ++word; } inline void float_ieee754_half::set_max(bool negative) { word = 0x7bff; if (negative) word |= SGN_MASK; } inline void float_ieee754_half::build(bool negative, mpz_t mantissa, int exponent) { word = mpz_get_ui(mantissa) & ((1UL << MANTISSA_BITS) - 1); if (negative) word |= SGN_MASK; word |= static_cast(exponent + EXPONENT_BIAS) << MANTISSA_BITS; } inline int float_ieee754_single::is_inf() const { if (word == NEG_INF) return -1; if (word == POS_INF) return 1; return 0; } inline int float_ieee754_single::is_nan() const { return (word & ~SGN_MASK) > POS_INF; } inline int float_ieee754_single::is_zero() 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 int float_ieee754_single::sign_bit() const { return !!(word & SGN_MASK); } inline void float_ieee754_single::dec() { --word; } inline void float_ieee754_single::inc() { ++word; } inline void float_ieee754_single::set_max(bool negative) { word = 0x7f7fffff; if (negative) word |= SGN_MASK; } inline void float_ieee754_single::build(bool negative, mpz_t mantissa, int exponent) { word = mpz_get_ui(mantissa) & ((1UL << MANTISSA_BITS) - 1); if (negative) word |= SGN_MASK; word |= static_cast(exponent + EXPONENT_BIAS) << MANTISSA_BITS; } inline int float_ieee754_double::is_inf() const { if (lsp != LSP_INF) return 0; if (msp == MSP_NEG_INF) return -1; if (msp == MSP_POS_INF) return 1; return 0; } inline int float_ieee754_double::is_nan() 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::is_zero() 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 int float_ieee754_double::sign_bit() const { return !!(msp & MSP_SGN_MASK); } 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 = 0x7fefffff; lsp = 0xffffffff; if (negative) msp |= MSP_SGN_MASK; } inline void float_ieee754_double::build(bool negative, mpz_t mantissa, int exponent) { #if ULONG_MAX == 0xffffffffUL lsp = mpz_get_ui(mantissa); mpz_tdiv_q_2exp(mantissa, mantissa, 32); unsigned long m = mpz_get_ui(mantissa); #else unsigned long m = mpz_get_ui(mantissa); lsp = m; m >>= 32; #endif msp = m & ((1UL << (MANTISSA_BITS - 32)) - 1); if (negative) msp |= MSP_SGN_MASK; msp |= static_cast(exponent + EXPONENT_BIAS) << (MANTISSA_BITS - 32); } inline int float_ibm_single::is_inf() const { if (word == NEG_INF) return -1; if (word == POS_INF) return 1; return 0; } inline int float_ibm_single::is_nan() const { return (word & ~SGN_MASK) > POS_INF; } inline int float_ibm_single::is_zero() 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 int float_ibm_single::sign_bit() const { return !!(word & SGN_MASK); } inline void float_ibm_single::dec() { --word; } inline void float_ibm_single::inc() { ++word; } inline void float_ibm_single::set_max(bool negative) { word = 0x7f000000; if (negative) word |= SGN_MASK; } inline void float_ibm_single::build(bool negative, mpz_t mantissa, int exponent) { word = mpz_get_ui(mantissa) & ((1UL << MANTISSA_BITS) - 1); if (negative) word |= SGN_MASK; word |= static_cast(exponent + EXPONENT_BIAS) << MANTISSA_BITS; } inline int float_intel_double_extended::is_inf() const { if (lsp != LSP_INF) return 0; uint32_t a = msp & MSP_NEG_INF; if (a == MSP_NEG_INF) return -1; if (a == MSP_POS_INF) return 1; return 0; } inline int float_intel_double_extended::is_nan() const { return (msp & MSP_POS_INF) == MSP_POS_INF && lsp != LSP_INF; } inline int float_intel_double_extended::is_zero() const { if (lsp != LSP_ZERO) return 0; 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 int float_intel_double_extended::sign_bit() const { return !!(msp & MSP_SGN_MASK); } 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 = 0x00007ffe; lsp = 0xffffffffffffffffULL; 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, 8, 0, 0, mantissa); #else lsp = mpz_get_ui(mantissa); #endif msp = (negative ? MSP_SGN_MASK : 0); msp |= static_cast(exponent + EXPONENT_BIAS); } inline int float_ieee754_quad::is_inf() const { if (lsp != LSP_INF) return 0; if (msp == MSP_NEG_INF) return -1; if (msp == MSP_POS_INF) return 1; return 0; } inline int float_ieee754_quad::is_nan() const { return (msp & ~MSP_SGN_MASK) == MSP_POS_INF && lsp != LSP_INF; } inline int float_ieee754_quad::is_zero() 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 int float_ieee754_quad::sign_bit() const { return !!(msp & MSP_SGN_MASK); } 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 = 0x7ffeffffffffffffULL; lsp = 0xffffffffffffffffULL; 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, 8, 0, 0, mantissa); lsp = parts[0]; msp = parts[1]; msp &= ((1ULL << (MANTISSA_BITS - 64)) - 1); if (negative) msp |= MSP_SGN_MASK; msp |= static_cast(exponent + EXPONENT_BIAS) << (MANTISSA_BITS - 64); } #if PPL_SUPPORTED_FLOAT inline Float::Float() { } inline Float::Float(float v) { u.number = v; } inline float Float::value() { return u.number; } #endif #if PPL_SUPPORTED_DOUBLE inline Float::Float() { } inline Float::Float(double v) { u.number = v; } inline double Float::value() { return u.number; } #endif #if PPL_SUPPORTED_LONG_DOUBLE inline Float::Float() { } inline Float::Float(long double v) { u.number = v; } inline long double Float::value() { return u.number; } #endif } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Float.defs.hh line 366. */ /* Automatically generated from PPL source file ../src/checked.defs.hh line 34. */ 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 \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(qual type& arg) { \ return PPL_FUNCTION_CLASS(name)::function(arg); \ } #define PPL_DECLARE_FUN1_0_1(name, ret_type, qual, type, after1) \ template \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(qual type& arg, after1 a1) { \ return PPL_FUNCTION_CLASS(name)::function(arg, a1); \ } #define PPL_DECLARE_FUN1_0_2(name, ret_type, qual, type, after1, after2) \ template \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(qual type& arg, after1 a1, after2 a2) { \ return PPL_FUNCTION_CLASS(name)::function(arg, a1, a2); \ } #define PPL_DECLARE_FUN1_0_3(name, ret_type, qual, type, \ after1, after2, after3) \ template \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(qual type& arg, \ after1 a1, after2 a2, after3 a3) { \ return PPL_FUNCTION_CLASS(name)::function(arg, \ a1, a2, a3); \ } #define PPL_DECLARE_FUN1_1_1(name, ret_type, before1, qual, type, after1) \ template \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(before1 b1, qual type& arg, after1 a1) { \ return PPL_FUNCTION_CLASS(name)::function(b1, arg, a1); \ } #define PPL_DECLARE_FUN1_1_2(name, ret_type, before1, qual, type, \ after1, after2) \ template \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(before1 b1, qual type& arg, \ after1 a1, after2 a2) { \ return PPL_FUNCTION_CLASS(name)::function(b1, arg, \ a1, a2); \ } #define PPL_DECLARE_FUN1_2_2(name, ret_type, before1, before2, qual, type, \ after1, after2) \ template \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(before1 b1, before2 b2, qual type& arg, \ after1 a1, after2 a2) { \ return PPL_FUNCTION_CLASS(name)::function(b1, b2, arg, \ a1, a2); \ } #define PPL_DECLARE_FUN2_0_0(name, ret_type, qual1, type1, qual2, type2) \ template \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(qual1 type1& arg1, qual2 type2& arg2) { \ return PPL_FUNCTION_CLASS(name)::function(arg1, arg2); \ } #define PPL_DECLARE_FUN2_0_1(name, ret_type, qual1, type1, \ qual2, type2, after1) \ template \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(qual1 type1& arg1, qual2 type2& arg2, \ after1 a1) { \ return PPL_FUNCTION_CLASS(name)::function(arg1, arg2, a1); \ } #define PPL_DECLARE_FUN2_0_2(name, ret_type, qual1, type1, qual2, type2, \ after1, after2) \ template \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(qual1 type1& arg1, qual2 type2& arg2, \ after1 a1, after2 a2) { \ return PPL_FUNCTION_CLASS(name)::function(arg1, arg2, a1, a2); \ } #define PPL_DECLARE_FUN3_0_1(name, ret_type, qual1, type1, \ qual2, type2, qual3, type3, after1) \ template \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(qual1 type1& arg1, qual2 type2& arg2, \ qual3 type3& arg3, after1 a1) { \ return PPL_FUNCTION_CLASS(name)::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 \ struct PPL_FUNCTION_CLASS(name); \ template \ inline ret_type name(qual1 type1& arg1, qual2 type2& arg2, \ qual3 type3& arg3, qual4 type4& arg4, \ qual5 type5& arg5, after1 a1) { \ return PPL_FUNCTION_CLASS(name) \ ::function(arg1, arg2, arg3, arg4, arg5, a1); \ } #define PPL_SPECIALIZE_FUN1_0_0(name, func, ret_type, qual, type) \ template \ struct PPL_FUNCTION_CLASS(name) { \ static inline ret_type function(qual type& arg) { \ return func(arg); \ } \ }; #define PPL_SPECIALIZE_FUN1_0_1(name, func, ret_type, qual, type, after1) \ template \ struct PPL_FUNCTION_CLASS(name) { \ static inline ret_type function(qual type& arg, after1 a1) { \ return func(arg, a1); \ } \ }; #define PPL_SPECIALIZE_FUN1_0_2(name, func, ret_type, qual, type, \ after1, after2) \ template \ struct PPL_FUNCTION_CLASS(name) { \ static inline ret_type function(qual type& arg, \ after1 a1, after2 a2) { \ return func(arg, a1, a2); \ } \ }; #define PPL_SPECIALIZE_FUN1_0_3(name, func, ret_type, qual, type, \ after1, after2, after3) \ template \ struct PPL_FUNCTION_CLASS(name) { \ static inline ret_type function(qual type& arg, \ after1 a1, after2 a2, after3 a3) { \ return func(arg, a1, a2, a3); \ } \ }; #define PPL_SPECIALIZE_FUN1_1_1(name, func, ret_type, before1, \ qual, type, after1) \ template \ struct PPL_FUNCTION_CLASS(name) { \ static inline ret_type function(before1 b1, qual type& arg, \ after1 a1) { \ return func(b1, arg, a1); \ } \ }; #define PPL_SPECIALIZE_FUN1_1_2(name, func, ret_type, before1, \ qual, type, after1, after2) \ template \ struct PPL_FUNCTION_CLASS(name) { \ static inline ret_type function(before1 b1, qual type& arg, \ after1 a1, after2 a2) { \ return func(b1, arg, a1, a2); \ } \ }; #define PPL_SPECIALIZE_FUN1_2_2(name, func, ret_type, before1, before2, \ qual, type, after1, after2) \ template \ struct PPL_FUNCTION_CLASS(name) { \ static inline ret_type function(before1 b1, before2 b2, \ qual type& arg, \ after1 a1, after2 a2) { \ return func(b1, b2, arg, a1, a2); \ } \ }; #define PPL_SPECIALIZE_FUN2_0_0(name, func, ret_type, qual1, type1, \ qual2, type2) \ template \ struct PPL_FUNCTION_CLASS(name) { \ static inline ret_type function(qual1 type1& arg1, \ qual2 type2 &arg2) { \ return func(arg1, arg2); \ } \ }; #define PPL_SPECIALIZE_FUN2_0_1(name, func, ret_type, qual1, type1, \ qual2, type2, after1) \ template \ struct PPL_FUNCTION_CLASS(name) { \ static inline ret_type function(qual1 type1& arg1, \ qual2 type2 &arg2, after1 a1) { \ return func(arg1, arg2, a1); \ } \ }; #define PPL_SPECIALIZE_FUN2_0_2(name, func, ret_type, qual1, type1, \ qual2, type2, after1, after2) \ template \ struct PPL_FUNCTION_CLASS(name) { \ static inline ret_type function(qual1 type1& arg1, \ qual2 type2 &arg2, \ after1 a1, after2 a2) { \ return func(arg1, arg2, a1, a2); \ } \ }; #define PPL_SPECIALIZE_FUN3_0_1(name, func, ret_type, qual1, type1, \ qual2, type2, qual3, type3, after1) \ template \ struct PPL_FUNCTION_CLASS(name) { \ static inline Result function(qual1 type1& arg1, qual2 type2 &arg2, \ qual3 type3 &arg3, after1 a1) { \ return func(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 \ struct PPL_FUNCTION_CLASS(name) { \ static inline Result \ function(qual1 type1& arg1, qual2 type2 &arg2, qual3 type3 &arg3, \ qual4 type4 &arg4, qual5 type5 &arg5, after1 a1) { \ return func(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 Result round(To& to, Result r, Rounding_Dir dir); Result input_mpq(mpq_class& to, std::istream& is); } // 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 struct Is_Special : public False { }; template <> struct Is_Special : public True {}; template <> struct Is_Special : public True {}; template <> struct Is_Special : 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 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::has_quiet_NaN); //! Handle infinity special values if \p T have them. const_bool_nodef(has_infinity, std::numeric_limits::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 a < b avoiding the warning about the comparison being always false due to limited range of data type. FIXME: we don't have found a working solution. gcc option -Wno-type-limits suppress 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 struct Safe_Conversion : public False { }; template struct Safe_Conversion : public True { }; #define PPL_SAFE_CONVERSION(To, From) \ template <> struct Safe_Conversion : 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); //PPL_SAFE_CONVERSION(mpz_class, 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); //PPL_SAFE_CONVERSION(mpz_class, 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); //PPL_SAFE_CONVERSION(mpq_class, 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); //PPL_SAFE_CONVERSION(mpq_class, unsigned long long); PPL_SAFE_CONVERSION(mpq_class, float); PPL_SAFE_CONVERSION(mpq_class, double); //PPL_SAFE_CONVERSION(mpq_class, long double); #undef PPL_SAFE_CONVERSION template struct PPL_FUNCTION_CLASS(construct) { static inline Result function(Type& to, const Type& from, Rounding_Dir) { new (&to) Type(from); return V_EQ; } }; template struct PPL_FUNCTION_CLASS(construct) { static inline Result function(To& to, const From& from, Rounding_Dir dir) { new (&to) To(); return assign(to, from, dir); } }; template 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, r, dir); } }; template inline Result assign_exact(To& to, const From& from, Rounding_Dir) { to = from; return V_EQ; } template inline typename Enable_If::value, void>::type copy_generic(Type& to, const Type& from) { to = from; } template inline Result abs_generic(To& to, const From& from, Rounding_Dir dir) { if (from < 0) return neg(to, from, dir); else return assign(to, from, dir); } template inline void gcd_exact_noabs(To& to, const From& x, const From& y) { To nx = x; To ny = y; To rm; while (ny != 0) { // The following is derived from the assumption that x % y // is always representable. This is true for both native integers // and IEC 559 floating point numbers. rem(rm, nx, ny, ROUND_NOT_NEEDED); nx = ny; ny = rm; } to = nx; } template inline Result gcd_exact(To& to, const From1& x, const From2& y, Rounding_Dir dir) { gcd_exact_noabs(to, x, y); return abs(to, to, dir); } template 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(to, x, dir); } } s = 1; t = 0; bool negative_x = x < 0; bool negative_y = y < 0; Result r; r = abs(to, x, dir); if (r != V_EQ) return r; From2 ay; r = abs(ay, 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 == ay) goto sign_check; #endif { To2 v1 = 0; To3 v2 = 1; To1 v3 = static_cast(ay); while (true) { To1 q = to / v3; // Remainder, next candidate GCD. To1 t3 = to - q*v3; To2 t1 = s - static_cast(q)*v1; To3 t2 = t - static_cast(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(s, s, dir); if (r != V_EQ) return r; } if (negative_y) return neg(t, t, dir); return V_EQ; #undef PPL_MATCH_GMP_GCDEXT } template 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 nx, ny; Result r; r = abs(nx, x, dir); if (r != V_EQ) return r; r = abs(ny, y, dir); if (r != V_EQ) return r; To gcd; gcd_exact_noabs(gcd, nx, ny); // The following is derived from the assumption that x / gcd(x, y) // is always representable. This is true for both native integers // and IEC 559 floating point numbers. div(to, nx, gcd, ROUND_NOT_NEEDED); return mul(to, to, ny, dir); } template inline Result_Relation sgn_generic(const Type& x) { if (x > 0) return VR_GT; if (x == 0) return VR_EQ; return VR_LT; } template struct Safe_Int_Comparison : public False { }; template struct Safe_Int_Comparison::value && C_Integer::value)>::type> : public Bool<(C_Integer::is_signed ? (C_Integer::is_signed || sizeof(T2) < sizeof(T1) || sizeof(T2) < sizeof(int)) : (!C_Integer::is_signed || sizeof(T1) < sizeof(T2) || sizeof(T1) < sizeof(int)))> { }; template inline typename Enable_If<(Safe_Int_Comparison::value || Safe_Conversion::value || Safe_Conversion::value), bool>::type lt(const T1& x, const T2& y) { return x < y; } template inline typename Enable_If<(Safe_Int_Comparison::value || Safe_Conversion::value || Safe_Conversion::value), bool>::type le(const T1& x, const T2& y) { return x <= y; } template inline typename Enable_If<(Safe_Int_Comparison::value || Safe_Conversion::value || Safe_Conversion::value), bool>::type eq(const T1& x, const T2& y) { return x == y; } template inline typename Enable_If<(!Safe_Int_Comparison::value && C_Integer::value && C_Integer::is_signed), bool>::type lt(const S& x, const U& y) { return x < 0 || static_cast::other_type>(x) < y; } template inline typename Enable_If<(!Safe_Int_Comparison::value && C_Integer::value && C_Integer::is_signed), bool>::type lt(const U& x, const S& y) { return y >= 0 && x < static_cast::other_type>(y); } template inline typename Enable_If<(!Safe_Int_Comparison::value && C_Integer::value && C_Integer::is_signed), bool>::type le(const S& x, const U& y) { return x < 0 || static_cast::other_type>(x) <= y; } template inline typename Enable_If<(!Safe_Int_Comparison::value && C_Integer::value && C_Integer::is_signed), bool>::type le(const U& x, const S& y) { return y >= 0 && x <= static_cast::other_type>(y); } template inline typename Enable_If<(!Safe_Int_Comparison::value && C_Integer::value && C_Integer::is_signed), bool>::type eq(const S& x, const U& y) { return x >= 0 && static_cast::other_type>(x) == y; } template inline typename Enable_If<(!Safe_Int_Comparison::value && C_Integer::value && C_Integer::is_signed), bool>::type eq(const U& x, const S& y) { return y >= 0 && x == static_cast::other_type>(y); } template inline typename Enable_If<(!Safe_Conversion::value && !Safe_Conversion::value && (!C_Integer::value || !C_Integer::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 inline typename Enable_If<(!Safe_Conversion::value && !Safe_Conversion::value && (!C_Integer::value || !C_Integer::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 inline typename Enable_If<(!Safe_Conversion::value && !Safe_Conversion::value && (!C_Integer::value || !C_Integer::value)), bool>::type le(const T1& x, const T2& y) { PPL_DIRTY_TEMP(T1, tmp); Result r = assign_r(tmp, y, static_cast(ROUND_UP | ROUND_STRICT_RELATION)); 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: // FIXME: See comment above. PPL_ASSERT(0); default: return false; } } template inline bool lt_p(const Type1& x, const Type2& y) { return lt(x, y); } template inline bool le_p(const Type1& x, const Type2& y) { return le(x, y); } template inline bool eq_p(const Type1& x, const Type2& y) { return eq(x, y); } template 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 inline Result assign_nan(Type& to, Result r) { assign_special(to, VC_NAN, ROUND_IGNORE); return r; } template inline Result input_generic(Type& to, std::istream& is, Rounding_Dir dir) { PPL_DIRTY_TEMP0(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(to, c, dir); case VC_NAN: return assign_nan(to, r); default: break; } PPL_ASSERT(r == V_EQ); return assign(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 #include #include #include #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 struct Extended_Int { static const Type plus_infinity = C_Integer::max; static const Type minus_infinity = (C_Integer::min >= 0 ? C_Integer::max - 1 : C_Integer::min); static const Type not_a_number = (C_Integer::min >= 0 ? C_Integer::max - Policy::has_infinity * 2 : C_Integer::min + Policy::has_infinity); static const Type min = (C_Integer::min + (C_Integer::min >= 0 ? 0 : (Policy::has_infinity + Policy::has_nan))); static const Type max = (C_Integer::max - (C_Integer::min >= 0 ? (2 * Policy::has_infinity + Policy::has_nan) : Policy::has_infinity)); }; template inline Result set_neg_overflow_int(To& to, Rounding_Dir dir) { if (round_up(dir)) { to = Extended_Int::min; return V_LT_INF; } else { if (Policy::has_infinity) { to = Extended_Int::minus_infinity; return V_GT_MINUS_INFINITY; } return V_GT_MINUS_INFINITY | V_UNREPRESENTABLE; } } template inline Result set_pos_overflow_int(To& to, Rounding_Dir dir) { if (round_down(dir)) { to = Extended_Int::max; return V_GT_SUP; } else { if (Policy::has_infinity) { to = Extended_Int::plus_infinity; return V_LT_PLUS_INFINITY; } return V_LT_PLUS_INFINITY | V_UNREPRESENTABLE; } } template inline Result round_lt_int_no_overflow(To& to, Rounding_Dir dir) { if (round_down(dir)) { --to; return V_GT; } return V_LT; } template inline Result round_gt_int_no_overflow(To& to, Rounding_Dir dir) { if (round_up(dir)) { ++to; return V_LT; } return V_GT; } template inline Result round_lt_int(To& to, Rounding_Dir dir) { if (round_down(dir)) { if (to == Extended_Int::min) { if (Policy::has_infinity) { to = Extended_Int::minus_infinity; return V_GT_MINUS_INFINITY; } return V_GT_MINUS_INFINITY | V_UNREPRESENTABLE; } else { --to; return V_GT; } } return V_LT; } template inline Result round_gt_int(To& to, Rounding_Dir dir) { if (round_up(dir)) { if (to == Extended_Int::max) { if (Policy::has_infinity) { to = Extended_Int::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 inline Result classify_int(const Type v, bool nan, bool inf, bool sign) { if (Policy::has_nan && (nan || sign) && v == Extended_Int::not_a_number) return V_NAN; if (!inf && !sign) return V_LGE; if (Policy::has_infinity) { if (v == Extended_Int::minus_infinity) return inf ? V_EQ_MINUS_INFINITY : V_LT; if (v == Extended_Int::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 inline bool is_nan_int(const Type v) { return Policy::has_nan && v == Extended_Int::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 inline bool is_minf_int(const Type v) { return Policy::has_infinity && v == Extended_Int::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 inline bool is_pinf_int(const Type v) { return Policy::has_infinity && v == Extended_Int::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 inline bool is_int_int(const Type v) { return !is_nan(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 inline Result assign_special_int(Type& v, Result_Class c, Rounding_Dir dir) { switch (c) { case VC_NAN: if (Policy::has_nan) { v = Extended_Int::not_a_number; return V_NAN; } return V_NAN | V_UNREPRESENTABLE; case VC_MINUS_INFINITY: if (Policy::has_infinity) { v = Extended_Int::minus_infinity; return V_EQ_MINUS_INFINITY; } if (round_up(dir)) { v = Extended_Int::min; return V_LT_INF; } return V_EQ_MINUS_INFINITY | V_UNREPRESENTABLE; case VC_PLUS_INFINITY: if (Policy::has_infinity) { v = Extended_Int::plus_infinity; return V_EQ_PLUS_INFINITY; } if (round_down(dir)) { v = Extended_Int::max; return V_GT_SUP; } return V_EQ_PLUS_INFINITY | V_UNREPRESENTABLE; default: PPL_ASSERT(0); 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 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::min > Extended_Int::min || Extended_Int::max < Extended_Int::max))) { if (CHECK_P(To_Policy::check_overflow, PPL_LT_SILENT(from, From(Extended_Int::min)))) return set_neg_overflow_int(to, dir); if (CHECK_P(To_Policy::check_overflow, PPL_GT_SILENT(from, From(Extended_Int::max)))) return set_pos_overflow_int(to, dir); } to = To(from); return V_EQ; } template 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 > From(Extended_Int::max))) return set_pos_overflow_int(to, dir); } to = To(from); return V_EQ; } template 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, dir); if (sizeof(To) < sizeof(From)) { if (CHECK_P(To_Policy::check_overflow, from > From(Extended_Int::max))) return set_pos_overflow_int(to, dir); } to = To(from); return V_EQ; } template 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::max < Extended_Int::max)) { if (CHECK_P(To_Policy::check_overflow, PPL_GT_SILENT(from, From(Extended_Int::max)))) return set_pos_overflow_int(to, dir); } to = 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 inline Result assign_int_float(To& to, const From from, Rounding_Dir dir) { if (is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(from)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(from)) return assign_special(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::min))) return set_neg_overflow_int(to, dir); if (CHECK_P(To_Policy::check_overflow, !le(from, Extended_Int::max))) return set_pos_overflow_int(to, dir); #else if (CHECK_P(To_Policy::check_overflow, (from < Extended_Int::min))) return set_neg_overflow_int(to, dir); if (CHECK_P(To_Policy::check_overflow, (from > Extended_Int::max))) return set_pos_overflow_int(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, dir); if (round_direct(ROUND_DOWN)) return round_gt_int(to, dir); if (from < i_from) return round_lt_int(to, dir); if (from > i_from) return round_gt_int(to, dir); PPL_ASSERT(false); return V_NAN; } 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 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::min))) return set_neg_overflow_int(to, dir); if (PPL_GT_SILENT(v, (Extended_Int::max))) return set_pos_overflow_int(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, v, dir); to = v; return V_EQ; } } } return ::sgn(from) < 0 ? set_neg_overflow_int(to, dir) : set_pos_overflow_int(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 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, dir); if (sizeof(To) <= sizeof(unsigned long)) { if (!To_Policy::check_overflow) { to = from.get_ui(); return V_EQ; } if (from.fits_ulong_p()) { unsigned long v = from.get_ui(); if (PPL_GT_SILENT(v, (Extended_Int::max))) return set_pos_overflow_int(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; else mpz_export(&to, 0, -1, sizeof(To), 0, 0, m); return V_EQ; } } return set_pos_overflow_int(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 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_TEMP0(mpz_class, q); mpz_ptr _q = q.get_mpz_t(); if (round_not_requested(dir)) { mpz_tdiv_q(_q, n, d); Result r = assign(to, q, dir); if (r != V_EQ) return r; return V_LGE; } mpz_t rem; int sign; mpz_init(rem); mpz_tdiv_qr(_q, rem, n, d); sign = mpz_sgn(rem); mpz_clear(rem); Result r = assign(to, q, dir); if (r != V_EQ) return r; switch (sign) { case -1: return round_lt_int(to, dir); case 1: return round_gt_int(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 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 inline Result neg_int_larger(Type& to, const Type x, Rounding_Dir dir) { typename Larger::type_for_neg l = x; l = -l; return assign(to, l, dir); } template inline Result add_int_larger(Type& to, const Type x, const Type y, Rounding_Dir dir) { typename Larger::type_for_add l = x; l += y; return assign(to, l, dir); } template inline Result sub_int_larger(Type& to, const Type x, const Type y, Rounding_Dir dir) { typename Larger::type_for_sub l = x; l -= y; return assign(to, l, dir); } template inline Result mul_int_larger(Type& to, const Type x, const Type y, Rounding_Dir dir) { typename Larger::type_for_mul l = x; l *= y; return assign(to, l, dir); } template inline Result neg_signed_int(Type& to, const Type from, Rounding_Dir dir) { if (To_Policy::check_overflow && Larger::use_for_neg) return neg_int_larger(to, from, dir); if (CHECK_P(To_Policy::check_overflow, (from < -Extended_Int::max))) return set_pos_overflow_int(to, dir); to = -from; return V_EQ; } template inline Result neg_unsigned_int(Type& to, const Type from, Rounding_Dir dir) { if (To_Policy::check_overflow && Larger::use_for_neg) return neg_int_larger(to, from, dir); if (CHECK_P(To_Policy::check_overflow, from != 0)) return set_neg_overflow_int(to, dir); to = from; return V_EQ; } template inline Result add_signed_int(Type& to, const Type x, const Type y, Rounding_Dir dir) { if (To_Policy::check_overflow && Larger::use_for_add) return add_int_larger(to, x, y, dir); if (To_Policy::check_overflow) { if (y >= 0) { if (x > Extended_Int::max - y) return set_pos_overflow_int(to, dir); } else if (x < Extended_Int::min - y) return set_neg_overflow_int(to, dir); } to = x + y; return V_EQ; } template inline Result add_unsigned_int(Type& to, const Type x, const Type y, Rounding_Dir dir) { if (To_Policy::check_overflow && Larger::use_for_add) return add_int_larger(to, x, y, dir); if (CHECK_P(To_Policy::check_overflow, (x > Extended_Int::max - y))) return set_pos_overflow_int(to, dir); to = x + y; return V_EQ; } template inline Result sub_signed_int(Type& to, const Type x, const Type y, Rounding_Dir dir) { if (To_Policy::check_overflow && Larger::use_for_sub) return sub_int_larger(to, x, y, dir); if (To_Policy::check_overflow) { if (y >= 0) { if (x < Extended_Int::min + y) return set_neg_overflow_int(to, dir); } else if (x > Extended_Int::max + y) return set_pos_overflow_int(to, dir); } to = x - y; return V_EQ; } template inline Result sub_unsigned_int(Type& to, const Type x, const Type y, Rounding_Dir dir) { if (To_Policy::check_overflow && Larger::use_for_sub) return sub_int_larger(to, x, y, dir); if (CHECK_P(To_Policy::check_overflow, (x < Extended_Int::min + y))) return set_neg_overflow_int(to, dir); to = x - y; return V_EQ; } template inline Result mul_signed_int(Type& to, const Type x, const Type y, Rounding_Dir dir) { if (To_Policy::check_overflow && Larger::use_for_mul) return mul_int_larger(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, x, dir); if (x >= 0) { if (y > 0) { if (x > Extended_Int::max / y) return set_pos_overflow_int(to, dir); } else { if (x > Extended_Int::min / y) return set_neg_overflow_int(to, dir); } } else { if (y < 0) { if (x < Extended_Int::max / y) return set_pos_overflow_int(to, dir); } else { if (x < Extended_Int::min / y) return set_neg_overflow_int(to, dir); } } to = x * y; return V_EQ; } template inline Result mul_unsigned_int(Type& to, const Type x, const Type y, Rounding_Dir dir) { if (To_Policy::check_overflow && Larger::use_for_mul) return mul_int_larger(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::max / y) return set_pos_overflow_int(to, dir); to = x * y; return V_EQ; } template 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, V_DIV_ZERO); } if (To_Policy::check_overflow && y == -1) return neg_signed_int(to, x, dir); to = x / y; if (round_not_requested(dir)) return V_LGE; Type m = x % y; if (m < 0) return round_lt_int_no_overflow(to, dir); else if (m > 0) return round_gt_int_no_overflow(to, dir); else return V_EQ; } template 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, 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, dir); } template 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, V_DIV_ZERO); } if (To_Policy::check_overflow && y == -1) return neg_signed_int(to, x, dir); to = x / y; return V_EQ; } template 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, V_DIV_ZERO); } to = x / y; return V_EQ; } template 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, V_MOD_ZERO); } to = x % y; return V_EQ; } template 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, V_MOD_ZERO); } to = x % y; return V_EQ; } template inline Result div_2exp_unsigned_int(Type& to, const Type x, unsigned int exp, Rounding_Dir dir) { if (exp >= sizeof(Type) * CHAR_BIT) { to = 0; if (round_not_requested(dir)) return V_GE; if (x == 0) return V_EQ; return round_gt_int_no_overflow(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, dir); else return V_EQ; } template inline Result div_2exp_signed_int(Type& to, const Type x, unsigned int exp, Rounding_Dir dir) { if (exp > sizeof(Type) * CHAR_BIT - 1) { zero: to = 0; if (round_not_requested(dir)) return V_LGE; if (x < 0) return round_lt_int_no_overflow(to, dir); else if (x > 0) return round_gt_int_no_overflow(to, dir); else return V_EQ; } if (exp == sizeof(Type) * CHAR_BIT - 1) { if (x == C_Integer::min) { to = -1; return V_EQ; } goto zero; } #if 0 to = x / (Type(1) << exp); if (round_not_requested(dir)) return V_GE; Type r = x % (Type(1) << exp); if (r < 0) return round_lt_int_no_overflow(to, dir); else if (r > 0) return round_gt_int_no_overflow(to, dir); else return V_EQ; #else // Faster but compiler implementation dependent (see C++98 5.8.3) to = x >> exp; if (round_not_requested(dir)) return V_GE; if (x & ((Type(1) << exp) - 1)) return round_gt_int_no_overflow(to, dir); return V_EQ; #endif } template 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(Type) * CHAR_BIT) return set_pos_overflow_int(to, dir); Type n = Type(1) << exp; return add_unsigned_int(to, x, n, dir); } template 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(Type) * CHAR_BIT) return set_pos_overflow_int(to, dir); if (exp == sizeof(Type) * CHAR_BIT - 1) { Type n = -2 * (Type(1) << (exp - 1)); return sub_signed_int(to, x, n, dir); } else { Type n = Type(1) << exp; return add_signed_int(to, x, n, dir); } } template 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(Type) * CHAR_BIT) return set_neg_overflow_int(to, dir); Type n = Type(1) << exp; return sub_unsigned_int(to, x, n, dir); } template 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(Type) * CHAR_BIT) return set_neg_overflow_int(to, dir); if (exp == sizeof(Type) * CHAR_BIT - 1) { Type n = -2 * (Type(1) << (exp - 1)); return add_signed_int(to, x, n, dir); } else { Type n = Type(1) << exp; return sub_signed_int(to, x, n, dir); } } template 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(Type) * CHAR_BIT) { if (x == 0) { to = 0; return V_EQ; } return set_pos_overflow_int(to, dir); } if (x & (((Type(1) << exp) - 1) << (sizeof(Type) * CHAR_BIT - exp))) return set_pos_overflow_int(to, dir); Type n = x << exp; if (PPL_GT_SILENT(n, (Extended_Int::max))) return set_pos_overflow_int(to, dir); to = n; return V_EQ; } template inline Result mul_2exp_signed_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(Type) * CHAR_BIT - 1) { if (x < 0) return set_neg_overflow_int(to, dir); else if (x > 0) return set_pos_overflow_int(to, dir); else { to = 0; return V_EQ; } } Type mask = ((Type(1) << exp) - 1) << (sizeof(Type) * CHAR_BIT - 1 - exp); Type n; if (x < 0) { if ((x & mask) != mask) return set_neg_overflow_int(to, dir); n = x << exp; if (PPL_LT_SILENT(n, (Extended_Int::min))) return set_neg_overflow_int(to, dir); } else { if (x & mask) return set_pos_overflow_int(to, dir); n = x << exp; if (PPL_GT_SILENT(n, (Extended_Int::max))) return set_pos_overflow_int(to, dir); } to = n; return V_EQ; } template inline Result smod_2exp_unsigned_int(Type& to, const Type x, unsigned int exp, Rounding_Dir dir) { if (exp > sizeof(Type) * CHAR_BIT) to = x; else { Type v = exp == sizeof(Type) * CHAR_BIT ? x : (x & ((Type(1) << exp) - 1)); if (v >= Type(1) << (exp - 1)) return set_neg_overflow_int(to, dir); else to = v; } return V_EQ; } template inline Result smod_2exp_signed_int(Type& to, const Type x, unsigned int exp, Rounding_Dir) { if (exp >= sizeof(Type) * CHAR_BIT) to = x; else { Type m = Type(1) << (exp - 1); to = (x & (m - 1)) - (x & m); } return V_EQ; } template inline Result umod_2exp_unsigned_int(Type& to, const Type x, unsigned int exp, Rounding_Dir) { if (exp >= sizeof(Type) * CHAR_BIT) to = x; else to = x & ((Type(1) << exp) - 1); return V_EQ; } template inline Result umod_2exp_signed_int(Type& to, const Type x, unsigned int exp, Rounding_Dir dir) { if (exp >= sizeof(Type) * CHAR_BIT) { if (x < 0) return set_pos_overflow_int(to, dir); to = x; } else to = x & ((Type(1) << exp) - 1); return V_EQ; } template inline void isqrtrem(Type& q, Type& r, const Type from) { q = 0; r = from; Type t(1); for (t <<= CHAR_BIT * sizeof(Type) - 2; t != 0; t >>= 2) { Type s = q + t; if (s <= r) { r -= s; q = s + t; } q >>= 1; } } template inline Result sqrt_unsigned_int(Type& to, const Type from, Rounding_Dir dir) { Type rem; isqrtrem(to, rem, from); if (round_not_requested(dir)) return V_GE; if (rem == 0) return V_EQ; return round_gt_int(to, dir); } template 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, V_SQRT_NEG); } return sqrt_unsigned_int(to, from, dir); } template inline Result add_mul_int(Type& to, const Type x, const Type y, Rounding_Dir dir) { Type z; Result r = mul(z, x, y, dir); switch (result_overflow(r)) { case 0: return add(to, to, z, dir); case -1: if (to <= 0) return set_neg_overflow_int(to, dir); return assign_nan(to, V_UNKNOWN_NEG_OVERFLOW); case 1: if (to >= 0) return set_pos_overflow_int(to, dir); return assign_nan(to, V_UNKNOWN_POS_OVERFLOW); default: PPL_ASSERT(false); return V_NAN; } } template inline Result sub_mul_int(Type& to, const Type x, const Type y, Rounding_Dir dir) { Type z; Result r = mul(z, x, y, dir); switch (result_overflow(r)) { case 0: return sub(to, to, z, dir); case -1: if (to >= 0) return set_pos_overflow_int(to, dir); return assign_nan(to, V_UNKNOWN_NEG_OVERFLOW); case 1: if (to <= 0) return set_neg_overflow_int(to, dir); return assign_nan(to, V_UNKNOWN_POS_OVERFLOW); default: PPL_ASSERT(false); return V_NAN; } } template inline Result output_char(std::ostream& os, Type& from, const Numeric_Format&, Rounding_Dir) { os << int(from); return V_EQ; } template 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. */ #ifndef __alpha #include #endif namespace Parma_Polyhedra_Library { namespace Checked { inline float fma(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 fma(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 fma(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 rint(float x) { return ::rintf(x); } #endif inline double rint(double x) { return ::rint(x); } #if PPL_HAVE_DECL_RINTL inline long double rint(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 rint(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 rint(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) { cc_flush(v); } inline void limit_precision(const double& v) { cc_flush(v); } inline void limit_precision(const long double&) { } template inline Result classify_float(const T v, bool nan, bool inf, bool sign) { Float f(v); if ((nan || sign) && CHECK_P(Policy::has_nan, f.u.binary.is_nan())) return V_NAN; if (inf) { int i = CHECK_P(Policy::has_infinity, f.u.binary.is_inf()); if (i < 0) return V_EQ_MINUS_INFINITY; if (i > 0) return V_EQ_PLUS_INFINITY; } if (sign) { if (v < 0) return V_LT; if (v > 0) return V_GT; return V_EQ; } return V_LGE; } template inline bool is_nan_float(const T v) { Float f(v); return CHECK_P(Policy::has_nan, f.u.binary.is_nan()); } template inline int is_inf_float(const T v) { Float f(v); return CHECK_P(Policy::has_infinity, f.u.binary.is_inf()); } template inline bool is_minf_float(const T v) { return is_inf_float(v) < 0; } template inline bool is_pinf_float(const T v) { return is_inf_float(v) > 0; } template inline bool is_int_float(const T v) { return rint(v) == v; } template inline Result assign_special_float(T& v, Result_Class c, Rounding_Dir) { 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_ASSERT(false); return V_NAN | V_UNREPRESENTABLE; } } template inline void pred_float(T& v) { Float f(v); PPL_ASSERT(!f.u.binary.is_nan()); PPL_ASSERT(f.u.binary.is_inf() >= 0); if (f.u.binary.is_zero() > 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 inline void succ_float(T& v) { Float f(v); PPL_ASSERT(!f.u.binary.is_nan()); PPL_ASSERT(f.u.binary.is_inf() <= 0); if (f.u.binary.is_zero() < 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 inline Result round_lt_float(To& to, Rounding_Dir dir) { if (round_down(dir)) { pred_float(to); return V_GT; } return V_LT; } template inline Result round_gt_float(To& to, Rounding_Dir dir) { if (round_up(dir)) { succ_float(to); return V_LT; } return V_GT; } template inline void prepare_inexact(Rounding_Dir dir) { if (Policy::fpu_check_inexact && !round_not_needed(dir) && round_strict_relation(dir)) fpu_reset_inexact(); } template 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 inline Result assign_float_float_exact(To& to, const From from, Rounding_Dir) { if (To_Policy::fpu_check_nan_result && is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); to = from; return V_EQ; } template inline Result assign_float_float_inexact(To& to, const From from, Rounding_Dir dir) { if (To_Policy::fpu_check_nan_result && is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); prepare_inexact(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(dir); } template inline Result assign_float_float(To& to, const From from, Rounding_Dir dir) { if (sizeof(From) > sizeof(To)) return assign_float_float_inexact(to, from, dir); else return assign_float_float_exact(to, from, dir); } template inline Result floor_float(Type& to, const Type from, Rounding_Dir) { if (To_Policy::fpu_check_nan_result && is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); if (fpu_direct_rounding(ROUND_DOWN)) to = rint(from); else if (fpu_inverse_rounding(ROUND_DOWN)) { to = rint(-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 = rint(from); limit_precision(to); fpu_restore_rounding_direction(old); } return V_EQ; } template inline Result ceil_float(Type& to, const Type from, Rounding_Dir) { if (To_Policy::fpu_check_nan_result && is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); if (fpu_direct_rounding(ROUND_UP)) to = rint(from); else if (fpu_inverse_rounding(ROUND_UP)) { to = rint(-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 = rint(from); limit_precision(to); fpu_restore_rounding_direction(old); } return V_EQ; } template inline Result trunc_float(Type& to, const Type from, Rounding_Dir dir) { if (To_Policy::fpu_check_nan_result && is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); if (from >= 0) return floor(to, from, dir); else return ceil(to, from, dir); } template inline Result neg_float(Type& to, const Type from, Rounding_Dir) { if (To_Policy::fpu_check_nan_result && is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); to = -from; return V_EQ; } template 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(x) && x == -y) { return assign_nan(to, V_INF_ADD_INF); } prepare_inexact(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)) return V_NAN; return result_relation(dir); } template 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(x) && x == y) { return assign_nan(to, V_INF_SUB_INF); } prepare_inexact(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)) return V_NAN; return result_relation(dir); } template 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(y)) || (y == 0 && is_inf_float(x)))) { return assign_nan(to, V_INF_MUL_ZERO); } prepare_inexact(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)) return V_NAN; return result_relation(dir); } template 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(x) && is_inf_float(y)) { return assign_nan(to, V_INF_DIV_INF); } if (To_Policy::check_div_zero && y == 0) { return assign_nan(to, V_DIV_ZERO); } prepare_inexact(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)) return V_NAN; return result_relation(dir); } template 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(temp, x, y, dir); if (result_class(r) != VC_NORMAL) { to = temp; return r; } Result r1 = trunc(to, temp, ROUND_NOT_NEEDED); PPL_ASSERT(r1 == V_EQ); if (r == V_EQ || to != temp) return r1; // FIXME: Prove that it's impossibile to return a strict relation return dir == ROUND_UP ? V_LE : V_GE; } template inline Result rem_float(Type& to, const Type x, const Type y, Rounding_Dir) { if (To_Policy::check_inf_mod && is_inf_float(x)) { return assign_nan(to, V_INF_MOD); } if (To_Policy::check_div_zero && y == 0) { return assign_nan(to, V_MOD_ZERO); } to = std::fmod(x, y); if (To_Policy::fpu_check_nan_result && is_nan(to)) return V_NAN; return V_EQ; } struct Float_2exp { const_bool_nodef(has_nan, false); const_bool_nodef(has_infinity, false); }; template 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(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); PPL_ASSERT(exp < sizeof(unsigned long long) * CHAR_BIT); return add(to, x, Type(1ULL << exp), dir); } template 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(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); PPL_ASSERT(exp < sizeof(unsigned long long) * CHAR_BIT); return sub(to, x, Type(1ULL << exp), dir); } template 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(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); PPL_ASSERT(exp < sizeof(unsigned long long) * CHAR_BIT); return mul(to, x, Type(1ULL << exp), dir); } template 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(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); PPL_ASSERT(exp < sizeof(unsigned long long) * CHAR_BIT); return div(to, x, Type(1ULL << exp), dir); } template 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(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); if (To_Policy::check_inf_mod && is_inf_float(x)) { return assign_nan(to, V_INF_MOD); } PPL_ASSERT(exp < sizeof(unsigned long long) * CHAR_BIT); Type m = 1ULL << exp; rem_float(to, x, m, ROUND_IGNORE); Type m2 = m / 2; if (to < -m2) return add_float(to, to, m, dir); else if (to >= m2) return sub_float(to, to, m, dir); return V_EQ; } template 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(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); if (To_Policy::check_inf_mod && is_inf_float(x)) { return assign_nan(to, V_INF_MOD); } PPL_ASSERT(exp < sizeof(unsigned long long) * CHAR_BIT); Type m = 1ULL << exp; rem_float(to, x, m, ROUND_IGNORE); if (to < 0) return add_float(to, to, m, dir); return V_EQ; } template inline Result abs_float(Type& to, const Type from, Rounding_Dir) { if (To_Policy::fpu_check_nan_result && is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); to = std::abs(from); return V_EQ; } template inline Result sqrt_float(Type& to, const Type from, Rounding_Dir dir) { if (To_Policy::fpu_check_nan_result && is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); if (To_Policy::check_sqrt_neg && from < 0) { return assign_nan(to, V_SQRT_NEG); } prepare_inexact(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(dir); } template 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 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 inline Result assign_float_int_inexact(To& to, const From from, Rounding_Dir dir) { prepare_inexact(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(dir); } template inline Result assign_float_int(To& to, const From from, Rounding_Dir dir) { if (sizeof(From) * CHAR_BIT > Float::Binary::MANTISSA_BITS) return assign_float_int_inexact(to, from, dir); else return assign_exact(to, from, dir); } template inline Result set_neg_overflow_float(T& to, Rounding_Dir dir) { switch (round_dir(dir)) { case ROUND_UP: { Float f; f.u.binary.set_max(true); to = f.value(); return V_LT_INF; } default: to = -HUGE_VAL; return V_GT_MINUS_INFINITY; } } template inline Result set_pos_overflow_float(T& to, Rounding_Dir dir) { switch (round_dir(dir)) { case ROUND_DOWN: { Float f; f.u.binary.set_max(false); to = f.value(); return V_GT_SUP; } default: to = HUGE_VAL; return V_LT_PLUS_INFINITY; } } template inline Result assign_float_mpz(T& to, const mpz_class& _from, Rounding_Dir dir) { mpz_srcptr from = _from.get_mpz_t(); int sign = mpz_sgn(from); if (sign == 0) { to = 0; return V_EQ; } size_t exponent = mpz_sizeinbase(from, 2) - 1; if (exponent > size_t(Float::Binary::EXPONENT_MAX)) { if (sign < 0) return set_neg_overflow_float(to, dir); else return set_pos_overflow_float(to, dir); } unsigned long zeroes = mpn_scan1(from->_mp_d, 0); size_t meaningful_bits = exponent - zeroes; mpz_t mantissa; mpz_init(mantissa); if (exponent > Float::Binary::MANTISSA_BITS) mpz_tdiv_q_2exp(mantissa, from, exponent - Float::Binary::MANTISSA_BITS); else mpz_mul_2exp(mantissa, from, Float::Binary::MANTISSA_BITS - exponent); Float f; f.u.binary.build(sign < 0, mantissa, exponent); mpz_clear(mantissa); to = f.value(); if (meaningful_bits > Float::Binary::MANTISSA_BITS) { if (sign < 0) return round_lt_float(to, dir); else return round_gt_float(to, dir); } return V_EQ; } template inline Result assign_float_mpq(T& to, const mpq_class& from, Rounding_Dir dir) { const mpz_class& _num = from.get_num(); const mpz_class& _den = from.get_den(); if (_den == 1) return assign_float_mpz(to, _num, dir); mpz_srcptr num = _num.get_mpz_t(); mpz_srcptr den = _den.get_mpz_t(); int sign = mpz_sgn(num); signed long exponent = mpz_sizeinbase(num, 2) - mpz_sizeinbase(den, 2); if (exponent < Float::Binary::EXPONENT_MIN_DENORM) { to = 0; inexact: if (sign < 0) return round_lt_float(to, dir); else return round_gt_float(to, dir); } if (exponent > int(Float::Binary::EXPONENT_MAX + 1)) { overflow: if (sign < 0) return set_neg_overflow_float(to, dir); else return set_pos_overflow_float(to, dir); } unsigned int needed_bits = Float::Binary::MANTISSA_BITS + 1; if (exponent < Float::Binary::EXPONENT_MIN) needed_bits -= Float::Binary::EXPONENT_MIN - exponent; mpz_t mantissa; mpz_init(mantissa); signed long shift = needed_bits - exponent; if (shift > 0) { mpz_mul_2exp(mantissa, num, shift); num = mantissa; } else if (shift < 0) { mpz_mul_2exp(mantissa, den, -shift); den = mantissa; } mpz_t r; mpz_init(r); mpz_tdiv_qr(mantissa, r, num, den); 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 > int(Float::Binary::EXPONENT_MAX)) { mpz_clear(mantissa); goto overflow; } else if (exponent < Float::Binary::EXPONENT_MIN - 1) { // Denormalized. exponent = Float::Binary::EXPONENT_MIN - 1; } Float f; f.u.binary.build(sign < 0, mantissa, exponent); mpz_clear(mantissa); to = f.value(); if (inexact) goto inexact; return V_EQ; } template 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(y)) || (y == 0 && is_inf_float(x)))) { return assign_nan(to, V_INF_MUL_ZERO); } // FIXME: missing check_inf_add_inf prepare_inexact(dir); if (fpu_direct_rounding(dir)) to = fma(x, y, to); else if (fpu_inverse_rounding(dir)) { to = fma(-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 = fma(x, y, to); limit_precision(to); fpu_restore_rounding_direction(old); } if (To_Policy::fpu_check_nan_result && is_nan(to)) return V_NAN; return result_relation(dir); } template 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(y)) || (y == 0 && is_inf_float(x)))) { return assign_nan(to, V_INF_MUL_ZERO); } // FIXME: missing check_inf_add_inf prepare_inexact(dir); if (fpu_direct_rounding(dir)) to = fma(x, -y, to); else if (fpu_inverse_rounding(dir)) { to = fma(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 = fma(x, -y, to); limit_precision(to); fpu_restore_rounding_direction(old); } if (To_Policy::fpu_check_nan_result && is_nan(to)) return V_NAN; return result_relation(dir); } template inline Result output_float(std::ostream& os, const Type from, const Numeric_Format&, Rounding_Dir) { if (from == 0) os << "0"; else if (is_minf(from)) os << "-inf"; else if (is_pinf(from)) os << "+inf"; else if (is_nan(from)) os << "nan"; else { int old_precision = os.precision(10000); // FIXME: here correctness depends on the behavior of the standard // output operator which, in turn, may depend on the behavior // of printf(). The C99 standard, 7.19.16.1#13, does not give // enough guarantees. We could not find something similar // in the C++ standard, so there is a concrete danger here. os << from; os.precision(old_precision); } 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 namespace Parma_Polyhedra_Library { namespace Checked { template inline Result round_lt_mpz(mpz_class& to, Rounding_Dir dir) { if (round_down(dir)) { --to; return V_GT; } return V_LT; } template inline Result round_gt_mpz(mpz_class& to, Rounding_Dir dir) { if (round_up(dir)) { ++to; return V_LT; } return V_GT; } #ifdef 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 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::min + 1) return V_NAN; if (!inf && !sign) return V_LGE; if (Policy::has_infinity) { if (s == C_Integer::min) return inf ? V_EQ_MINUS_INFINITY : V_LT; if (s == C_Integer::max) return inf ? V_EQ_PLUS_INFINITY : V_GT; } } if (sign) return (Result) sgn(v); return V_LGE; } PPL_SPECIALIZE_CLASSIFY(classify_mpz, mpz_class) template inline bool is_nan_mpz(const mpz_class& v) { return Policy::has_nan && get_mp_size(v) == C_Integer::min + 1; } PPL_SPECIALIZE_IS_NAN(is_nan_mpz, mpz_class) template inline bool is_minf_mpz(const mpz_class& v) { return Policy::has_infinity && get_mp_size(v) == C_Integer::min; } PPL_SPECIALIZE_IS_MINF(is_minf_mpz, mpz_class) template inline bool is_pinf_mpz(const mpz_class& v) { return Policy::has_infinity && get_mp_size(v) == C_Integer::max; } PPL_SPECIALIZE_IS_PINF(is_pinf_mpz, mpz_class) template inline bool is_int_mpz(const mpz_class& v) { return !is_nan(v); } PPL_SPECIALIZE_IS_INT(is_int_mpz, mpz_class) template 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::min + 1); return V_NAN; case VC_MINUS_INFINITY: if (Policy::has_infinity) { set_mp_size(v, C_Integer::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::max); return V_EQ_PLUS_INFINITY; } return V_EQ_PLUS_INFINITY | V_UNREPRESENTABLE; default: PPL_ASSERT(false); return V_NAN; } } PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_mpz, mpz_class) template inline void copy_mpz(mpz_class& to, const mpz_class& from) { if (is_nan_mpz(from)) PPL_ASSERT(To_Policy::has_nan); else if (is_minf_mpz(from) || is_pinf_mpz(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 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 inline Result construct_mpz_float(mpz_class& to, const From& from, Rounding_Dir dir) { if (is_nan(from)) return construct_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(from)) return construct_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(from)) return construct_special(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, dir); else return round_gt_mpz(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 inline Result assign_mpz_signed_int(mpz_class& to, const From from, Rounding_Dir) { if (sizeof(From) <= sizeof(signed long)) to = static_cast(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 inline Result assign_mpz_unsigned_int(mpz_class& to, const From from, Rounding_Dir) { if (sizeof(From) <= sizeof(unsigned long)) to = static_cast(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 inline Result assign_mpz_float(mpz_class& to, const From from, Rounding_Dir dir) { if (is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(from)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(from)) return assign_special(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, dir); if (round_direct(ROUND_DOWN)) return round_gt_mpz(to, dir); if (from < i_from) return round_lt_mpz(to, dir); if (from > i_from) return round_gt_mpz(to, dir); PPL_ASSERT(false); return V_NAN; } PPL_SPECIALIZE_ASSIGN(assign_mpz_float, mpz_class, float) PPL_SPECIALIZE_ASSIGN(assign_mpz_float, mpz_class, double) template inline Result assign_mpz_long_double(mpz_class& to, const From& from, Rounding_Dir dir) { if (is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(from)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(from)) return assign_special(to, VC_PLUS_INFINITY, dir); // FIXME: this is an incredibly inefficient implementation! std::stringstream ss; output(ss, from, Numeric_Format(), dir); PPL_DIRTY_TEMP0(mpq_class, tmp); #ifndef NDEBUG Result r = #endif input_mpq(tmp, ss); PPL_ASSERT(r == V_EQ); return assign(to, tmp, dir); } PPL_SPECIALIZE_ASSIGN(assign_mpz_long_double, mpz_class, long double) template 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; } mpz_srcptr n = from.get_num().get_mpz_t(); 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) ? 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) ? 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 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 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 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 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 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, V_DIV_ZERO); } mpz_srcptr n = x.get_mpz_t(); 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) ? 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) ? V_EQ : V_LT; return V_LE; } } PPL_SPECIALIZE_DIV(div_mpz, mpz_class, mpz_class, mpz_class) template 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, 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 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, V_MOD_ZERO); } to = x % y; return V_EQ; } PPL_SPECIALIZE_REM(rem_mpz, mpz_class, mpz_class, mpz_class) template 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 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 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 inline Result div_2exp_mpz(mpz_class& to, const mpz_class& x, unsigned int exp, Rounding_Dir dir) { 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) ? 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) ? V_EQ : V_LT; return V_LE; } } PPL_SPECIALIZE_DIV_2EXP(div_2exp_mpz, mpz_class, mpz_class) template 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)) 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 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 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 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 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 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 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 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 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, V_SQRT_NEG); } if (round_not_requested(dir)) { to = sqrt(from); return V_GE; } PPL_DIRTY_TEMP0(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, dir); } PPL_SPECIALIZE_SQRT(sqrt_mpz, mpz_class, mpz_class) template inline Result_Relation sgn_mp(const Type& x) { int i = ::sgn(x); return i > 0 ? VR_GT : i < 0 ? VR_LT : VR_EQ; } PPL_SPECIALIZE_SGN(sgn_mp, mpz_class) PPL_SPECIALIZE_SGN(sgn_mp, mpq_class) template 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 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 #include #include namespace Parma_Polyhedra_Library { namespace Checked { template 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 (Result) sgn(v); return V_LGE; } PPL_SPECIALIZE_CLASSIFY(classify_mpq, mpq_class) template 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 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 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 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 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_ASSERT(false); return V_NAN | V_UNREPRESENTABLE; } } PPL_SPECIALIZE_ASSIGN_SPECIAL(assign_special_mpq, mpq_class) PPL_SPECIALIZE_COPY(copy_generic, mpq_class) template 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 inline Result construct_mpq_float(mpq_class& to, const From& from, Rounding_Dir dir) { if (is_nan(from)) return construct_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(from)) return construct_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(from)) return construct_special(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 inline Result assign_mpq_float(mpq_class& to, const From& from, Rounding_Dir dir) { if (is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(from)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(from)) return assign_special(to, VC_PLUS_INFINITY, dir); to = from; return V_EQ; } PPL_SPECIALIZE_ASSIGN(assign_mpq_float, mpq_class, float) PPL_SPECIALIZE_ASSIGN(assign_mpq_float, mpq_class, double) template inline Result assign_mpq_signed_int(mpq_class& to, const From from, Rounding_Dir) { if (sizeof(From) <= sizeof(signed long)) to = static_cast(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 inline Result assign_mpq_unsigned_int(mpq_class& to, const From from, Rounding_Dir) { if (sizeof(From) <= sizeof(unsigned long)) to = static_cast(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 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 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 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 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 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 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 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 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, V_DIV_ZERO); } to = x / y; return V_EQ; } PPL_SPECIALIZE_DIV(div_mpq, mpq_class, mpq_class, mpq_class) template 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, V_DIV_ZERO); } to = x / y; return trunc(to, to, dir); } PPL_SPECIALIZE_IDIV(idiv_mpq, mpq_class, mpq_class, mpq_class) template 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, 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 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 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 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 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 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 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 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 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 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 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, 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_a, from_a, 2*irrational_precision, ROUND_IGNORE); Result rdiv = div(to_a, to_a, from_b, rdir); Result rsqrt = sqrt(to_a, to_a, rdir); to_b = 1; mul_2exp(to_b, to_b, irrational_precision, ROUND_IGNORE); to.canonicalize(); return rdiv != V_EQ ? rdiv : rsqrt; } PPL_SPECIALIZE_SQRT(sqrt_mpq, mpq_class, mpq_class) template 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(to, c, dir); case VC_NAN: return assign_nan(to, r); default: return r; } } PPL_SPECIALIZE_INPUT(input_mpq, mpq_class) template 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) template inline Result assign_mpq_long_double(mpq_class& to, const From& from, Rounding_Dir dir) { if (is_nan(from)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(from)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(from)) return assign_special(to, VC_PLUS_INFINITY, dir); // FIXME: this is an incredibly inefficient implementation! std::stringstream ss; output(ss, from, Numeric_Format(), dir); return input_mpq(to, ss); } PPL_SPECIALIZE_ASSIGN(assign_mpq_long_double, mpq_class, long double) } // 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 INT_MAX, sets the precision parameter used for irrational calculations to \p p. \exception std::invalid_argument Thrown if \p p is greater than INT_MAX. */ 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 struct FPU_Related : public False {}; template <> struct FPU_Related : public True {}; template <> struct FPU_Related : public True {}; template <> struct FPU_Related : public True {}; namespace Checked { template inline bool handle_ext_natively(const T&) { return FPU_Related::value; } template inline bool ext_to_handle(const Type& x) { return !handle_ext_natively(x) && (Policy::has_infinity || Policy::has_nan); } template inline Result_Relation sgn_ext(const Type& x) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return VR_EMPTY; else if (is_minf(x)) return VR_LT; else if (is_pinf(x)) return VR_GT; else { native: return sgn(x); } } template inline Result construct_ext(To& to, const From& x, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return construct_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) return construct_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(x)) return construct_special(to, VC_PLUS_INFINITY, dir); else { native: return construct(to, x, dir); } } template inline Result assign_ext(To& to, const From& x, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(x)) return assign_special(to, VC_PLUS_INFINITY, dir); else { native: return assign(to, x, dir); } } template inline Result neg_ext(To& to, const From& x, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) return assign_special(to, VC_PLUS_INFINITY, dir); else if (is_pinf(x)) return assign_special(to, VC_MINUS_INFINITY, dir); else { native: return neg(to, x, dir); } } template inline Result floor_ext(To& to, const From& x, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(x)) return assign_special(to, VC_PLUS_INFINITY, dir); else { native: return floor(to, x, dir); } } template inline Result ceil_ext(To& to, const From& x, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(x)) return assign_special(to, VC_PLUS_INFINITY, dir); else { native: return ceil(to, x, dir); } } template inline Result trunc_ext(To& to, const From& x, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(x)) return assign_special(to, VC_PLUS_INFINITY, dir); else { native: return trunc(to, x, dir); } } template inline Result abs_ext(To& to, const From& x, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x) || is_pinf(x)) return assign_special(to, VC_PLUS_INFINITY, dir); else { native: return abs(to, x, dir); } } template inline Result add_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) { if (!ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(x) || is_nan(y)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) { if (CHECK_P(To_Policy::check_inf_add_inf, is_pinf(y))) goto inf_add_inf; else goto minf; } else if (is_pinf(x)) { if (CHECK_P(To_Policy::check_inf_add_inf, is_minf(y))) { inf_add_inf: return assign_nan(to, V_INF_ADD_INF); } else goto pinf; } else { if (is_minf(y)) { minf: return assign_special(to, VC_MINUS_INFINITY, dir); } else if (is_pinf(y)) { pinf: return assign_special(to, VC_PLUS_INFINITY, dir); } else { native: return add(to, x, y, dir); } } } template inline Result sub_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) { if (!ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(x) || is_nan(y)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) { if (CHECK_P(To_Policy::check_inf_sub_inf, is_minf(y))) goto inf_sub_inf; else goto minf; } else if (is_pinf(x)) { if (CHECK_P(To_Policy::check_inf_sub_inf, is_pinf(y))) { inf_sub_inf: return assign_nan(to, V_INF_SUB_INF); } else goto pinf; } else { if (is_pinf(y)) { minf: return assign_special(to, VC_MINUS_INFINITY, dir); } else if (is_minf(y)) { pinf: return assign_special(to, VC_PLUS_INFINITY, dir); } else { native: return sub(to, x, y, dir); } } } template inline Result mul_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) { if (!ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(x) || is_nan(y)) return assign_special(to, VC_NAN, ROUND_IGNORE); if (is_minf(x)) { switch (sgn_ext(y)) { case VR_LT: goto pinf; case VR_GT: goto minf; default: goto inf_mul_zero; } } else if (is_pinf(x)) { switch (sgn_ext(y)) { case VR_LT: goto minf; case VR_GT: goto pinf; default: goto inf_mul_zero; } } else { if (is_minf(y)) { switch (sgn(x)) { case VR_LT: goto pinf; case VR_GT: goto minf; default: goto inf_mul_zero; } } else if (is_pinf(y)) { switch (sgn(x)) { case VR_LT: minf: return assign_special(to, VC_MINUS_INFINITY, dir); case VR_GT: pinf: return assign_special(to, VC_PLUS_INFINITY, dir); default: inf_mul_zero: PPL_ASSERT(To_Policy::check_inf_mul_zero); return assign_nan(to, V_INF_MUL_ZERO); } } else { native: return mul(to, x, y, dir); } } } template inline Result add_mul_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) { if (!ext_to_handle(to) && !ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(to) || is_nan(x) || is_nan(y)) return assign_special(to, VC_NAN, ROUND_IGNORE); if (is_minf(x)) { switch (sgn_ext(y)) { case VR_LT: goto a_pinf; case VR_GT: goto a_minf; default: goto inf_mul_zero; } } else if (is_pinf(x)) { switch (sgn_ext(y)) { case VR_LT: goto a_minf; case VR_GT: goto a_pinf; default: goto inf_mul_zero; } } else { if (is_minf(y)) { switch (sgn(x)) { case VR_LT: goto a_pinf; case VR_GT: goto a_minf; default: goto inf_mul_zero; } } else if (is_pinf(y)) { switch (sgn(x)) { case VR_LT: a_minf: if (CHECK_P(To_Policy::check_inf_add_inf, is_pinf(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))) { inf_add_inf: return assign_nan(to, V_INF_ADD_INF); } else goto pinf; default: inf_mul_zero: PPL_ASSERT(To_Policy::check_inf_mul_zero); return assign_nan(to, V_INF_MUL_ZERO); } } else { if (is_minf(to)) { minf: return assign_special(to, VC_MINUS_INFINITY, dir); } if (is_pinf(to)) { pinf: return assign_special(to, VC_PLUS_INFINITY, dir); } native: return add_mul(to, x, y, dir); } } } template inline Result sub_mul_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) { if (!ext_to_handle(to) && !ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(to) || is_nan(x) || is_nan(y)) return assign_special(to, VC_NAN, ROUND_IGNORE); if (is_minf(x)) { switch (sgn_ext(y)) { case VR_LT: goto a_pinf; case VR_GT: goto a_minf; default: goto inf_mul_zero; } } else if (is_pinf(x)) { switch (sgn_ext(y)) { case VR_LT: goto a_minf; case VR_GT: goto a_pinf; default: goto inf_mul_zero; } } else { if (is_minf(y)) { switch (sgn(x)) { case VR_LT: goto a_pinf; case VR_GT: goto a_minf; default: goto inf_mul_zero; } } else if (is_pinf(y)) { switch (sgn(x)) { case VR_LT: a_minf: if (CHECK_P(To_Policy::check_inf_sub_inf, is_minf(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))) { inf_sub_inf: return assign_nan(to, V_INF_SUB_INF); } else goto minf; default: inf_mul_zero: PPL_ASSERT(To_Policy::check_inf_mul_zero); return assign_nan(to, V_INF_MUL_ZERO); } } else { if (is_minf(to)) { minf: return assign_special(to, VC_MINUS_INFINITY, dir); } if (is_pinf(to)) { pinf: return assign_special(to, VC_PLUS_INFINITY, dir); } native: return sub_mul(to, x, y, dir); } } } template inline Result div_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) { if (!ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(x) || is_nan(y)) return assign_special(to, VC_NAN, ROUND_IGNORE); if (is_minf(x)) { if (CHECK_P(To_Policy::check_inf_div_inf, is_minf(y) || is_pinf(y))) goto inf_div_inf; else { switch (sgn(y)) { case VR_LT: goto pinf; case VR_GT: goto minf; default: goto div_zero; } } } else if (is_pinf(x)) { if (CHECK_P(To_Policy::check_inf_div_inf, is_minf(y) || is_pinf(y))) { inf_div_inf: return assign_nan(to, V_INF_DIV_INF); } else { switch (sgn(y)) { case VR_LT: minf: return assign_special(to, VC_MINUS_INFINITY, dir); case VR_GT: pinf: return assign_special(to, VC_PLUS_INFINITY, dir); default: div_zero: PPL_ASSERT(To_Policy::check_div_zero); return assign_nan(to, V_DIV_ZERO); } } } else { if (is_minf(y) || is_pinf(y)) { to = 0; return V_EQ; } else { native: return div(to, x, y, dir); } } } template inline Result idiv_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) { if (!ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(x) || is_nan(y)) return assign_special(to, VC_NAN, ROUND_IGNORE); if (is_minf(x)) { if (CHECK_P(To_Policy::check_inf_div_inf, is_minf(y) || is_pinf(y))) goto inf_div_inf; else { switch (sgn(y)) { case VR_LT: goto pinf; case VR_GT: goto minf; default: goto div_zero; } } } else if (is_pinf(x)) { if (CHECK_P(To_Policy::check_inf_div_inf, is_minf(y) || is_pinf(y))) { inf_div_inf: return assign_nan(to, V_INF_DIV_INF); } else { switch (sgn(y)) { case VR_LT: minf: return assign_special(to, VC_MINUS_INFINITY, dir); case VR_GT: pinf: return assign_special(to, VC_PLUS_INFINITY, dir); default: div_zero: PPL_ASSERT(To_Policy::check_div_zero); return assign_nan(to, V_DIV_ZERO); } } } else { if (is_minf(y) || is_pinf(y)) { to = 0; return V_EQ; } else { native: return idiv(to, x, y, dir); } } } template inline Result rem_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) { if (!ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(x) || is_nan(y)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (CHECK_P(To_Policy::check_inf_mod, is_minf(x) || is_pinf(x))) { return assign_nan(to, V_INF_MOD); } else { if (is_minf(y) || is_pinf(y)) { to = x; return V_EQ; } else { native: return rem(to, x, y, dir); } } } template inline Result add_2exp_ext(To& to, const From& x, unsigned int exp, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(x)) return assign_special(to, VC_PLUS_INFINITY, dir); else { native: return add_2exp(to, x, exp, dir); } } template inline Result sub_2exp_ext(To& to, const From& x, unsigned int exp, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(x)) return assign_special(to, VC_PLUS_INFINITY, dir); else { native: return sub_2exp(to, x, exp, dir); } } template inline Result mul_2exp_ext(To& to, const From& x, unsigned int exp, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(x)) return assign_special(to, VC_PLUS_INFINITY, dir); else { native: return mul_2exp(to, x, exp, dir); } } template inline Result div_2exp_ext(To& to, const From& x, int exp, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) return assign_special(to, VC_MINUS_INFINITY, dir); else if (is_pinf(x)) return assign_special(to, VC_PLUS_INFINITY, dir); else { native: return div_2exp(to, x, exp, dir); } } template inline Result smod_2exp_ext(To& to, const From& x, unsigned int exp, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (CHECK_P(To_Policy::check_inf_mod, is_minf(x) || is_pinf(x))) { return assign_nan(to, V_INF_MOD); } else { native: return smod_2exp(to, x, exp, dir); } } template inline Result umod_2exp_ext(To& to, const From& x, unsigned int exp, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (CHECK_P(To_Policy::check_inf_mod, is_minf(x) || is_pinf(x))) { return assign_nan(to, V_INF_MOD); } else { native: return umod_2exp(to, x, exp, dir); } } template inline Result sqrt_ext(To& to, const From& x, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x)) { return assign_nan(to, V_SQRT_NEG); } else if (is_pinf(x)) return assign_special(to, VC_PLUS_INFINITY, dir); else { native: return sqrt(to, x, dir); } } template inline Result gcd_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) { if (is_nan(x) || is_nan(y)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x) || is_pinf(x)) return abs_ext(to, y, dir); else if (is_minf(y) || is_pinf(y)) return abs_ext(to, x, dir); else return gcd(to, x, y, dir); } template inline Result gcdext_ext(To1& to, To2& s, To3& t, const From1& x, const From2& y, Rounding_Dir dir) { if (is_nan(x) || is_nan(y)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x) || is_pinf(x)) { s = 0; t = y > 0 ? -1 : 1; return abs_ext(to, y, dir); } else if (is_minf(y) || is_pinf(y)) { s = x > 0 ? -1 : 1; t = 0; return abs_ext(to, x, dir); } else return gcdext(to, s, t, x, y, dir); } template inline Result lcm_ext(To& to, const From1& x, const From2& y, Rounding_Dir dir) { if (is_nan(x) || is_nan(y)) return assign_special(to, VC_NAN, ROUND_IGNORE); else if (is_minf(x) || is_pinf(x) || is_minf(y) || is_pinf(y)) return assign_special(to, VC_PLUS_INFINITY, dir); else return lcm(to, x, y, dir); } template inline Result_Relation cmp_ext(const Type1& x, const Type2& y) { if (!ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(x) || is_nan(y)) return VR_EMPTY; else if (is_minf(x)) return is_minf(y) ? VR_EQ : VR_LT; else if (is_pinf(x)) return is_pinf(y) ? VR_EQ : VR_GT; else { if (is_minf(y)) return VR_GT; if (is_pinf(y)) return VR_LT; native: return cmp(x, y); } } template inline bool lt_ext(const Type1& x, const Type2& y) { if (!ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(x) || is_nan(y)) return false; if (is_pinf(x) || is_minf(y)) return false; if (is_minf(x) || is_pinf(y)) return true; native: return lt_p(x, y); } template inline bool gt_ext(const Type1& x, const Type2& y) { return lt_ext(y, x); } template inline bool le_ext(const Type1& x, const Type2& y) { if (!ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(x) || is_nan(y)) return false; if (is_minf(x) || is_pinf(y)) return true; if (is_pinf(x) || is_minf(y)) return false; native: return le_p(x, y); } template inline bool ge_ext(const Type1& x, const Type2& y) { return le_ext(y, x); } template inline bool eq_ext(const Type1& x, const Type2& y) { if (!ext_to_handle(x) && !ext_to_handle(y)) goto native; if (is_nan(x) || is_nan(y)) return false; if (is_minf(x)) return is_minf(y); if (is_pinf(x)) return is_pinf(y); else if (is_minf(y) || is_pinf(y)) return false; native: return eq_p(x, y); } template inline bool ne_ext(const Type1& x, const Type2& y) { return !eq_ext(x, y); } template inline Result output_ext(std::ostream& os, const Type& x, const Numeric_Format& format, Rounding_Dir dir) { if (!ext_to_handle(x)) goto native; if (is_nan(x)) { os << "nan"; return V_NAN; } if (is_minf(x)) { os << "-inf"; return V_EQ; } if (is_pinf(x)) { os << "+inf"; return V_EQ; } native: return output(os, x, format, dir); } template inline Result input_ext(To& to, std::istream& is, Rounding_Dir dir) { return input(to, is, dir); } } // namespace Checked } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/checked.defs.hh line 660. */ #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 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); // Do not uncomment the following. // The compile time error on conversions is the expected behavior. // const_bool_nodef(convertible, false); const_bool_nodef(fpu_check_inexact, true); const_bool_nodef(fpu_check_nan_result, true); // Do not uncomment the following. // The compile time error is the expected behavior. // static const Rounding_Dir ROUND_DEFAULT_CONSTRUCTOR = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_OPERATOR = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_FUNCTION = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_INPUT = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_OUTPUT = ROUND_UP; 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 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::has_quiet_NaN); const_bool_nodef(has_infinity, std::numeric_limits::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 struct Native_Checked_From_Wrapper; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \ingroup PPL_CXX_interface */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template struct Native_Checked_From_Wrapper::value>::type> { typedef Checked_Number_Transparent_Policy 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 struct Native_Checked_From_Wrapper > { typedef P Policy; static const T& raw_value(const Checked_Number& v) { return v.raw_value(); } }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \ingroup PPL_CXX_interface */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template struct Native_Checked_To_Wrapper; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \ingroup PPL_CXX_interface */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template struct Native_Checked_To_Wrapper::value>::type> { typedef Check_Overflow_Policy 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 struct Native_Checked_To_Wrapper > { typedef P Policy; static T& raw_value(Checked_Number& v) { return v.raw_value(); } }; /*! \ingroup PPL_CXX_interface */ template struct Is_Checked : public False { }; /*! \ingroup PPL_CXX_interface */ template struct Is_Checked > : public True { }; /*! \ingroup PPL_CXX_interface */ template struct Is_Native_Or_Checked : public Bool::value || Is_Checked::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 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 Checked_Number(const Checked_Number& 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 Checked_Number(const From&, Rounding_Dir dir, typename Enable_If::value, bool>::type ignored = false); //! Direct initialization from a Checked_Number, default rounding mode. template explicit Checked_Number(const Checked_Number& 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 Checked_Number(const From&, typename Enable_If::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 true; - whether \p *this is a (positive or negative) infinity, if \p inf is true; - the sign of \p *this, if \p sign is true. */ 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 Checked_Number& operator=(const From& y); //! Add and assign operator. template Checked_Number& operator+=(const Checked_Number& y); //! Add and assign operator. Checked_Number& operator+=(const T& y); //! Add and assign operator. template typename Enable_If::value, Checked_Number&>::type operator+=(const From& y); //! Subtract and assign operator. template Checked_Number& operator-=(const Checked_Number& y); //! Subtract and assign operator. Checked_Number& operator-=(const T& y); //! Subtract and assign operator. template typename Enable_If::value, Checked_Number&>::type operator-=(const From& y); //! Multiply and assign operator. template Checked_Number& operator*=(const Checked_Number& y); //! Multiply and assign operator. Checked_Number& operator*=(const T& y); //! Multiply and assign operator. template typename Enable_If::value, Checked_Number&>::type operator*=(const From& y); //! Divide and assign operator. template Checked_Number& operator/=(const Checked_Number& y); //! Divide and assign operator. Checked_Number& operator/=(const T& y); //! Divide and assign operator. template typename Enable_If::value, Checked_Number&>::type operator/=(const From& y); //! Compute remainder and assign operator. template Checked_Number& operator%=(const Checked_Number& y); //! Compute remainder and assign operator. Checked_Number& operator%=(const T& y); //! Compute remainder and assign operator. template typename Enable_If::value, Checked_Number& >::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 struct Slow_Copy > : public Bool::value> {}; /*! \relates Checked_Number */ template typename Enable_If::value, bool>::type is_not_a_number(const T& x); /*! \relates Checked_Number */ template typename Enable_If::value, bool>::type is_minus_infinity(const T& x); /*! \relates Checked_Number */ template typename Enable_If::value, bool>::type is_plus_infinity(const T& x); /*! \relates Checked_Number */ template typename Enable_If::value, int>::type is_infinity(const T& x); /*! \relates Checked_Number */ template typename Enable_If::value, bool>::type is_integer(const T& x); /*! \relates Checked_Number */ template typename Enable_If::value && Is_Special::value, Result>::type construct(To& to, const From& x, Rounding_Dir dir); /*! \relates Checked_Number */ template typename Enable_If::value && Is_Special::value, Result>::type assign_r(To& to, const From& x, Rounding_Dir dir); /*! \relates Checked_Number */ template typename Enable_If::value, Result>::type assign_r(To& to, const char* x, Rounding_Dir dir); /*! \relates Checked_Number */ template typename Enable_If::value, Result>::type assign_r(To& to, char* x, Rounding_Dir dir); #define PPL_DECLARE_FUNC1_A(name) \ template \ typename Enable_If::value \ && Is_Native_Or_Checked::value, \ Result>::type \ 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 Enable_If::value \ && Is_Native_Or_Checked::value, \ Result>::type \ name(To& to, const From& x, 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 Enable_If::value \ && Is_Native_Or_Checked::value \ && Is_Native_Or_Checked::value, \ Result>::type \ 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 Enable_If::value \ && Is_Native_Or_Checked::value \ && Is_Native_Or_Checked::value \ && Is_Native_Or_Checked::value \ && Is_Native_Or_Checked::value, \ Result>::type \ 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 size_t total_memory_in_bytes(const Checked_Number& x); //! Returns the size in bytes of the memory managed by \p x. /*! \relates Checked_Number */ template memory_size_type external_memory_in_bytes(const Checked_Number& x); //@} // Memory Size Inspection Functions //! \name Arithmetic Operators //@{ //! Unary plus operator. /*! \relates Checked_Number */ template Checked_Number operator+(const Checked_Number& x); //! Unary minus operator. /*! \relates Checked_Number */ template Checked_Number operator-(const Checked_Number& x); //! Assigns to \p x largest integral value not greater than \p x. /*! \relates Checked_Number */ template void floor_assign(Checked_Number& x); //! Assigns to \p x largest integral value not greater than \p y. /*! \relates Checked_Number */ template void floor_assign(Checked_Number& x, const Checked_Number& y); //! Assigns to \p x smallest integral value not less than \p x. /*! \relates Checked_Number */ template void ceil_assign(Checked_Number& x); //! Assigns to \p x smallest integral value not less than \p y. /*! \relates Checked_Number */ template void ceil_assign(Checked_Number& x, const Checked_Number& y); //! Round \p x to the nearest integer not larger in absolute value. /*! \relates Checked_Number */ template void trunc_assign(Checked_Number& x); //! Assigns to \p x the value of \p y rounded to the nearest integer not larger in absolute value. /*! \relates Checked_Number */ template void trunc_assign(Checked_Number& x, const Checked_Number& y); //! Assigns to \p x its negation. /*! \relates Checked_Number */ template void neg_assign(Checked_Number& x); //! Assigns to \p x the negation of \p y. /*! \relates Checked_Number */ template void neg_assign(Checked_Number& x, const Checked_Number& y); //! Assigns to \p x its absolute value. /*! \relates Checked_Number */ template void abs_assign(Checked_Number& x); //! Assigns to \p x the absolute value of \p y. /*! \relates Checked_Number */ template void abs_assign(Checked_Number& x, const Checked_Number& y); //! Assigns to \p x the value x + y * z. /*! \relates Checked_Number */ template void add_mul_assign(Checked_Number& x, const Checked_Number& y, const Checked_Number& z); //! Assigns to \p x the value x - y * z. /*! \relates Checked_Number */ template void sub_mul_assign(Checked_Number& x, const Checked_Number& y, const Checked_Number& z); //! Assigns to \p x the greatest common divisor of \p y and \p z. /*! \relates Checked_Number */ template void gcd_assign(Checked_Number& x, const Checked_Number& y, const Checked_Number& 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 void gcdext_assign(Checked_Number& x, Checked_Number& s, Checked_Number& t, const Checked_Number& y, const Checked_Number& z); //! Assigns to \p x the least common multiple of \p y and \p z. /*! \relates Checked_Number */ template void lcm_assign(Checked_Number& x, const Checked_Number& y, const Checked_Number& z); //! Assigns to \p x the value \f$ y \cdot 2^\mathtt{exp} \f$. /*! \relates Checked_Number */ template void mul_2exp_assign(Checked_Number& x, const Checked_Number& y, unsigned int exp); //! Assigns to \p x the value \f$ y / 2^\mathtt{exp} \f$. /*! \relates Checked_Number */ template void div_2exp_assign(Checked_Number& x, const Checked_Number& 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 void exact_div_assign(Checked_Number& x, const Checked_Number& y, const Checked_Number& z); //! Assigns to \p x the integer square root of \p y. /*! \relates Checked_Number */ template void sqrt_assign(Checked_Number& x, const Checked_Number& y); //@} // Arithmetic Operators //! \name Relational Operators and Comparison Functions //@{ //! Equality operator. /*! \relates Checked_Number */ template inline typename Enable_If::value && Is_Native_Or_Checked::value && (Is_Checked::value || Is_Checked::value), bool>::type operator==(const T1& x, const T2& y); template inline typename Enable_If::value && Is_Native_Or_Checked::value, bool>::type equal(const T1& x, const T2& y); //! Disequality operator. /*! \relates Checked_Number */ template inline typename Enable_If::value && Is_Native_Or_Checked::value && (Is_Checked::value || Is_Checked::value), bool>::type operator!=(const T1& x, const T2& y); template inline typename Enable_If::value && Is_Native_Or_Checked::value, bool>::type not_equal(const T1& x, const T2& y); //! Greater than or equal to operator. /*! \relates Checked_Number */ template inline typename Enable_If::value && Is_Native_Or_Checked::value && (Is_Checked::value || Is_Checked::value), bool>::type operator>=(const T1& x, const T2& y); template inline typename Enable_If::value && Is_Native_Or_Checked::value, bool>::type greater_or_equal(const T1& x, const T2& y); //! Greater than operator. /*! \relates Checked_Number */ template inline typename Enable_If::value && Is_Native_Or_Checked::value && (Is_Checked::value || Is_Checked::value), bool>::type operator>(const T1& x, const T2& y); template inline typename Enable_If::value && Is_Native_Or_Checked::value, bool>::type greater_than(const T1& x, const T2& y); //! Less than or equal to operator. /*! \relates Checked_Number */ template inline typename Enable_If::value && Is_Native_Or_Checked::value && (Is_Checked::value || Is_Checked::value), bool>::type operator<=(const T1& x, const T2& y); template inline typename Enable_If::value && Is_Native_Or_Checked::value, bool>::type less_or_equal(const T1& x, const T2& y); //! Less than operator. /*! \relates Checked_Number */ template inline typename Enable_If::value && Is_Native_Or_Checked::value && (Is_Checked::value || Is_Checked::value), bool>::type operator<(const T1& x, const T2& y); template inline typename Enable_If::value && Is_Native_Or_Checked::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 inline typename Enable_If::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 inline typename Enable_If::value && Is_Native_Or_Checked::value, int>::type cmp(const From1& x, const From2& y); //@} // Relational Operators and Comparison Functions //! \name Input-Output Operators //@{ /*! \relates Checked_Number */ template typename Enable_If::value, Result>::type output(std::ostream& os, const T& x, const Numeric_Format& fmt, Rounding_Dir dir); //! Output operator. /*! \relates Checked_Number */ template std::ostream& operator<<(std::ostream& os, const Checked_Number& x); //! Ascii dump for native or checked. template typename Enable_If::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 34976098, -77 and +13; - base-10 integer numbers in scientific notation as 15e2 and 15*^2 (both meaning \f$15 \cdot 10^2 = 1500\f$), 9200e-2 and -18*^+11111111111111111; - base-10 rational numbers in fraction notation as 15/3 and 15/-3; - base-10 rational numbers in fraction/scientific notation as 15/30e-1 (meaning \f$5\f$) and 15*^-3/29e2 (meaning \f$3/580000\f$); - base-10 rational numbers in floating point notation as 71.3 (meaning \f$713/10\f$) and -0.123456 (meaning \f$-1929/15625\f$); - base-10 rational numbers in floating point scientific notation as 2.2e-1 (meaning \f$11/50\f$) and -2.20001*^+3 (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 2^^11 (meaning \f$3\f$), 36^^z (meaning \f$35\f$), 36^^xyz (meaning \f$44027\f$), 2^^11.1 (meaning \f$7/2\f$), 10^^2e3 (meaning \f$2000\f$), 8^^2e3 (meaning \f$1024\f$), 8^^2.1e3 (meaning \f$1088\f$), 8^^20402543.120347e7 (meaning \f$9073863231288\f$), 8^^2.1 (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 e is interpreted as a digit, so that 16^^1e2 (meaning \f$482\f$) is different from 16^^1*^2 (meaning \f$256\f$); - the C-style hexadecimal prefix 0x is interpreted as the Mathematica-style prefix 16^^; - the C-style binary exponent indicator p 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 e is used); - special values like inf and +inf (meaning \f$+\infty\f$), -inf (meaning \f$-\infty\f$), and nan (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 case insensitive, that the syntactic category BDIGIT is further restricted by the current base and that for all bases above 14, any e 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 number : NAN INF : 'inf' | SIGN INF ; | INF | num NAN : 'nan' | num DIV num ; ; SIGN : '-' num : unum | '+' | SIGN unum ; unum : unum1 EXP : 'e' | HEX unum1 | 'p' | base BASE unum1 | '*^' ; ; POINT : '.' unum1 : 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 Enable_If::value, Result>::type input(T& x, std::istream& is, Rounding_Dir dir); //! Input operator. /*! \relates Checked_Number */ template std::istream& operator>>(std::istream& is, Checked_Number& x); //! Ascii load for native or checked. template typename Enable_If::value, bool>::type ascii_load(std::ostream& s, T& t); //@} // Input-Output Operators void throw_result_exception(Result r); template T plus_infinity(); template T minus_infinity(); template T not_a_number(); //! Swaps \p x with \p y. /*! \relates Checked_Number */ template void swap(Checked_Number& x, Checked_Number& y); template struct FPU_Related > : public FPU_Related {}; template void maybe_reset_fpu_inexact(); template 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 #include 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 inline void Checked_Number_Transparent_Policy::handle_result(Result) { } inline void Extended_Number_Policy::handle_result(Result r) { if (result_class(r) == VC_NAN) throw_result_exception(r); } template inline Checked_Number::Checked_Number() : v(0) { } template inline Checked_Number::Checked_Number(const Checked_Number& y) { // TODO: avoid default construction of value member. Checked::copy(v, y.raw_value()); } template template inline Checked_Number ::Checked_Number(const Checked_Number& y, Rounding_Dir dir) { // TODO: avoid default construction of value member. Policy::handle_result(check_result(Checked::assign_ext (v, y.raw_value(), rounding_dir(dir)), dir) ); } template template inline Checked_Number ::Checked_Number(const Checked_Number& y) { // TODO: avoid default construction of value member. Rounding_Dir dir = Policy::ROUND_DEFAULT_CONSTRUCTOR; Policy::handle_result(check_result(Checked::assign_ext (v, y.raw_value(), rounding_dir(dir)), dir)); } // TODO: avoid default construction of value member. #define PPL_DEFINE_CTOR(type) \ template \ inline \ Checked_Number::Checked_Number(const type x, Rounding_Dir dir) { \ Policy::handle_result \ (check_result(Checked::assign_ext > \ (v, x, rounding_dir(dir)), \ dir)); \ } \ template \ inline \ Checked_Number::Checked_Number(const type x) { \ Rounding_Dir dir = Policy::ROUND_DEFAULT_CONSTRUCTOR; \ Policy::handle_result \ (check_result(Checked::assign_ext > \ (v, x, rounding_dir(dir)), \ dir)); \ } #define PPL_COND_0(...) #define PPL_COND_1(...) __VA_ARGS__ #define PPL_COND_(if, ...) PPL_COND_##if(__VA_ARGS__) #define PPL_COND(if, ...) PPL_COND_(if, __VA_ARGS__) 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) PPL_COND(PPL_SUPPORTED_FLOAT, PPL_DEFINE_CTOR(float)) PPL_COND(PPL_SUPPORTED_DOUBLE, PPL_DEFINE_CTOR(double)) PPL_COND(PPL_SUPPORTED_LONG_DOUBLE, PPL_DEFINE_CTOR(long double)) PPL_DEFINE_CTOR(mpq_class&) PPL_DEFINE_CTOR(mpz_class&) #undef PPL_DEFINE_CTOR #undef PPL_COND #undef PPL_COND_ #undef PPL_COND_1 #undef PPL_COND_0 template inline Checked_Number::Checked_Number(const char* x, Rounding_Dir dir) { std::istringstream s(x); Policy::handle_result(check_result(Checked::input(v, s, rounding_dir(dir)), dir)); } template inline Checked_Number::Checked_Number(const char* x) { std::istringstream s(x); Rounding_Dir dir = Policy::ROUND_DEFAULT_CONSTRUCTOR; Policy::handle_result(check_result(Checked::input(v, s, rounding_dir(dir)), dir)); } template template inline Checked_Number::Checked_Number(const From&, Rounding_Dir dir, typename Enable_If::value, bool>::type) { Policy::handle_result(check_result(Checked::assign_special(v, From::vclass, rounding_dir(dir)), dir)); } template template inline Checked_Number::Checked_Number(const From&, typename Enable_If::value, bool>::type) { Rounding_Dir dir = Policy::ROUND_DEFAULT_CONSTRUCTOR; Policy::handle_result(check_result(Checked::assign_special(v, From::vclass, rounding_dir(dir)), dir)); } template inline typename Enable_If::value && Is_Special::value, Result>::type assign_r(To& to, const From&, Rounding_Dir dir) { return check_result(Checked::assign_special ::Policy>(Native_Checked_To_Wrapper::raw_value(to), From::vclass, rounding_dir(dir)), dir); } template inline typename Enable_If::value && Is_Special::value, Result>::type construct(To& to, const From&, Rounding_Dir dir) { return check_result(Checked::construct_special ::Policy>(Native_Checked_To_Wrapper::raw_value(to), From::vclass, rounding_dir(dir)), dir); } template inline typename Enable_If::value, bool>::type is_minus_infinity(const T& x) { return Checked::is_minf ::Policy>(Native_Checked_From_Wrapper::raw_value(x)); } template inline typename Enable_If::value, bool>::type is_plus_infinity(const T& x) { return Checked::is_pinf ::Policy>(Native_Checked_From_Wrapper::raw_value(x)); } template inline typename Enable_If::value, int>::type is_infinity(const T& x) { return is_minus_infinity(x) ? -1 : is_plus_infinity(x) ? 1 : 0; } template inline typename Enable_If::value, bool>::type is_not_a_number(const T& x) { return Checked::is_nan ::Policy>(Native_Checked_From_Wrapper::raw_value(x)); } template inline typename Enable_If::value, bool>::type is_integer(const T& x) { return Checked::is_int ::Policy>(Native_Checked_From_Wrapper::raw_value(x)); } template inline Checked_Number::operator T() const { if (Policy::convertible) return v; } template inline T& Checked_Number::raw_value() { return v; } template inline const T& Checked_Number::raw_value() const { return v; } /*! \relates Checked_Number */ template inline const T& raw_value(const Checked_Number& x) { return x.raw_value(); } /*! \relates Checked_Number */ template inline T& raw_value(Checked_Number& x) { return x.raw_value(); } template inline bool Checked_Number::OK() const { return true; } template inline Result Checked_Number::classify(bool nan, bool inf, bool sign) const { return Checked::classify(v, nan, inf, sign); } template inline bool is_not_a_number(const Checked_Number& x) { return Checked::is_nan(x.raw_value()); } template inline bool is_minus_infinity(const Checked_Number& x) { return Checked::is_minf(x.raw_value()); } template inline bool is_plus_infinity(const Checked_Number& x) { return Checked::is_pinf(x.raw_value()); } /*! \relates Checked_Number */ template inline memory_size_type total_memory_in_bytes(const Checked_Number& x) { return total_memory_in_bytes(x.raw_value()); } /*! \relates Checked_Number */ template inline memory_size_type external_memory_in_bytes(const Checked_Number& x) { return external_memory_in_bytes(x.raw_value()); } /*! \relates Checked_Number */ template inline typename Enable_If::value, Result>::type assign_r(To& to, const char* x, Rounding_Dir dir) { std::istringstream s(x); return check_result(Checked::input ::Policy>(Native_Checked_To_Wrapper::raw_value(to), s, rounding_dir(dir)), dir); } #define PPL_DEFINE_FUNC1_A(name, func) \ template \ inline typename Enable_If::value \ && Is_Native_Or_Checked::value, \ Result>::type \ name(To& to, const From& x, Rounding_Dir dir) { \ return \ check_result(Checked::func \ ::Policy, \ typename Native_Checked_From_Wrapper \ ::Policy>(Native_Checked_To_Wrapper::raw_value(to), \ Native_Checked_From_Wrapper::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 \ inline typename Enable_If::value \ && Is_Native_Or_Checked::value, \ Result>::type \ name(To& to, const From& x, int exp, Rounding_Dir dir) { \ return \ check_result(Checked::func \ ::Policy, \ typename Native_Checked_From_Wrapper \ ::Policy>(Native_Checked_To_Wrapper::raw_value(to), \ Native_Checked_From_Wrapper::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 \ inline typename Enable_If::value \ && Is_Native_Or_Checked::value \ && Is_Native_Or_Checked::value, \ Result>::type \ name(To& to, const From1& x, const From2& y, Rounding_Dir dir) { \ return \ check_result(Checked::func \ ::Policy, \ typename Native_Checked_From_Wrapper \ ::Policy, \ typename Native_Checked_From_Wrapper \ ::Policy>(Native_Checked_To_Wrapper::raw_value(to), \ Native_Checked_From_Wrapper::raw_value(x), \ Native_Checked_From_Wrapper::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 \ inline typename Enable_If::value \ && Is_Native_Or_Checked::value \ && Is_Native_Or_Checked::value \ && Is_Native_Or_Checked::value \ && Is_Native_Or_Checked::value, \ Result>::type \ name(To1& to, To2& s, To3& t, const From1& x, const From2& y, \ Rounding_Dir dir) { \ return \ check_result \ (Checked::func::Policy, \ typename Native_Checked_To_Wrapper::Policy, \ typename Native_Checked_To_Wrapper::Policy, \ typename Native_Checked_From_Wrapper::Policy, \ typename Native_Checked_From_Wrapper::Policy> \ (Native_Checked_To_Wrapper::raw_value(to), \ Native_Checked_To_Wrapper::raw_value(s), \ Native_Checked_To_Wrapper::raw_value(t), \ Native_Checked_From_Wrapper::raw_value(x), \ Native_Checked_From_Wrapper::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 \ inline Checked_Number& \ Checked_Number::f() { \ Policy::handle_result(fun(*this, *this, T(1), \ Policy::ROUND_DEFAULT_OPERATOR)); \ return *this; \ } \ template \ inline Checked_Number \ Checked_Number::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 inline Checked_Number& Checked_Number::operator=(const Checked_Number& y) { Checked::copy(v, y.raw_value()); return *this; } template template inline Checked_Number& Checked_Number::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 \ template \ inline Checked_Number& \ Checked_Number::f(const Checked_Number& y) { \ Policy::handle_result(fun(*this, *this, y, \ Policy::ROUND_DEFAULT_OPERATOR)); \ return *this; \ } \ template \ inline Checked_Number& \ Checked_Number::f(const T& y) { \ Policy::handle_result(fun(*this, *this, y, \ Policy::ROUND_DEFAULT_OPERATOR)); \ return *this; \ } \ template \ template \ inline typename Enable_If::value, \ Checked_Number& >::type \ Checked_Number::f(const From& y) { \ Checked_Number 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 \ inline Checked_Number \ f(const Checked_Number& x, const Checked_Number& y) { \ Checked_Number r; \ Policy::handle_result(fun(r, x, y, Policy::ROUND_DEFAULT_OPERATOR)); \ return r; \ } \ template \ inline \ typename Enable_If::value, Checked_Number >::type \ f(const Type& x, const Checked_Number& y) { \ Checked_Number r(x); \ Policy::handle_result(fun(r, r, y, Policy::ROUND_DEFAULT_OPERATOR)); \ return r; \ } \ template \ inline \ typename Enable_If::value, Checked_Number >::type \ f(const Checked_Number& x, const Type& y) { \ Checked_Number 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 \ inline \ typename Enable_If::value \ && Is_Native_Or_Checked::value \ && (Is_Checked::value || Is_Checked::value), \ bool>::type \ f(const T1& x, const T2& y) { \ return Checked::fun::Policy, \ typename Native_Checked_From_Wrapper::Policy> \ (Native_Checked_From_Wrapper::raw_value(x), \ Native_Checked_From_Wrapper::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 \ inline typename Enable_If::value \ && Is_Native_Or_Checked::value, \ bool>::type \ f(const T1& x, const T2& y) { \ return Checked::fun::Policy, \ typename Native_Checked_From_Wrapper::Policy> \ (Native_Checked_From_Wrapper::raw_value(x), \ Native_Checked_From_Wrapper::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 inline Checked_Number operator+(const Checked_Number& x) { return x; } /*! \relates Checked_Number */ template inline Checked_Number operator-(const Checked_Number& x) { Checked_Number r; Policy::handle_result(neg_assign_r(r, x, Policy::ROUND_DEFAULT_OPERATOR)); return r; } #define PPL_DEFINE_ASSIGN_FUN2_1(f, fun) \ template \ inline void \ f(Checked_Number& x) { \ Policy::handle_result(fun(x, x, Policy::ROUND_DEFAULT_FUNCTION)); \ } #define PPL_DEFINE_ASSIGN_FUN2_2(f, fun) \ template \ inline void \ f(Checked_Number& x, const Checked_Number& y) { \ Policy::handle_result(fun(x, y, Policy::ROUND_DEFAULT_FUNCTION)); \ } #define PPL_DEFINE_ASSIGN_FUN3_3(f, fun) \ template \ inline void \ f(Checked_Number& x, const Checked_Number& y, \ const Checked_Number& z) { \ Policy::handle_result(fun(x, y, z, Policy::ROUND_DEFAULT_FUNCTION)); \ } #define PPL_DEFINE_ASSIGN_FUN5_5(f, fun) \ template \ inline void \ f(Checked_Number& x, \ Checked_Number& s, Checked_Number& t, \ const Checked_Number& y, \ const Checked_Number& 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 \ inline void \ f(Checked_Number& to, \ const Checked_Number& x, unsigned int exp) { \ Policy::handle_result(fun(to, x, 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 inline void exact_div_assign(Checked_Number& x, const Checked_Number& y, const Checked_Number& z) { Policy::handle_result(div_assign_r(x, y, z, ROUND_NOT_NEEDED)); } /*! \relates Checked_Number */ template inline typename Enable_If::value, int>::type sgn(const From& x) { Result_Relation r = Checked::sgn_ext::Policy>(Native_Checked_From_Wrapper::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 inline typename Enable_If::value && Is_Native_Or_Checked::value, int>::type cmp(const From1& x, const From2& y) { Result_Relation r = Checked::cmp_ext::Policy, typename Native_Checked_From_Wrapper::Policy> (Native_Checked_From_Wrapper::raw_value(x), Native_Checked_From_Wrapper::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 Enable_If::value, Result>::type output(std::ostream& os, const T& x, const Numeric_Format& fmt, Rounding_Dir dir) { return check_result(Checked::output_ext::Policy> (os, Native_Checked_From_Wrapper::raw_value(x), fmt, rounding_dir(dir)), dir); } /*! \relates Checked_Number */ template inline std::ostream& operator<<(std::ostream& os, const Checked_Number& x) { Policy::handle_result(output(os, x, Numeric_Format(), ROUND_IGNORE)); return os; } /*! \relates Checked_Number */ template typename Enable_If::value, Result>::type input(T& x, std::istream& is, Rounding_Dir dir) { return check_result(Checked::input_ext::Policy> (Native_Checked_To_Wrapper::raw_value(x), is, rounding_dir(dir)), dir); } /*! \relates Checked_Number */ template inline std::istream& operator>>(std::istream& is, Checked_Number& 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 inline T plus_infinity() { return PLUS_INFINITY; } template inline T minus_infinity() { return MINUS_INFINITY; } template inline T not_a_number() { return NOT_A_NUMBER; } /*! \relates Checked_Number */ template inline void swap(Checked_Number& x, Checked_Number& y) { using std::swap; swap(x.raw_value(), y.raw_value()); } template inline void maybe_reset_fpu_inexact() { if (FPU_Related::value) return fpu_reset_inexact(); } template inline int maybe_check_fpu_inexact() { if (FPU_Related::value) return fpu_check_inexact(); else return 0; } } // 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_numeric_limits.hh line 29. */ #include namespace std { using namespace Parma_Polyhedra_Library; #define PPL_SPECIALIZE_LIMITS_INT(T) \ /*! \brief Partial specialization of std::numeric_limits. */ \ template \ class numeric_limits > \ : public numeric_limits { \ private: \ typedef Checked_Number 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::min; \ return v; \ } \ \ static Type max() { \ Type v; \ v.raw_value() = Checked::Extended_Int::max; \ return v; \ } \ \ static Type infinity() { \ Type v; \ Checked::assign_special(v.raw_value(), VC_PLUS_INFINITY, \ ROUND_IGNORE); \ return v; \ } \ \ static Type quiet_NaN() { \ Type v; \ Checked::assign_special(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 \ struct numeric_limits > \ : public numeric_limits { \ }; #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 class numeric_limits > : public numeric_limits { private: typedef Checked_Number 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(v.raw_value(), VC_PLUS_INFINITY, ROUND_IGNORE); return v; } static Type quiet_NaN() { Type v; Checked::assign_special(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 class numeric_limits > : public numeric_limits { private: typedef Checked_Number 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(v.raw_value(), VC_PLUS_INFINITY, ROUND_IGNORE); return v; } static Type quiet_NaN() { Type v; Checked::assign_special(v.raw_value(), VC_NAN, ROUND_IGNORE); return v; } }; } // namespace std /* 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 #include namespace Parma_Polyhedra_Library { template typename Enable_If::value, void>::type ascii_dump(std::ostream& s, const T& t) { if (std::numeric_limits::is_exact) // An exact datatype: pretty printer is accurate. s << t; else { // An inexact datatype (probably floating point): // first dump its hexadecimal representation ... const std::ios_base::fmtflags old_flags = s.flags(); s << std::hex; const unsigned char* p = reinterpret_cast(&t); for (unsigned i = 0; i < sizeof(T); ++i) { s << std::setw(2) << std::setfill('0'); s << static_cast(p[i]); } s.flags(old_flags); // ... and then pretty print it for readability. s << " (" << t << ")"; } } template typename Enable_If::value, bool>::type ascii_load(std::istream& s, T& t) { if (std::numeric_limits::is_exact) // An exact datatype: input from pretty printed version is accurate. return s >> t; else { // An inexact datatype (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(&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(byte_value); } // ... then read and discard pretty printed value. if (!(s >> str)) return false; const unsigned 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 1053. */ /* Automatically generated from PPL source file ../src/Coefficient.types.hh line 17. */ #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 struct Coefficient_traits_template > { //! The type used for references to const 8 bit checked integers. typedef Checked_Number 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 struct Coefficient_traits_template > { //! The type used for references to const 16 bit checked integers. typedef Checked_Number 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 struct Coefficient_traits_template > { //! The type used for references to const 32 bit checked integers. typedef Checked_Number 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 struct Coefficient_traits_template > { //! The type used for references to const 64 bit checked integers. typedef const Checked_Number& const_reference; }; } // namespace Parma_Polyhedra_Library #endif // defined(PPL_CHECKED_INTEGERS) || defined(PPL_NATIVE_INTEGERS) #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 17. */ #include /* Automatically generated from PPL source file ../src/GMP_Integer.types.hh line 19. */ 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 mpz_class type defined in the C++ interface of the GMP library. For more information, see http://www.swox.com/gmp/ */ 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 { //! 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 150. */ #endif 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 README.configure), a Coefficient may actually be: - The GMP_Integer type, which in turn is an alias for the mpz_class 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_traits; } // namespace Parma_Polyhedra_Library /* 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/Constraint.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Constraint; } // namespace Parma_Polyhedra_Library /* 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/Congruence.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Congruence; } /* 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/Scalar_Products.defs.hh line 35. */ #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! A class implementing various scalar product functions. /*! \ingroup PPL_CXX_interface When computing the scalar product of (Linear_Row or Constraint or Generator) objects x and y, it is assumed that the space dimension of the first object x is less than or equal to the space dimension of the second object y. */ #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_Row& x, const Linear_Row& 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& g); //! Computes the scalar product of \p g and \p cg and assigns it to \p z. static void assign(Coefficient& z, const Grid_Generator& g, 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& g); //! Returns the sign of the scalar product between \p x and \p y. static int sign(const Linear_Row& x, const Linear_Row& 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_Row& x, const Linear_Row& 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& g, 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_Row& x, const Linear_Row& 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_Row& x, const Linear_Row& 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 g 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& g, 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& g, 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_Row& x, const Linear_Row& 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 (*SPS_type)(const Linear_Row&, const Linear_Row&); //! The scalar product sign function pointer. SPS_type sps_fp; }; /* 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/Linear_Row.defs.hh line 1. */ /* Linear_Row class declaration. */ /* Automatically generated from PPL source file ../src/Row.defs.hh line 1. */ /* Row class declaration. */ /* Automatically generated from PPL source file ../src/Row.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Row_Impl_Handler; class Row; } // 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.defs.hh line 28. */ #include #if defined(PPL_CHECKED_INTEGERS) || defined(PPL_NATIVE_INTEGERS) /* Automatically generated from PPL source file ../src/Coefficient.defs.hh line 33. */ #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 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 Memory Size Inspection Functions //@{ #ifndef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns the total size in bytes of the memory occupied by \p x. /*! \relates GMP_Integer */ #endif // !defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) memory_size_type total_memory_in_bytes(const GMP_Integer& x); #ifndef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns the size in bytes of the memory managed by \p x. /*! \relates GMP_Integer */ #endif // !defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) memory_size_type external_memory_in_bytes(const GMP_Integer& x); //@} // Memory Size Inspection 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 x + y * z. /*! \relates GMP_Integer */ void add_mul_assign(GMP_Integer& x, const GMP_Integer& y, const GMP_Integer& z); //! Assigns to \p x the value x - y * z. /*! \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 150. */ /* Automatically generated from PPL source file ../src/Coefficient.defs.hh line 37. */ #endif 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 60. */ /* Automatically generated from PPL source file ../src/Row.defs.hh line 30. */ #include #include #ifndef PPL_ROW_EXTRA_DEBUG #ifdef PPL_ABI_BREAKING_EXTRA_DEBUG #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief Enables extra debugging information for class Row. \ingroup PPL_CXX_interface When PPL_ROW_EXTRA_DEBUG evaluates to true, each instance of the class Row carries its own capacity; this enables extra consistency checks to be performed. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) #define PPL_ROW_EXTRA_DEBUG 1 #else // !defined(PPL_ABI_BREAKING_EXTRA_DEBUG) #define PPL_ROW_EXTRA_DEBUG 0 #endif // !defined(PPL_ABI_BREAKING_EXTRA_DEBUG) #endif // !defined(PPL_ROW_EXTRA_DEBUG) #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! The handler of the actual 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) class Parma_Polyhedra_Library::Row_Impl_Handler { public: //! Default constructor. Row_Impl_Handler(); //! Destructor. ~Row_Impl_Handler(); class Impl; //! A pointer to the actual implementation. Impl* impl; #if PPL_ROW_EXTRA_DEBUG //! The capacity of \p impl (only available during debugging). dimension_type capacity_; #endif // PPL_ROW_EXTRA_DEBUG private: //! Private and unimplemented: copy construction is not allowed. Row_Impl_Handler(const Row_Impl_Handler&); //! Private and unimplemented: copy assignment is not allowed. Row_Impl_Handler& operator=(const Row_Impl_Handler&); }; #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::Row : private Row_Impl_Handler { public: #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief Wrapper class to represent a set of flags with bits in a native unsigned integral type. \ingroup PPL_CXX_interface */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) class Flags { public: //! Constructs an object with all the flags unset. Flags(); //! Returns true if and only if \p *this and \p y are equal. bool operator==(const Flags& y) const; /*! \brief Returns true if and only if \p *this and \p y are different. */ bool operator!=(const Flags& y) const; PPL_OUTPUT_DECLARATIONS //! Uses the ASCII Flags representation from \p s to recreate *this. /*! Returns true if successful, false otherwise. The ASCII representation is as output by \ref Parma_Polyhedra_Library::Row::Flags::ascii_dump(). */ bool ascii_load(std::istream& s); protected: //! A native integral type holding the bits that encode the flags. typedef unsigned int base_type; //! Index of the first bit derived classes can use. static const unsigned first_free_bit = 0; //! Total number of bits that can be stored. static const unsigned num_bits = std::numeric_limits::digits; //! Constructs an object with flags set as in \p n. explicit Flags(base_type n); //! Returns the integer encoding \p *this. base_type get_bits() const; //! Sets the bits in \p mask. void set_bits(base_type mask); //! Resets the bits in \p mask. void reset_bits(base_type mask); /*! \brief Returns true if and only if all the bits in \p mask are set. */ bool test_bits(base_type mask) const; private: //! The integer encoding \p *this. base_type bits; friend class Row; }; //! Pre-constructs a row: construction must be completed by construct(). Row(); //! \name Post-constructors //@{ //! Constructs properly a default-constructed element. /*! Builds a row with size and capacity \p sz and flags \p f. */ void construct(dimension_type sz, Flags f); //! Constructs properly a default-constructed element. /*! \param sz The size of the row that will be constructed; \param capacity The capacity of the row that will be constructed; \param f Flags for 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. The row flags are set to \p f. */ void construct(dimension_type sz, dimension_type capacity, Flags f); //@} // Post-constructors //! Tight constructor: resizing may require reallocation. /*! Constructs a row with size and capacity \p sz, and flags \p f. */ Row(dimension_type sz, Flags f); //! 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; \param f Flags for 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. The row flags are set to \p f. */ Row(dimension_type sz, dimension_type capacity, Flags f); //! Ordinary copy constructor. Row(const Row& y); //! Copy constructor with specified capacity. /*! It is assumed that \p capacity is greater than or equal to the size of \p y. */ Row(const 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. */ Row(const Row& y, dimension_type sz, dimension_type capacity); //! Destructor. ~Row(); //! Assignment operator. Row& operator=(const Row& y); //! Swaps \p *this with \p y. void swap(Row& y); //! Assigns the implementation of \p y to \p *this. /*! To be used with extra care, since it may easily cause memory leaks or undefined behavior. */ void assign(Row& y); /*! \brief Allocates memory for a default constructed Row object, setting flags to \p f and 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 Row object is zero. */ void allocate(dimension_type capacity, Flags f); //! 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 a const reference to the flags of \p *this. const Flags& flags() const; //! Returns a non-const reference to the flags of \p *this. Flags& flags(); //! Returns the size() of the largest possible 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(); 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 true if successful, false 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 and capacity match the values provided as arguments. */ bool OK(dimension_type row_size, dimension_type row_capacity) const; private: //! Exception-safe copy construction mechanism for coefficients. void copy_construct_coefficients(const Row& y); #if PPL_ROW_EXTRA_DEBUG //! Returns the capacity of the row (only available during debugging). dimension_type capacity() const; #endif // PPL_ROW_EXTRA_DEBUG }; namespace Parma_Polyhedra_Library { //! Returns true if and only if \p x and \p y are equal. /*! \relates Row */ bool operator==(const Row& x, const Row& y); //! Returns true if and only if \p x and \p y are different. /*! \relates Row */ bool operator!=(const Row& x, const Row& y); } // namespace Parma_Polyhedra_Library namespace std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Row */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void swap(Parma_Polyhedra_Library::Row& x, Parma_Polyhedra_Library::Row& y); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::iter_swap. /*! \relates Parma_Polyhedra_Library::Row */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void iter_swap(std::vector::iterator x, std::vector::iterator y); } // namespace std #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! The actual implementation of a Row object. /*! \ingroup PPL_CXX_interface The class Row_Impl_Handler::Impl provides the implementation of Row objects and, in particular, of the corresponding memory allocation functions. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) class Parma_Polyhedra_Library::Row_Impl_Handler::Impl { public: //! \name Custom allocator and deallocator //@{ //! Placement allocation function. /*! Allocates a chunk of memory able to contain \p capacity Coefficient objects beyond the specified \p fixed_size and returns a pointer to the newly allocated memory. */ static void* operator new(size_t fixed_size, dimension_type capacity); //! Usual (non-placement) deallocation function. /*! Uses the standard delete operator to free the memory \p p points to. \note The definition of this custom deallocation function is required since otherwise the placement deallocation function static void operator delete(void* p, dimension_type capacity); would be wrongly interpreted as a usual (non-placement) deallocation function (see C++98 3.7.3.2p2). This happens because \c dimension_type is just an alias for \c std::size_t. See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42115 */ static void operator delete(void* p); //! Placement deallocation function. /*! Uses the standard operator delete to free the memory \p p points to. */ static void operator delete(void* p, dimension_type capacity); //@} // Custom allocator and deallocator //! Constructor. Impl(Row::Flags f); //! Destructor. /*! Uses shrink() 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); //! Returns the size() of the largest possible Impl. static dimension_type max_size(); //! \name Flags accessors //@{ //! Returns a const reference to the flags of \p *this. const Row::Flags& flags() const; //! Returns a non-const reference to the flags of \p *this. Row::Flags& flags(); //@} // Flags accessors //! \name Size accessors //@{ //! Returns the actual size of \p this. dimension_type size() const; //! Sets to \p new_size the actual size of \p *this. void set_size(dimension_type new_size); //! Increment the size of \p *this by 1. void bump_size(); //@} // Size accessors //! \name Subscript operators //@{ //! Returns a reference to the element of \p *this indexed by \p k. Coefficient& operator[](dimension_type k); //! Returns a constant reference to the element of \p *this indexed by \p k. Coefficient_traits::const_reference operator[](dimension_type k) const; //@} // Subscript operators /*! \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: //! The number of coefficients in the row. dimension_type size_; //! The flags of this row. Row::Flags flags_; //! The vector of coefficients. Coefficient vec_[ #if !PPL_CXX_SUPPORTS_FLEXIBLE_ARRAYS 1 #endif ]; //! Private and unimplemented: default construction is not allowed. Impl(); //! Private and unimplemented: copy construction is not allowed. Impl(const Impl& y); //! Private and unimplemented: assignment is not allowed. Impl& operator=(const Impl&); }; /* Automatically generated from PPL source file ../src/Row.inlines.hh line 1. */ /* Row 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 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 Enable_If::value, void>::type numer_denom(const T& from, Coefficient& num, Coefficient& den); #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 Enable_If::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 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 void max_assign(N& x, const N& y); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true if and only if \p x is an even number. #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template typename Enable_If::value, bool>::type is_even(const T& x); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true if and only if \f$x = -y\f$. #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template typename Enable_If::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 nx and \p ny, respectively. \note \p x and \p nx may be the same object and likewise for \p y and \p ny. Any other aliasing results in undefined behavior. */ #endif void normalize2(Coefficient_traits::const_reference x, Coefficient_traits::const_reference y, Coefficient& nx, Coefficient& ny); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true 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 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 28. */ #include /* Automatically generated from PPL source file ../src/math_utilities.inlines.hh line 30. */ namespace Parma_Polyhedra_Library { inline void normalize2(Coefficient_traits::const_reference x, Coefficient_traits::const_reference y, Coefficient& nx, Coefficient& ny) { PPL_DIRTY_TEMP_COEFFICIENT(gcd); gcd_assign(gcd, x, y); exact_div_assign(nx, x, gcd); exact_div_assign(ny, y, gcd); } template inline T low_bits_mask(const unsigned n) { PPL_ASSERT(n < unsigned(std::numeric_limits::digits)); return n == 0 ? 0 : ~(~(T(0u)) << n); } template inline typename Enable_If::value, void>::type numer_denom(const T& from, Coefficient& num, Coefficient& den) { PPL_ASSERT(!is_not_a_number(from) && !is_minus_infinity(from) && !is_plus_infinity(from)); PPL_DIRTY_TEMP0(mpq_class, q); assign_r(q, from, ROUND_NOT_NEEDED); num = q.get_num(); den = q.get_den(); } template inline typename Enable_If::value, void>::type div_round_up(T& to, Coefficient_traits::const_reference x, Coefficient_traits::const_reference y) { PPL_DIRTY_TEMP0(mpq_class, qx); PPL_DIRTY_TEMP0(mpq_class, qy); // Note: this code assumes that a Coefficient is always convertible // to an mpq_class without loss of precision. assign_r(qx, x, ROUND_NOT_NEEDED); assign_r(qy, y, ROUND_NOT_NEEDED); div_assign_r(qx, qx, qy, ROUND_NOT_NEEDED); assign_r(to, qx, ROUND_UP); } template inline void min_assign(N& x, const N& y) { if (x > y) x = y; } template inline void max_assign(N& x, const N& y) { if (x < y) x = y; } template inline typename Enable_If::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 inline typename Enable_If::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_TEMP0(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/Row.inlines.hh line 28. */ #include #include #include namespace Parma_Polyhedra_Library { inline Row::Flags::Flags() : bits(0) { } inline Row::Flags::Flags(base_type n) : bits(n) { } inline Row::Flags::base_type Row::Flags::get_bits() const { return bits; } inline void Row::Flags::set_bits(const base_type mask) { bits |= mask; } inline void Row::Flags::reset_bits(const base_type mask) { bits &= ~mask; } inline bool Row::Flags::test_bits(const base_type mask) const { return (bits & mask) == mask; } inline bool Row::Flags::operator==(const Flags& y) const { base_type mask = low_bits_mask(first_free_bit); return (get_bits() & mask) == (y.get_bits() & mask); } inline bool Row::Flags::operator!=(const Flags& y) const { return !operator==(y); } inline void* Row_Impl_Handler::Impl::operator new(const size_t fixed_size, const dimension_type capacity) { #if PPL_CXX_SUPPORTS_FLEXIBLE_ARRAYS return ::operator new(fixed_size + capacity*sizeof(Coefficient)); #else PPL_ASSERT(capacity >= 1); return ::operator new(fixed_size + (capacity-1)*sizeof(Coefficient)); #endif } inline void Row_Impl_Handler::Impl::operator delete(void* p) { ::operator delete(p); } inline void Row_Impl_Handler::Impl::operator delete(void* p, dimension_type) { ::operator delete(p); } inline dimension_type Row_Impl_Handler::Impl::max_size() { return std::numeric_limits::max() / sizeof(Coefficient); } inline dimension_type Row_Impl_Handler::Impl::size() const { return size_; } inline void Row_Impl_Handler::Impl::set_size(const dimension_type new_size) { size_ = new_size; } inline void Row_Impl_Handler::Impl::bump_size() { ++size_; } inline Row_Impl_Handler::Impl::Impl(const Row::Flags f) : size_(0), flags_(f) { } inline Row_Impl_Handler::Impl::~Impl() { shrink(0); } inline const Row::Flags& Row_Impl_Handler::Impl::flags() const { return flags_; } inline Row::Flags& Row_Impl_Handler::Impl::flags() { return flags_; } inline Coefficient& Row_Impl_Handler::Impl::operator[](const dimension_type k) { PPL_ASSERT(k < size()); return vec_[k]; } inline Coefficient_traits::const_reference Row_Impl_Handler::Impl::operator[](const dimension_type k) const { PPL_ASSERT(k < size()); return vec_[k]; } inline memory_size_type Row_Impl_Handler::Impl::total_memory_in_bytes(dimension_type capacity) const { return sizeof(*this) + capacity*sizeof(Coefficient) #if !PPL_CXX_SUPPORTS_FLEXIBLE_ARRAYS - 1*sizeof(Coefficient) #endif + external_memory_in_bytes(); } inline memory_size_type Row_Impl_Handler::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_); } inline dimension_type Row::max_size() { return Impl::max_size(); } inline dimension_type Row::size() const { return impl->size(); } inline const Row::Flags& Row::flags() const { return impl->flags(); } inline Row::Flags& Row::flags() { return impl->flags(); } #if PPL_ROW_EXTRA_DEBUG inline dimension_type Row::capacity() const { return capacity_; } #endif inline Row_Impl_Handler::Row_Impl_Handler() : impl(0) { #if PPL_ROW_EXTRA_DEBUG capacity_ = 0; #endif } inline Row_Impl_Handler::~Row_Impl_Handler() { delete impl; } inline Row::Row() : Row_Impl_Handler() { } inline void Row::allocate( #if PPL_CXX_SUPPORTS_FLEXIBLE_ARRAYS const #endif dimension_type capacity, const Flags f) { PPL_ASSERT(capacity <= max_size()); #if !PPL_CXX_SUPPORTS_FLEXIBLE_ARRAYS if (capacity == 0) ++capacity; #endif PPL_ASSERT(impl == 0); impl = new (capacity) Impl(f); #if PPL_ROW_EXTRA_DEBUG PPL_ASSERT(capacity_ == 0); capacity_ = capacity; #endif } inline void Row::expand_within_capacity(const dimension_type new_size) { PPL_ASSERT(impl); #if PPL_ROW_EXTRA_DEBUG PPL_ASSERT(new_size <= capacity_); #endif impl->expand_within_capacity(new_size); } inline void Row::copy_construct_coefficients(const Row& y) { PPL_ASSERT(impl && y.impl); #if PPL_ROW_EXTRA_DEBUG PPL_ASSERT(y.size() <= capacity_); #endif impl->copy_construct_coefficients(*(y.impl)); } inline void Row::construct(const dimension_type sz, const dimension_type capacity, const Flags f) { PPL_ASSERT(sz <= capacity && capacity <= max_size()); allocate(capacity, f); expand_within_capacity(sz); } inline void Row::construct(const dimension_type sz, const Flags f) { construct(sz, sz, f); } inline Row::Row(const dimension_type sz, const dimension_type capacity, const Flags f) : Row_Impl_Handler() { construct(sz, capacity, f); } inline Row::Row(const dimension_type sz, const Flags f) : Row_Impl_Handler() { construct(sz, f); } inline Row::Row(const Row& y) : Row_Impl_Handler() { if (y.impl) { allocate(compute_capacity(y.size(), max_size()), y.flags()); copy_construct_coefficients(y); } } inline Row::Row(const Row& y, const dimension_type capacity) : Row_Impl_Handler() { PPL_ASSERT(y.impl); PPL_ASSERT(y.size() <= capacity && capacity <= max_size()); allocate(capacity, y.flags()); copy_construct_coefficients(y); } inline Row::Row(const Row& y, const dimension_type sz, const dimension_type capacity) : Row_Impl_Handler() { PPL_ASSERT(y.impl); PPL_ASSERT(y.size() <= sz && sz <= capacity && capacity <= max_size()); allocate(capacity, y.flags()); copy_construct_coefficients(y); expand_within_capacity(sz); } inline Row::~Row() { } inline void Row::shrink(const dimension_type new_size) { PPL_ASSERT(impl); impl->shrink(new_size); } inline void Row::swap(Row& y) { std::swap(impl, y.impl); #if PPL_ROW_EXTRA_DEBUG std::swap(capacity_, y.capacity_); #endif } inline void Row::assign(Row& y) { impl = y.impl; #if PPL_ROW_EXTRA_DEBUG capacity_ = y.capacity_; #endif } inline Row& Row::operator=(const Row& y) { // Copy-construct `tmp' from `y'. Row tmp(y); // Swap the implementation of `*this' with the one of `tmp'. swap(tmp); // Now `tmp' goes out of scope, so the old `*this' will be destroyed. return *this; } inline Coefficient& Row::operator[](const dimension_type k) { PPL_ASSERT(impl); return (*impl)[k]; } inline Coefficient_traits::const_reference Row::operator[](const dimension_type k) const { PPL_ASSERT(impl); return (*impl)[k]; } inline memory_size_type Row::external_memory_in_bytes(dimension_type capacity) const { return impl->total_memory_in_bytes(capacity); } inline memory_size_type Row::total_memory_in_bytes(dimension_type capacity) const { return sizeof(*this) + external_memory_in_bytes(capacity); } inline memory_size_type Row::external_memory_in_bytes() const { #if PPL_ROW_EXTRA_DEBUG return impl->total_memory_in_bytes(capacity_); #else return impl->total_memory_in_bytes(); #endif } inline memory_size_type Row::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } /*! \relates Row */ inline bool operator!=(const Row& x, const Row& y) { return !(x == y); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Row */ inline void swap(Parma_Polyhedra_Library::Row& x, Parma_Polyhedra_Library::Row& y) { x.swap(y); } /*! \relates Parma_Polyhedra_Library::Row */ inline void iter_swap(std::vector::iterator x, std::vector::iterator y) { swap(*x, *y); } } // namespace std /* Automatically generated from PPL source file ../src/Row.defs.hh line 521. */ /* Automatically generated from PPL source file ../src/Topology.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/Linear_Row.defs.hh line 34. */ #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! The base class for linear expressions, constraints and generators. /*! \ingroup PPL_CXX_interface The class Linear_Row allows us to build objects of the form \f$[b, a_0, \ldots, a_{d-1}]_{(t, k)}\f$, i.e., a finite sequence of coefficients subscripted by a pair of flags, which are both stored in a Linear_Row::Flags object. The flag \f$t \in \{ \mathrm{c}, \mathrm{nnc} \}\f$ represents the topology and the flag \f$k \in \{\mathord{=}, \mathord{\geq} \}\f$ represents the kind of the Linear_Row object. Note that, even though all the four possible combinations of topology and kind values will result in a legal Linear_Row::Flags object, some of these pose additional constraints on the values of the Linear_Row's coefficients. When \f$t = c\f$, we have the following cases (\f$d\f$ is the dimension of the vector space): - \f$[b, a_0, \ldots, a_{d-1}]_{(c,=)}\f$ represents the equality constraint \f$\sum_{i=0}^{d-1} a_i x_i + b = 0\f$. - \f$[b, a_0, \ldots, a_{d-1}]_{(c,\geq)}\f$ represents the non-strict inequality constraint \f$\sum_{i=0}^{d-1} a_i x_i + b \geq 0\f$. - \f$[0, a_0, \ldots, a_{d-1}]_{(c,=)}\f$ represents the line of direction \f$\vect{l} = (a_0, \ldots, a_{d-1})^\transpose\f$. - \f$[0, a_0, \ldots, a_{d-1}]_{(c,\geq)}\f$ represents the ray of direction \f$\vect{r} = (a_0, \ldots, a_{d-1})^\transpose\f$. - \f$[b, a_0, \ldots, a_{d-1}]_{(c,\geq)}\f$, with \f$b > 0\f$, represents the point \f$\vect{p} = (\frac{a_0}{b}, \ldots, \frac{a_{d-1}}{b})^\transpose\f$. When \f$t = \mathrm{nnc}\f$, the last coefficient of the Linear_Row is associated to the slack variable \f$\epsilon\f$, so that we have the following cases (\f$d\f$ is again the dimension of the vector space, but this time we have \f$d+2\f$ coefficients): - \f$[b, a_0, \ldots, a_{d-1}, 0]_{(\mathrm{nnc},=)}\f$ represents the equality constraint \f$\sum_{i=0}^{d-1} a_i x_i + b = 0\f$. - \f$[b, a_0, \ldots, a_{d-1}, 0]_{(\mathrm{nnc},\geq)}\f$ represents the non-strict inequality constraint \f$\sum_{i=0}^{d-1} a_i x_i + b \geq 0\f$. - \f$[b, a_0, \ldots, a_{d-1}, e]_{(\mathrm{nnc},\geq)}\f$, with \f$e < 0\f$, represents the strict inequality constraint \f$\sum_{i=0}^{d-1} a_i x_i + b > 0\f$. - \f$[0, a_0, \ldots, a_{d-1}, 0]_{(\mathrm{nnc},=)}\f$ represents the line of direction \f$\vect{l} = (a_0, \ldots, a_{d-1})^\transpose\f$. - \f$[0, a_0, \ldots, a_{d-1}, 0]_{(\mathrm{nnc},\geq)}\f$ represents the ray of direction \f$\vect{r} = (a_0, \ldots, a_{d-1})^\transpose\f$. - \f$[b, a_0, \ldots, a_{d-1}, e]_{(\mathrm{nnc},\geq)}\f$, with \f$b > 0\f$ and \f$e > 0\f$, represents the point \f$\vect{p} = (\frac{a_0}{b}, \ldots, \frac{a_{d-1}}{b})^\transpose\f$. - \f$[b, a_0, \ldots, a_{d-1}, 0]_{(\mathrm{nnc},\geq)}\f$, with \f$b > 0\f$, represents the closure point \f$\vect{c} = (\frac{a_0}{b}, \ldots, \frac{a_{d-1}}{b})^\transpose\f$. So, a Linear_Row can be both a constraint and a generator: it can be an equality, a strict or non-strict inequality, a line, a ray, a point or a closure point. The inhomogeneous term of a constraint can be zero or different from zero. Points and closure points must have a positive inhomogeneous term (which is used as a common divisor for all the other coefficients), lines and rays must have the inhomogeneous term equal to zero. If needed, the coefficients of points and closure points are negated at creation time so that they satisfy this invariant. The invariant is maintained because, when combining a point or closure point with another generator, we only consider positive combinations. The \f$\epsilon\f$ coefficient, when present, is negative for strict inequality constraints, positive for points and equal to zero in all the other cases. Note that the above description corresponds to the end-user, high-level view of a Linear_Row object. In the implementation, to allow for code reuse, it is sometimes useful to regard an \f$\mathrm{nnc}\f$-object on the vector space \f$\Rset^d\f$ as if it was a \f$\mathrm{c}\f$-object on the vector space \f$\Rset^{d+1}\f$, therefore interpreting the slack variable \f$\epsilon\f$ as an ordinary dimension of the vector space. A Linear_Row object implementing a Linear_Expression is always of the form \f$[0, a_0, \ldots, a_{d-1}]_{(c,=)}\f$, which represents the linear expression \f$\sum_{i=0}^{d-1} a_i x_i\f$. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) class Parma_Polyhedra_Library::Linear_Row : public Row { public: //! The possible kinds of Linear_Row objects. enum Kind { LINE_OR_EQUALITY = 0, RAY_OR_POINT_OR_INEQUALITY = 1 }; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief The type of the object to which the coefficients refer to, encoding both topology and kind. \ingroup PPL_CXX_interface This combines the information about the topology (necessarily closed or not) and the kind (line/equality or ray/point/inequality) of a Linear_Row object. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) class Flags : public Row::Flags { public: //! Default constructor: builds an object where all flags are invalid. Flags(); //! Builds an object corresponding to the topology \p t. explicit Flags(Topology t); //! Builds an object corresponding to the topology \p t and kind \p k. Flags(Topology t, Kind k); //! \name Testing and setting the type //@{ Topology topology() const; bool is_necessarily_closed() const; bool is_not_necessarily_closed() const; bool is_line_or_equality() const; bool is_ray_or_point_or_inequality() const; void set_necessarily_closed(); void set_not_necessarily_closed(); void set_is_line_or_equality(); void set_is_ray_or_point_or_inequality(); //@} // Testing and setting the type //! Returns true if and only if \p *this and \p y are equal. bool operator==(const Flags& y) const; /*! \brief Returns true if and only if \p *this and \p y are different. */ bool operator!=(const Flags& 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 true if successful, false otherwise. */ bool ascii_load(std::istream& s); private: //! Builds the type from a bit-mask. explicit Flags(base_type mask); //! \name The bits that are currently in use //@{ // NB: ascii_load assumes that these are sequential. static const unsigned rpi_validity_bit = Row::Flags::first_free_bit + 0; static const unsigned rpi_bit = Row::Flags::first_free_bit + 1; static const unsigned nnc_validity_bit = Row::Flags::first_free_bit + 2; static const unsigned nnc_bit = Row::Flags::first_free_bit + 3; //@} protected: //! Index of the first bit derived classes can use. static const unsigned first_free_bit = Row::Flags::first_free_bit + 4; friend class Parma_Polyhedra_Library::Linear_Row; }; //! Pre-constructs a row: construction must be completed by construct(). Linear_Row(); //! \name Post-constructors //@{ //! Constructs properly a default-constructed element. /*! Builds a row with type \p t, size \p sz and minimum capacity. */ void construct(dimension_type sz, Flags f); //! 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. \param f Flags for the row that will be constructed. The row that we are constructing has a minimum capacity, i.e., it can contain at least \p capacity elements, \p sz of which will be default-constructed now. The row flags are set to \p f. */ void construct(dimension_type sz, dimension_type capacity, Flags f); //@} // Post-constructors //! Tight constructor: resizing will require reallocation. Linear_Row(dimension_type sz, Flags f); //! Sizing constructor with capacity. Linear_Row(dimension_type sz, dimension_type capacity, Flags f); //! Ordinary copy constructor. Linear_Row(const Linear_Row& y); //! Copy constructor with specified capacity. /*! It is assumed that \p capacity is greater than or equal to \p y size. */ Linear_Row(const Linear_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. */ Linear_Row(const Linear_Row& y, dimension_type sz, dimension_type capacity); //! Destructor. ~Linear_Row(); //! \name Flags inspection methods //@{ //! Returns a const reference to the flags of \p *this. const Flags& flags() const; //! Returns a non-const reference to the flags of \p *this. Flags& flags(); //! Returns the topological kind of \p *this. Topology topology() const; /*! \brief Returns true if and only if the topology of \p *this row is not necessarily closed. */ bool is_not_necessarily_closed() const; /*! \brief Returns true if and only if the topology of \p *this row is necessarily closed. */ bool is_necessarily_closed() const; /*! \brief Returns true if and only if \p *this row represents a line or an equality. */ bool is_line_or_equality() const; /*! \brief Returns true 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 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 //! Returns the maximum space dimension a Linear_Row 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 inhomogeneous term. Coefficient_traits::const_reference inhomogeneous_term() const; //! Returns the coefficient \f$a_n\f$. Coefficient_traits::const_reference coefficient(dimension_type n) const; /*! \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 Linear_Row objects represent different hyperplanes or hyperspaces. Applies both Linear_Row::normalize() and Linear_Row::sign_normalize(). */ void strong_normalize(); /*! \brief Returns true if and only if the coefficients are strongly normalized. */ bool check_strong_normalized() const; //! Linearly combines \p *this with \p y so that *this[k] is 0. /*! \param y The Linear_Row that will be combined with \p *this object; \param k The position of \p *this that have to be \f$0\f$. Computes a linear combination of \p *this and \p y having the element of index \p k equal to \f$0\f$. Then it assigns the resulting Linear_Row to \p *this and normalizes it. */ void linear_combine(const Linear_Row& y, dimension_type k); /*! \brief Returns true if and only if all the terms of \p *this are \f$0\f$. */ bool is_zero() const; /*! \brief Returns true if and only if all the homogeneous terms of \p *this are \f$0\f$. */ bool all_homogeneous_terms_are_zero() 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 true if successful, false otherwise. */ bool ascii_load(std::istream& s); //! Checks if all the invariants are satisfied. bool OK() const; /*! \brief Checks if all the invariants are satisfied and that the actual size and capacity match the values provided as arguments. */ bool OK(dimension_type row_size, dimension_type row_capacity) const; private: friend class Parma_Polyhedra_Library::Linear_Expression; friend class Parma_Polyhedra_Library::Constraint; friend class Parma_Polyhedra_Library::Generator; }; namespace Parma_Polyhedra_Library { //! Returns true if and only if \p x and \p y are equal. /*! \relates Linear_Row */ bool operator==(const Linear_Row& x, const Linear_Row& y); //! Returns true if and only if \p x and \p y are different. /*! \relates Linear_Row */ bool operator!=(const Linear_Row& x, const Linear_Row& y); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! The basic comparison function. /*! \relates Linear_Row \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 Linear_Row& x, const Linear_Row& y); } // namespace Parma_Polyhedra_Library namespace std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Linear_Row */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void swap(Parma_Polyhedra_Library::Linear_Row& x, Parma_Polyhedra_Library::Linear_Row& y); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::iter_swap. /*! \relates Parma_Polyhedra_Library::Linear_Row */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void iter_swap(std::vector::iterator x, std::vector::iterator y); } // namespace std /* Automatically generated from PPL source file ../src/Linear_Row.inlines.hh line 1. */ /* Linear_Row class implementation: inline functions. */ /* Automatically generated from PPL source file ../src/Linear_Row.inlines.hh line 29. */ #include namespace Parma_Polyhedra_Library { inline Linear_Row::Flags::Flags() : Row::Flags() { // Note that the constructed type has its validity bit unset. } inline Linear_Row::Flags::Flags(const Topology t) : Row::Flags(t << nnc_bit) { #ifndef NDEBUG set_bits(1 << nnc_validity_bit); #endif } inline Linear_Row::Flags::Flags(const Topology t, const Kind k) : Row::Flags((k << rpi_bit) | (t << nnc_bit)) { #ifndef NDEBUG set_bits((1 << rpi_validity_bit) | (1 << nnc_validity_bit)); #endif } inline bool Linear_Row::Flags::is_ray_or_point_or_inequality() const { PPL_ASSERT(test_bits(1 << rpi_validity_bit)); return test_bits(RAY_OR_POINT_OR_INEQUALITY << rpi_bit); } inline void Linear_Row::Flags::set_is_ray_or_point_or_inequality() { #ifndef NDEBUG set_bits(1 << rpi_validity_bit); #endif set_bits(RAY_OR_POINT_OR_INEQUALITY << rpi_bit); } inline bool Linear_Row::Flags::is_line_or_equality() const { PPL_ASSERT(test_bits(1 << rpi_validity_bit)); return !is_ray_or_point_or_inequality(); } inline void Linear_Row::Flags::set_is_line_or_equality() { #ifndef NDEBUG set_bits(1 << rpi_validity_bit); #endif reset_bits(RAY_OR_POINT_OR_INEQUALITY << rpi_bit); } inline bool Linear_Row::Flags::is_not_necessarily_closed() const { PPL_ASSERT(test_bits(1 << nnc_validity_bit)); return test_bits(NOT_NECESSARILY_CLOSED << nnc_bit); } inline bool Linear_Row::Flags::is_necessarily_closed() const { PPL_ASSERT(test_bits(1 << nnc_validity_bit)); return !is_not_necessarily_closed(); } inline void Linear_Row::Flags::set_not_necessarily_closed() { #ifndef NDEBUG set_bits(1 << nnc_validity_bit); #endif set_bits(NOT_NECESSARILY_CLOSED << nnc_bit); } inline void Linear_Row::Flags::set_necessarily_closed() { #ifndef NDEBUG set_bits(1 << nnc_validity_bit); #endif reset_bits(NOT_NECESSARILY_CLOSED << nnc_bit); } inline Topology Linear_Row::Flags::topology() const { return is_necessarily_closed() ? NECESSARILY_CLOSED : NOT_NECESSARILY_CLOSED; } inline bool Linear_Row::Flags::operator==(const Flags& y) const { base_type mask = low_bits_mask(first_free_bit); return (get_bits() & mask) == (y.get_bits() & mask); } inline bool Linear_Row::Flags::operator!=(const Flags& y) const { return !operator==(y); } inline const Linear_Row::Flags& Linear_Row::flags() const { return static_cast(Row::flags()); } inline Linear_Row::Flags& Linear_Row::flags() { return static_cast(Row::flags()); } inline bool Linear_Row::is_necessarily_closed() const { return flags().is_necessarily_closed(); } inline dimension_type Linear_Row::max_space_dimension() { // The first coefficient holds the inhomogeneous term or the divisor. // In NNC rows, the last coefficient is for the epsilon dimension. return max_size() - 2; } inline dimension_type Linear_Row::space_dimension() const { const dimension_type sz = size(); return (sz == 0) ? 0 : sz - (is_necessarily_closed() ? 1 : 2); } inline Linear_Row::Linear_Row() : Row() { } inline void Linear_Row::construct(const dimension_type sz, const dimension_type capacity, const Flags f) { Row::construct(sz, capacity, f); } inline Linear_Row::Linear_Row(const dimension_type sz, const dimension_type capacity, const Flags f) { construct(sz, capacity, f); } inline void Linear_Row::construct(const dimension_type sz, const Flags f) { construct(sz, sz, f); } inline Linear_Row::Linear_Row(const dimension_type sz, const Flags f) { construct(sz, f); } inline Linear_Row::Linear_Row(const Linear_Row& y) : Row(y) { } inline Linear_Row::Linear_Row(const Linear_Row& y, const dimension_type capacity) : Row(y, capacity) { } inline Linear_Row::Linear_Row(const Linear_Row& y, const dimension_type sz, const dimension_type capacity) : Row(y, sz, capacity) { } inline Linear_Row::~Linear_Row() { } inline bool Linear_Row::is_line_or_equality() const { return flags().is_line_or_equality(); } inline bool Linear_Row::is_ray_or_point_or_inequality() const { return flags().is_ray_or_point_or_inequality(); } inline Topology Linear_Row::topology() const { return flags().topology(); } inline void Linear_Row::set_is_line_or_equality() { flags().set_is_line_or_equality(); } inline void Linear_Row::set_is_ray_or_point_or_inequality() { flags().set_is_ray_or_point_or_inequality(); } inline void Linear_Row::set_necessarily_closed() { flags().set_necessarily_closed(); } inline void Linear_Row::set_not_necessarily_closed() { flags().set_not_necessarily_closed(); } inline Coefficient_traits::const_reference Linear_Row::inhomogeneous_term() const { return (*this)[0]; } inline Coefficient_traits::const_reference Linear_Row::coefficient(const dimension_type k) const { return (*this)[k+1]; } inline void Linear_Row::strong_normalize() { normalize(); sign_normalize(); } /*! \relates Linear_Row */ inline bool operator==(const Linear_Row& x, const Linear_Row& y) { return x.flags() == y.flags() && static_cast(x) == static_cast(y); } /*! \relates Linear_Row */ inline bool operator!=(const Linear_Row& x, const Linear_Row& y) { return !(x == y); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Linear_Row */ inline void swap(Parma_Polyhedra_Library::Linear_Row& x, Parma_Polyhedra_Library::Linear_Row& y) { x.swap(y); } /*! \relates Parma_Polyhedra_Library::Linear_Row */ inline void iter_swap(std::vector::iterator x, std::vector::iterator y) { swap(*x, *y); } } // namespace std /* Automatically generated from PPL source file ../src/Linear_Row.defs.hh line 470. */ /* Automatically generated from PPL source file ../src/Linear_Expression.defs.hh line 1. */ /* Linear_Expression class declaration. */ /* Automatically generated from PPL source file ../src/Variable.defs.hh line 1. */ /* Variable class declaration. */ /* 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/Variable.defs.hh line 30. */ #include #include 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); } // 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 e1 and e2 are equivalent, since the two variables x and z 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 i+1 exceeds Variable::max_space_dimension(). */ 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 id()+1. */ 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); //! 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 true if and only if \p x comes before \p y. bool operator()(Variable x, Variable y) const; }; 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; //! The default output function. static void default_output_function(std::ostream& s, const Variable& v); }; /* Automatically generated from PPL source file ../src/Variable.inlines.hh line 1. */ /* Variable class implementation: inline functions. */ /* Automatically generated from PPL source file ../src/Variable.inlines.hh line 28. */ #include 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); } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Variable.defs.hh line 148. */ /* Automatically generated from PPL source file ../src/Constraint_System.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Constraint_System; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Generator_System.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Generator_System; } // 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/Grid_Generator_System.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Grid_Generator_System; } /* 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/Grid.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Grid; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Linear_Expression.defs.hh line 42. */ #include 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 Linear_Expression::max_space_dimension(). */ 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 Linear_Expression::max_space_dimension(). */ 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 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); //! 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); 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 namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Linear_Expression */ void swap(Parma_Polyhedra_Library::Linear_Expression& x, Parma_Polyhedra_Library::Linear_Expression& y); } // namespace std //! 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 x is defined as x.id()+1, 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 : private Linear_Row { public: //! Default constructor: returns a copy of Linear_Expression::zero(). Linear_Expression(); //! Ordinary copy constructor. Linear_Expression(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); //! Builds the linear expression corresponding to the variable \p v. /*! \exception std::length_error Thrown if the space dimension of \p v exceeds Linear_Expression::max_space_dimension(). */ Linear_Expression(Variable v); //! Builds the linear expression corresponding to constraint \p c. /*! Given the constraint \f$c = \bigl(\sum_{i=0}^{n-1} a_i x_i + b \relsym 0\bigr)\f$, where \f$\mathord{\relsym} \in \{ =, \geq, > \}\f$, this builds the linear expression \f$\sum_{i=0}^{n-1} a_i x_i + b\f$. If \p c is an inequality (resp., equality) constraint, then the built linear expression is unique up to a positive (resp., non-zero) factor. */ explicit Linear_Expression(const Constraint& c); /*! \brief Builds the linear expression corresponding to generator \p g (for points and closure points, the divisor is not copied). Given the generator \f$g = (\frac{a_0}{d}, \ldots, \frac{a_{n-1}}{d})^\transpose\f$ (where, for lines and rays, we have \f$d = 1\f$), this builds the linear expression \f$\sum_{i=0}^{n-1} a_i x_i\f$. The inhomogeneous term of the linear expression will always be 0. If \p g is a ray, point or closure point (resp., a line), then the linear expression is unique up to a positive (resp., non-zero) factor. */ explicit Linear_Expression(const Generator& g); /*! \brief Builds the linear expression corresponding to grid generator \p g (for points, parameters and lines the divisor is not copied). Given the grid generator \f$g = (\frac{a_0}{d}, \ldots, \frac{a_{n-1}}{d})^\transpose\f$ this builds the linear expression \f$\sum_{i=0}^{n-1} a_i x_i\f$. The inhomogeneous term of the linear expression is always 0. */ explicit Linear_Expression(const Grid_Generator& g); //! Builds the linear expression corresponding to congruence \p cg. /*! Given the congruence \f$cg = \bigl(\sum_{i=0}^{n-1} a_i x_i + b = 0 \pmod{m}\bigr)\f$, this builds the linear expression \f$\sum_{i=0}^{n-1} a_i x_i + b\f$. */ explicit Linear_Expression(const Congruence& cg); //! 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; //! 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 true if and only if \p *this is \f$0\f$. bool is_zero() const; /*! \brief Returns true 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; 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 true if successful, false otherwise. */ bool ascii_load(std::istream& s); //! Checks if all the invariants are satisfied. bool OK() const; //! Swaps \p *this with \p y. void swap(Linear_Expression& y); private: /*! \brief Holds (between class initialization and finalization) a pointer to the (zero-dimension space) constant 0. */ static const Linear_Expression* zero_p; friend class Parma_Polyhedra_Library::Scalar_Products; friend class Parma_Polyhedra_Library::Constraint; friend class Parma_Polyhedra_Library::Generator; // The following declaration grants access to Grid_Generator::parameter. friend class Parma_Polyhedra_Library::Grid_Generator; friend class Parma_Polyhedra_Library::Congruence; // FIXME: the following friend declaration should be avoided. friend class Parma_Polyhedra_Library::Polyhedron; friend class Parma_Polyhedra_Library::Grid; // FIXME: the following friend declaration is only to grant access to // Constraint_System::affine_preimage(). friend class Parma_Polyhedra_Library::Constraint_System; // FIXME: the following friend declaration is only to grant access to // Generator_System::affine_image(). friend class Parma_Polyhedra_Library::Generator_System; // FIXME: the following friend declaration is only to grant access to // Congruence_System::affine_preimage(). friend class Parma_Polyhedra_Library::Congruence_System; // FIXME: the following friend declaration is only to grant access to // Grid_Generator_System::affine_image(). friend class Parma_Polyhedra_Library::Grid_Generator_System; //! Copy constructor with a specified space dimension. Linear_Expression(const Linear_Expression& e, dimension_type sz); //! 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 sz, bool); /*! \brief Builds the linear expression 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 Linear_Expression::max_space_dimension(). */ Linear_Expression(Variable v, Variable w); 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& 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 std::ostream& Parma_Polyhedra_Library::IO_Operators ::operator<<(std::ostream& s, const Linear_Expression& e); }; /* 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 29. */ #include namespace Parma_Polyhedra_Library { inline dimension_type Linear_Expression::max_space_dimension() { return Linear_Row::max_space_dimension(); } inline Linear_Expression::Linear_Expression() : Linear_Row(1, Linear_Row::Flags()) { } inline Linear_Expression::Linear_Expression(dimension_type sz, bool) : Linear_Row(sz, Linear_Row::Flags()) { } inline Linear_Expression::Linear_Expression(const Linear_Expression& e) : Linear_Row(e) { } inline Linear_Expression::~Linear_Expression() { } inline Linear_Expression::Linear_Expression(const Linear_Expression& e, dimension_type sz) : Linear_Row(e, sz, sz) { } inline Linear_Expression::Linear_Expression(Coefficient_traits::const_reference n) : Linear_Row(1, Linear_Row::Flags()) { (*this)[0] = n; } inline dimension_type Linear_Expression::space_dimension() const { return size() - 1; } inline Coefficient_traits::const_reference Linear_Expression::coefficient(Variable v) const { if (v.space_dimension() > space_dimension()) return Coefficient_zero(); return Linear_Row::coefficient(v.id()); } inline Coefficient_traits::const_reference Linear_Expression::inhomogeneous_term() const { return Linear_Row::inhomogeneous_term(); } inline bool Linear_Expression::is_zero() const { return Linear_Row::is_zero(); } inline bool Linear_Expression::all_homogeneous_terms_are_zero() const { return Linear_Row::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 Linear_Row::external_memory_in_bytes(); } inline memory_size_type Linear_Expression::total_memory_in_bytes() const { return Linear_Row::total_memory_in_bytes(); } /*! \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) { return n + e; } /*! \relates Linear_Expression */ inline Linear_Expression operator+(const Linear_Expression& e, const Variable v) { return v + e; } /*! \relates Linear_Expression */ inline Linear_Expression operator-(const Linear_Expression& e, Coefficient_traits::const_reference n) { return -n + e; } /*! \relates Linear_Expression */ inline Linear_Expression operator-(const Variable v, const Variable w) { return Linear_Expression(v, w); } /*! \relates Linear_Expression */ inline Linear_Expression operator*(const Linear_Expression& e, Coefficient_traits::const_reference n) { return n * e; } /*! \relates Linear_Expression */ inline Linear_Expression& operator+=(Linear_Expression& e, Coefficient_traits::const_reference n) { e[0] += n; return e; } /*! \relates Linear_Expression */ inline Linear_Expression& operator-=(Linear_Expression& e, Coefficient_traits::const_reference n) { e[0] -= n; return e; } inline void Linear_Expression::swap(Linear_Expression& y) { Linear_Row::swap(y); } inline void Linear_Expression::ascii_dump(std::ostream& s) const { Linear_Row::ascii_dump(s); } inline bool Linear_Expression::ascii_load(std::istream& s) { return Linear_Row::ascii_load(s); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Linear_Expression */ inline void swap(Parma_Polyhedra_Library::Linear_Expression& x, Parma_Polyhedra_Library::Linear_Expression& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/Linear_Expression.defs.hh line 489. */ /* Automatically generated from PPL source file ../src/Constraint.defs.hh line 1. */ /* Constraint class declaration. */ /* Automatically generated from PPL source file ../src/Constraint_System.defs.hh line 1. */ /* Constraint_System class declaration. */ /* 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.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Linear_System; } // namespace Parma_Polyhedra_Library /* 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/Matrix.defs.hh line 1. */ /* Matrix class declaration. */ /* Automatically generated from PPL source file ../src/Matrix.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Matrix; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Matrix.defs.hh line 32. */ #include #include #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! A 2-dimensional matrix of coefficients. /*! \ingroup PPL_CXX_interface A Matrix object is a sequence of Row objects and is characterized by the matrix dimensions (the number of rows and columns). All the rows in a matrix, besides having the same size (corresponding to the number of columns of the matrix), are also bound to have the same capacity. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) class Parma_Polyhedra_Library::Matrix { public: //! Returns the maximum number of rows of a Matrix. static dimension_type max_num_rows(); //! Returns the maximum number of columns of a Matrix. static dimension_type max_num_columns(); //! Builds an empty matrix. /*! Rows' size and capacity are initialized to \f$0\f$. */ Matrix(); //! Builds a zero matrix with specified dimensions and flags. /*! \param n_rows The number of rows of the matrix that will be created; \param n_columns The number of columns of the matrix that will be created. \param row_flags The flags used to build the rows of the matrix; by default, the rows will have all flags unset. */ Matrix(dimension_type n_rows, dimension_type n_columns, Row::Flags row_flags = Row::Flags()); //! Copy constructor. Matrix(const Matrix& y); //! Destructor. ~Matrix(); //! Assignment operator. Matrix& operator=(const Matrix& y); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! An iterator over a matrix. /*! \ingroup PPL_CXX_interface A const_iterator is used to provide read-only access to each row contained in a Matrix object. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) class const_iterator { private: typedef std::vector::const_iterator Iter; //! The const iterator on the rows' vector \p rows. Iter i; public: typedef std::forward_iterator_tag iterator_category; typedef std::iterator_traits::value_type value_type; typedef std::iterator_traits::difference_type difference_type; typedef std::iterator_traits::pointer pointer; typedef std::iterator_traits::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 true if and only if \p *this and \p y are identical. */ bool operator==(const const_iterator& y) const; /*! \brief Returns true if and only if \p *this and \p y are different. */ bool operator!=(const const_iterator& y) const; }; // class const_iterator //! Returns true 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; /*! \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; // FIXME: the following section must become private. protected: //! Contains the rows of the matrix. std::vector rows; //! Size of the initialized part of each row. dimension_type row_size; //! Capacity allocated for each row. dimension_type row_capacity; public: //! Swaps \p *this with \p y. void swap(Matrix& y); //! Adds to the matrix \p n rows of zeroes with flags set to \p row_flags. /*! \param n The number of rows to be added: must be strictly positive. \param row_flags Flags for the newly added rows. 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. */ void add_zero_rows(dimension_type n, Row::Flags row_flags); //! 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$. The matrix is expanded avoiding reallocation whenever possible. */ void add_zero_columns(dimension_type n); //! 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. \param row_flags Flags for the newly added rows. 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. */ void add_zero_rows_and_columns(dimension_type n, dimension_type m, Row::Flags row_flags); //! Adds a copy of the row \p y to the matrix. /*! \param y The row to be copied: it must have the same number of columns as the matrix. 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_row(const Row& y); //! 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 const 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); //! Makes the matrix shrink by removing its \p n trailing columns. void remove_trailing_columns(dimension_type n); //! Resizes the matrix without worrying about the old contents. /*! \param new_n_rows The number of rows of the resized matrix; \param new_n_columns The number of columns of the resized matrix. \param row_flags The flags of the rows eventually added to the matrix. The matrix is expanded to the specified dimensions avoiding reallocation whenever possible. The contents of the original matrix is lost. */ void resize_no_copy(dimension_type new_n_rows, dimension_type new_n_columns, Row::Flags row_flags); //! Swaps the columns having indexes \p i and \p j. void swap_columns(dimension_type i, dimension_type j); //! Permutes the columns of the matrix. /* \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. \note The first column of the matrix, having index zero, is never involved in a permutation. */ void permute_columns(const std::vector& cycles); //! \name Accessors //@{ //! Returns the number of columns of the matrix (i.e., the size of the rows). dimension_type num_columns() const; //! Returns the number of rows in the matrix. dimension_type num_rows() const; //@} // Accessors //! \name Subscript operators //@{ //! Returns a reference to the \p k-th row of the matrix. Row& operator[](dimension_type k); //! Returns a constant reference to the \p k-th row of the matrix. const Row& operator[](dimension_type k) const; //@} // Subscript operators //! 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 true if successful, false 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 Erases from the matrix all the rows but those having an index less than \p first_to_erase. */ void erase_to_end(dimension_type first_to_erase); //! Checks if all the invariants are satisfied. bool OK() const; }; namespace std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void swap(Parma_Polyhedra_Library::Matrix& x, Parma_Polyhedra_Library::Matrix& y); } // namespace std namespace Parma_Polyhedra_Library { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true if and only if \p x and \p y are identical. /*! \relates Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) bool operator==(const Matrix& x, const Matrix& y); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true if and only if \p x and \p y are different. /*! \relates Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) bool operator!=(const Matrix& x, const Matrix& y); } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Matrix.inlines.hh line 1. */ /* Matrix class implementation: inline functions. */ /* Automatically generated from PPL source file ../src/Matrix.inlines.hh line 27. */ #include /* Automatically generated from PPL source file ../src/Matrix.inlines.hh line 29. */ namespace Parma_Polyhedra_Library { inline dimension_type Matrix::max_num_rows() { return std::vector().max_size(); } inline dimension_type Matrix::max_num_columns() { return Row::max_size(); } inline memory_size_type Matrix::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } inline Matrix::const_iterator::const_iterator() : i() { } inline Matrix::const_iterator::const_iterator(const Iter& b) : i(b) { } inline Matrix::const_iterator::const_iterator(const const_iterator& y) : i(y.i) { } inline Matrix::const_iterator& Matrix::const_iterator::operator=(const const_iterator& y) { i = y.i; return *this; } inline Matrix::const_iterator::reference Matrix::const_iterator::operator*() const { return *i; } inline Matrix::const_iterator::pointer Matrix::const_iterator::operator->() const { return &*i; } inline Matrix::const_iterator& Matrix::const_iterator::operator++() { ++i; return *this; } inline Matrix::const_iterator Matrix::const_iterator::operator++(int) { return const_iterator(i++); } inline bool Matrix::const_iterator::operator==(const const_iterator& y) const { return i == y.i; } inline bool Matrix::const_iterator::operator!=(const const_iterator& y) const { return !operator==(y); } inline bool Matrix::has_no_rows() const { return rows.empty(); } inline Matrix::const_iterator Matrix::begin() const { return const_iterator(rows.begin()); } inline Matrix::const_iterator Matrix::end() const { return const_iterator(rows.end()); } inline void Matrix::swap(Matrix& y) { std::swap(rows, y.rows); std::swap(row_size, y.row_size); std::swap(row_capacity, y.row_capacity); } inline Matrix::Matrix() : rows(), row_size(0), row_capacity(0) { } inline Matrix::Matrix(const Matrix& y) : rows(y.rows), row_size(y.row_size), row_capacity(compute_capacity(y.row_size, max_num_columns())) { } inline Matrix::~Matrix() { } inline Matrix& Matrix::operator=(const 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; } inline void Matrix::add_row(const Row& y) { Row new_row(y, row_capacity); add_recycled_row(new_row); } inline Row& Matrix::operator[](const dimension_type k) { PPL_ASSERT(k < rows.size()); return rows[k]; } inline const Row& Matrix::operator[](const dimension_type k) const { PPL_ASSERT(k < rows.size()); return rows[k]; } inline dimension_type Matrix::num_rows() const { return rows.size(); } inline dimension_type Matrix::num_columns() const { return row_size; } /*! \relates Matrix */ inline bool operator!=(const Matrix& x, const Matrix& y) { return !(x == y); } inline void Matrix::erase_to_end(const dimension_type first_to_erase) { PPL_ASSERT(first_to_erase <= rows.size()); if (first_to_erase < rows.size()) rows.erase(rows.begin() + first_to_erase, rows.end()); } inline void Matrix::clear() { // Clear `rows' and minimize its capacity. std::vector().swap(rows); row_size = 0; row_capacity = 0; } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Matrix */ inline void swap(Parma_Polyhedra_Library::Matrix& x, Parma_Polyhedra_Library::Matrix& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/Matrix.defs.hh line 373. */ /* Automatically generated from PPL source file ../src/Linear_System.defs.hh line 34. */ #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 Linear_Row objects, where each Linear_Row implements a constraint or a generator. Linear systems are characterized by the matrix of coefficients, also encoding the number, size and capacity of Linear_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 pending, 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 true, ensures that the non-pending prefix of the sequence of rows is sorted. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) class Parma_Polyhedra_Library::Linear_System : public Matrix { public: //! Builds an empty linear system with specified topology. /*! Rows size and capacity are initialized to \f$0\f$. */ Linear_System(Topology topol); //! Builds a system with specified topology and dimensions. /*! \param topol The topology of the system that will be created; \param n_rows The number of rows of the system that will be created; \param n_columns The number of columns of the system that will be created. Creates a \p n_rows \f$\times\f$ \p n_columns system whose coefficients are all zero and whose rows are all initialized to be of the given topology. */ Linear_System(Topology topol, dimension_type n_rows, dimension_type n_columns); #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); //! Full copy constructor: pending rows are copied as pending. Linear_System(const Linear_System& y, 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 swap(Linear_System& y); //! 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 NOT_NECESSARILY_CLOSED, also the column of the \f$\epsilon\f$-dimension coefficients will be ignored. */ dimension_type space_dimension() const; //! Makes the system shrink by removing its \p n trailing columns. void remove_trailing_columns(dimension_type n); //! Permutes the columns of the system. /* \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 non-zero column indexes. Each cycle is terminated by zero. For example, assuming the system has 6 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. */ void permute_columns(const std::vector& cycles); //! \name Subscript operators //@{ //! Returns a reference to the \p k-th row of the system. Linear_Row& operator[](dimension_type k); //! Returns a constant reference to the \p k-th row of the system. const Linear_Row& operator[](dimension_type k) const; //@} // Subscript operators //! 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 true if and only if the system topology is NECESSARILY_CLOSED. */ 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 true if and only if \p *this is sorted, without checking for duplicates. */ bool check_sorted() const; //! Sets the system topology to NECESSARILY_CLOSED. void set_necessarily_closed(); //! Sets the system topology to NOT_NECESSARILY_CLOSED. void set_not_necessarily_closed(); //! Sets the topology of all rows equal to the system topology. void set_rows_topology(); //! 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); //! Resizes the system without worrying about the old contents. /*! \param new_n_rows The number of rows of the resized system; \param new_n_columns The number of columns of the resized system. The system is expanded to the specified dimensions avoiding reallocation whenever possible. The contents of the original system is lost. */ void resize_no_copy(dimension_type new_n_rows, dimension_type new_n_columns); //! Adds \p n rows and columns to the system. /*! \param n The number of rows and columns 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_rows_and_columns(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 Linear_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 Linear_Row& r); //! Adds a copy of the given row to the system. void add_row(const Linear_Row& r); //! Adds a new empty row to the system, setting only its flags. void add_pending_row(Linear_Row::Flags flags); //! Adds a copy of the given row to the pending part of the system. void add_pending_row(const Linear_Row& r); //! Adds to \p *this a copy of the rows of `y'. /*! It is assumed that \p *this has no pending rows. */ void add_rows(const Linear_System& y); //! Adds a copy of the rows of `y' to the pending part of `*this'. void add_pending_rows(const Linear_System& y); /*! \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(); class With_Bit_Matrix_iterator; /*! \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 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); /*! \brief Applies Gaussian elimination and back-substitution so as to simplify the linear system. */ void simplify(); /*! \brief Normalizes the system by dividing each row for the GCD of the row's elements. */ void normalize(); //! 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 true if successful, false 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; //! Checks if all the invariants are satisfied. /*! \param check_strong_normalized true if and only if the strong normalization of all the rows in the system has to be checked. By default, the strong normalization check is performed. This check may be turned off to avoid useless repeated checking; e.g., when re-checking a well-formed Linear_System after the permutation or deletion of some of its rows. */ bool OK(bool check_strong_normalized = true) const; private: //! The topological kind of the rows in the system. Topology row_topology; //! The index of the first pending row. dimension_type index_first_pending; /*! \brief true if rows are sorted in the ascending order as defined by bool compare(const Linear_Row&, const Linear_Row&). If false may not be sorted. */ bool sorted; //! Ordering predicate (used when implementing the sort algorithm). struct Row_Less_Than { bool operator()(const Row& x, const Row& y) const; }; }; namespace std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Linear_System */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void swap(Parma_Polyhedra_Library::Linear_System& x, Parma_Polyhedra_Library::Linear_System& y); } // namespace std namespace Parma_Polyhedra_Library { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true if and only if \p x and \p y are identical. /*! \relates Linear_System */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) bool operator==(const Linear_System& x, const Linear_System& y); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true if and only if \p x and \p y are different. /*! \relates Linear_System */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) bool operator!=(const Linear_System& x, const Linear_System& 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 #include #include namespace Parma_Polyhedra_Library { // Put them in the namespace here to declare them friends later. #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true 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 true 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); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Set-theoretic union. /*! \relates Bit_Row */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void set_union(const Bit_Row& x, const Bit_Row& y, Bit_Row& z); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Set-theoretic intersection. /*! \relates Bit_Row */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void set_intersection(const Bit_Row& x, const Bit_Row& y, Bit_Row& z); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Set-theoretic difference. /*! \relates Bit_Row */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void set_difference(const Bit_Row& x, const Bit_Row& y, Bit_Row& z); } // 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 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(); 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); friend void set_union(const Bit_Row& x, const Bit_Row& y, Bit_Row& z); friend void set_intersection(const Bit_Row& x, const Bit_Row& y, Bit_Row& z); friend void set_difference(const Bit_Row& x, const Bit_Row& y, Bit_Row& z); //! 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 true 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& x, const Bit_Row& y); }; namespace std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Bit_Row */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void swap(Parma_Polyhedra_Library::Bit_Row& x, Parma_Polyhedra_Library::Bit_Row& y); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::iter_swap. /*! \relates Parma_Polyhedra_Library::Bit_Row */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void iter_swap(std::vector::iterator x, std::vector::iterator y); } // namespace std /* 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 29. */ // For the declaration of ffs(3). #if defined(PPL_HAVE_STRINGS_H) # include #elif defined(PPL_HAVE_STRING_H) # include #endif #define PPL_BITS_PER_GMP_LIMB (PPL_SIZEOF_MP_LIMB_T*CHAR_BIT) 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(z_size) <= ULONG_MAX / PPL_BITS_PER_GMP_LIMB); mpz_init2(vec, z_size * PPL_BITS_PER_GMP_LIMB); union_helper(y, z); } else { PPL_ASSERT(static_cast(y_size) <= ULONG_MAX / PPL_BITS_PER_GMP_LIMB); mpz_init2(vec, 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 { 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::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 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(); } /*! \relates Bit_Row */ inline void set_union(const Bit_Row& x, const Bit_Row& y, Bit_Row& z) { 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(y_size) <= ULONG_MAX / PPL_BITS_PER_GMP_LIMB); mpz_realloc2(z.vec, y_size * PPL_BITS_PER_GMP_LIMB); z.union_helper(x, y); } else { PPL_ASSERT(static_cast(x_size) <= ULONG_MAX / PPL_BITS_PER_GMP_LIMB); mpz_realloc2(z.vec, x_size * PPL_BITS_PER_GMP_LIMB); z.union_helper(y, x); } } /*! \relates Bit_Row */ inline void set_intersection(const Bit_Row& x, const Bit_Row& y, Bit_Row& z) { mpz_and(z.vec, x.vec, y.vec); } /*! \relates Bit_Row */ inline void set_difference(const Bit_Row& x, const Bit_Row& y, Bit_Row& z) { PPL_DIRTY_TEMP0(mpz_class, complement_y); mpz_com(complement_y.get_mpz_t(), y.vec); mpz_and(z.vec, x.vec, complement_y.get_mpz_t()); } namespace Implementation { #if defined(__GNUC__) /*! \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) { PPL_ASSERT(u != 0); return __builtin_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) { PPL_ASSERT(ul != 0); return __builtin_ctzl(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) { PPL_ASSERT(ull != 0); return __builtin_ctzll(ull); } #elif PPL_HAVE_DECL_FFS && PPL_SIZEOF_MP_LIMB_T == PPL_SIZEOF_INT /*! \brief Assuming \p w is nonzero, returns the index of the first set bit in \p w. */ inline unsigned int first_one(mp_limb_t w) { return ffs(w)-1; } #else /*! \brief Assuming \p w is nonzero, returns the index of the first set bit in \p w. */ inline unsigned int first_one(mp_limb_t w) { unsigned int r = 0; w = w & -w; #if PPL_SIZEOF_MP_LIMB_T == 8 if ((w & 0xffffffff) == 0) { w >>= 32; r += 32; } #elif PPL_SIZEOF_MP_LIMB_T != 4 #error "size of mp_limb_t not supported by first_one(mp_limb_t w)." #endif if ((w & 0xffff) == 0) { w >>= 16; r += 16; } if ((w & 0xff) == 0) { w >>= 8; r += 8; } if (w & 0xf0) r += 4; if (w & 0xcc) r += 2; if (w & 0xaa) r += 1; return r; } #endif // !defined(__GNUC__) // && (!PPL_HAVE_DECL_FFS || PPL_SIZEOF_MP_LIMB_T != PPL_SIZEOF_INT) #if defined(__GNUC__) /*! \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) { PPL_ASSERT(u != 0); return sizeof(unsigned int)*CHAR_BIT - 1 - __builtin_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) { PPL_ASSERT(ul != 0); return sizeof(unsigned long)*CHAR_BIT - 1 - __builtin_clzl(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) { PPL_ASSERT(ull != 0); return sizeof(unsigned long long)*CHAR_BIT - 1 - __builtin_clzll(ull); } #else // !defined(__GNUC__) /*! \brief Assuming \p w is nonzero, returns the index of the last set bit in \p w. */ inline unsigned int last_one(mp_limb_t w) { PPL_ASSERT(w != 0); unsigned int r = 0; #if PPL_SIZEOF_MP_LIMB_T == 8 if (w & #if PPL_SIZEOF_LONG == 8 0xffffffff00000000 #else 0xffffffff00000000LL #endif ) { w >>= 32; r += 32; } #elif PPL_SIZEOF_MP_LIMB_T != 4 #error "size of mp_limb_t not supported by last_one(mp_limb_t w)." #endif if (w & 0xffff0000) { w >>= 16; r += 16; } if (w & 0xff00) { w >>= 8; r += 8; } if (w & 0xf0) { w >>= 4; r += 4; } if (w & 0xc) { w >>= 2; r += 2; } if (w & 0x2) r += 1; return r; } #endif // !defined(__GNUC__) } // namespace Implementation } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Bit_Row */ inline void swap(Parma_Polyhedra_Library::Bit_Row& x, Parma_Polyhedra_Library::Bit_Row& y) { x.swap(y); } /*! \relates Parma_Polyhedra_Library::Bit_Row */ inline void iter_swap(std::vector::iterator x, std::vector::iterator y) { swap(*x, *y); } } // namespace std /* Automatically generated from PPL source file ../src/Bit_Row.defs.hh line 229. */ /* Automatically generated from PPL source file ../src/Linear_System.inlines.hh line 28. */ namespace Parma_Polyhedra_Library { inline memory_size_type Linear_System::external_memory_in_bytes() const { return Matrix::external_memory_in_bytes(); } inline memory_size_type Linear_System::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } inline bool Linear_System::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; } inline void Linear_System::set_sorted(const bool b) { sorted = b; } inline Linear_System::Linear_System(Topology topol) : Matrix(), row_topology(topol), index_first_pending(0), sorted(true) { } inline Linear_System::Linear_System(Topology topol, dimension_type n_rows, dimension_type n_columns) : Matrix(n_rows, n_columns, Linear_Row::Flags(topol)), row_topology(topol), index_first_pending(n_rows), sorted(true) { } inline dimension_type Linear_System::first_pending_row() const { return index_first_pending; } inline dimension_type Linear_System::num_pending_rows() const { PPL_ASSERT(num_rows() >= first_pending_row()); return num_rows() - first_pending_row(); } inline void Linear_System::unset_pending_rows() { index_first_pending = num_rows(); } inline void Linear_System::set_index_first_pending_row(const dimension_type i) { index_first_pending = i; } inline Linear_System::Linear_System(const Linear_System& y) : Matrix(y), row_topology(y.row_topology) { unset_pending_rows(); // Previously pending rows may violate sortedness. sorted = (y.num_pending_rows() > 0) ? false : y.sorted; PPL_ASSERT(num_pending_rows() == 0); } inline Linear_System::Linear_System(const Linear_System& y, With_Pending) : Matrix(y), row_topology(y.row_topology), index_first_pending(y.index_first_pending), sorted(y.sorted) { } inline Linear_System& Linear_System::operator=(const Linear_System& y) { Matrix::operator=(y); row_topology = y.row_topology; unset_pending_rows(); // Previously pending rows may violate sortedness. sorted = (y.num_pending_rows() > 0) ? false : y.sorted; PPL_ASSERT(num_pending_rows() == 0); return *this; } inline void Linear_System::assign_with_pending(const Linear_System& y) { Matrix::operator=(y); row_topology = y.row_topology; index_first_pending = y.index_first_pending; sorted = y.sorted; } inline void Linear_System::swap(Linear_System& y) { Matrix::swap(y); std::swap(row_topology, y.row_topology); std::swap(index_first_pending, y.index_first_pending); std::swap(sorted, y.sorted); } inline void Linear_System::clear() { // Note: do NOT modify the value of `row_topology'. Matrix::clear(); index_first_pending = 0; sorted = true; } inline void Linear_System::resize_no_copy(const dimension_type new_n_rows, const dimension_type new_n_columns) { Matrix::resize_no_copy(new_n_rows, new_n_columns, Linear_Row::Flags(row_topology)); // Even though `*this' may happen to keep its sortedness, we believe // that checking such a property is not worth the effort. In fact, // it is very likely that the system will be overwritten as soon as // we return. set_sorted(false); } inline void Linear_System::set_necessarily_closed() { row_topology = NECESSARILY_CLOSED; if (!has_no_rows()) set_rows_topology(); } inline void Linear_System::set_not_necessarily_closed() { row_topology = NOT_NECESSARILY_CLOSED; if (!has_no_rows()) set_rows_topology(); } inline bool Linear_System::is_necessarily_closed() const { return row_topology == NECESSARILY_CLOSED; } inline Linear_Row& Linear_System::operator[](const dimension_type k) { return static_cast(Matrix::operator[](k)); } inline const Linear_Row& Linear_System::operator[](const dimension_type k) const { return static_cast(Matrix::operator[](k)); } inline Topology Linear_System::topology() const { return row_topology; } inline dimension_type Linear_System::max_space_dimension() { // Column zero holds the inhomogeneous term or the divisor. // In NNC linear systems, the last column holds the coefficient // of the epsilon dimension. return max_num_columns() - 2; } inline dimension_type Linear_System::space_dimension() const { const dimension_type n_columns = num_columns(); return (n_columns == 0) ? 0 : n_columns - (is_necessarily_closed() ? 1 : 2); } inline void Linear_System::remove_trailing_columns(const dimension_type n) { Matrix::remove_trailing_columns(n); // Have to re-normalize the rows of the system, // since we removed some coefficients. strong_normalize(); } inline void Linear_System::permute_columns(const std::vector& cycles) { Matrix::permute_columns(cycles); // The rows with permuted columns are still normalized but may // be not strongly normalized: sign normalization is necessary. sign_normalize(); } /*! \relates Linear_System */ inline bool operator!=(const Linear_System& x, const Linear_System& y) { return !(x == y); } inline bool Linear_System::Row_Less_Than::operator()(const Row& x, const Row& y) const { return compare(static_cast(x), static_cast(y)) < 0; } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Linear_System */ inline void swap(Parma_Polyhedra_Library::Linear_System& x, Parma_Polyhedra_Library::Linear_System& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/Linear_System.defs.hh line 427. */ /* Automatically generated from PPL source file ../src/Constraint_System.defs.hh line 34. */ #include #include namespace Parma_Polyhedra_Library { namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Constraint_System Writes true 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 // Put it in the namespace here to declare it friend later. /*! \relates Polyhedron */ bool operator==(const Polyhedron& x, const Polyhedron& y); } // namespace Parma_Polyhedra_Library namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Constraint_System */ void swap(Parma_Polyhedra_Library::Constraint_System& x, Parma_Polyhedra_Library::Constraint_System& y); } // namespace std //! 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 x and y 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 exact copy of them can be retrieved: in general, only an equivalent 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 : private Linear_System { public: //! Default constructor: builds an empty system of constraints. Constraint_System(); //! Builds the singleton system containing only constraint \p c. explicit Constraint_System(const Constraint& c); //! Builds a system containing copies of any equalities in \p cgs. explicit Constraint_System(const Congruence_System& cgs); //! Ordinary copy constructor. Constraint_System(const Constraint_System& cs); //! Destructor. ~Constraint_System(); //! Assignment operator. Constraint_System& operator=(const Constraint_System& y); //! 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; /*! \brief Returns true if and only if \p *this contains one or more equality constraints. */ bool has_equalities() const; /*! \brief Returns true if and only if \p *this contains one or more strict inequality constraints. */ bool has_strict_inequalities() const; /*! \brief Removes all the constraints from the constraint system and sets its space dimension to 0. */ void clear(); /*! \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(); //! 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 ph: \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 */ class const_iterator : public std::iterator { 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 Constraint& operator*() const; //! Indirect member selector. const Constraint* operator->() const; //! Prefix increment operator. const_iterator& operator++(); //! Postfix increment operator. const_iterator operator++(int); /*! \brief Returns true if and only if \p *this and \p y are identical. */ bool operator==(const const_iterator& y) const; /*! \brief Returns true if and only if \p *this and \p y are different. */ bool operator!=(const const_iterator& y) const; private: friend class Constraint_System; //! The const iterator over the matrix of constraints. Linear_System::const_iterator i; //! A const pointer to the matrix of constraints. const Linear_System* csp; //! Constructor. const_iterator(const Linear_System::const_iterator& iter, const Constraint_System& csys); //! \p *this skips to the next non-trivial constraint. void skip_forward(); }; //! Returns true if and only if \p *this has no constraints. bool empty() const; /*! \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. #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! Returns true if and only if \p *this is a valid Linear_System and each row in the system is a valid Constraint. */ #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 true if successful, false 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 swap(Constraint_System& y); private: /*! \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 const_iterator; friend class Parma_Polyhedra_Library::Polyhedron; friend bool operator==(const Polyhedron& x, const Polyhedron& y); //! Builds an empty system of constraints having the specified topology. explicit Constraint_System(Topology topol); /*! \brief Builds a system of \p n_rows constraints on a \p n_columns - 1 dimensional space (including the \f$\epsilon\f$ dimension, if \p topol is NOT_NECESSARILY_CLOSED). */ Constraint_System(Topology topol, dimension_type n_rows, dimension_type n_columns); /*! \brief Adjusts \p *this so that it matches the topology and the number of space dimensions given as parameters (adding or removing columns if needed). Returns false if and only if \p topol is equal to NECESSARILY_CLOSED and \p *this contains strict inequalities. */ bool adjust_topology_and_space_dimension(Topology topol, dimension_type num_dimensions); //! Returns the \p k- th constraint of the system. Constraint& operator[](dimension_type k); //! Returns a constant reference to the \p k- th constraint of the system. const Constraint& operator[](dimension_type k) const; //! Returns true 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 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 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(dimension_type v, const Linear_Expression& expr, Coefficient_traits::const_reference denominator); //! 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 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(); }; // Constraint_System.inlines.hh is not included here on purpose. /* Automatically generated from PPL source file ../src/Constraint.defs.hh line 35. */ #include namespace Parma_Polyhedra_Library { // Put them in the namespace here to declare them friend later. //! Returns true if and only if \p x is equivalent to \p y. /*! \relates Constraint */ bool operator==(const Constraint& x, const Constraint& y); //! Returns true if and only if \p x is not equivalent to \p y. /*! \relates Constraint */ bool operator!=(const Constraint& x, const Constraint& y); //! 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); } // namespace Parma_Polyhedra_Library namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Constraint */ void swap(Parma_Polyhedra_Library::Constraint& x, Parma_Polyhedra_Library::Constraint& y); } // namespace std //! 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 (==), non-strict inequalities (\>= and \<=) and strict inequalities (\< and \>). 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 x, y and z 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 : private Linear_Row { public: //! Ordinary copy constructor. Constraint(const Constraint& c); //! Copy-constructs from equality congruence \p cg. /*! \exception std::invalid_argument Thrown if \p cg is a proper congruence. */ explicit Constraint(const Congruence& cg); //! Destructor. ~Constraint(); //! 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; //! 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 }; //! Returns the constraint type of \p *this. Type type() const; /*! \brief Returns true if and only if \p *this is an equality constraint. */ bool is_equality() const; /*! \brief Returns true if and only if \p *this is an inequality constraint (either strict or non-strict). */ bool is_inequality() const; /*! \brief Returns true if and only if \p *this is a non-strict inequality constraint. */ bool is_nonstrict_inequality() const; /*! \brief Returns true 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 positivity constraint. */ 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 true 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 true 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 true 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; 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 true if successful, false otherwise. */ bool ascii_load(std::istream& s); //! Checks if all the invariants are satisfied. bool OK() const; //! Swaps \p *this with \p y. void swap(Constraint& y); private: /*! \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 positivity constraint. */ 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; friend class Parma_Polyhedra_Library::Congruence; friend class Parma_Polyhedra_Library::Scalar_Products; friend class Parma_Polyhedra_Library::Topology_Adjusted_Scalar_Product_Sign; friend class Parma_Polyhedra_Library::Constraint_System; friend class Parma_Polyhedra_Library::Constraint_System::const_iterator; // FIXME: the following friend declaration should be avoided. friend class Parma_Polyhedra_Library::Polyhedron; friend Parma_Polyhedra_Library ::Linear_Expression::Linear_Expression(const Constraint& c); //! Default constructor: private and not implemented. Constraint(); /*! \brief Builds a constraint of type \p type and topology \p topology, stealing the coefficients from \p e. */ Constraint(Linear_Expression& e, Type type, Topology topology); //! Constructs from a congruence, with specified size and capacity. Constraint(const Congruence& cg, dimension_type sz, dimension_type capacity); /*! \brief Throws a std::invalid_argument exception containing error message \p message. */ void throw_invalid_argument(const char* method, const char* message) const; /*! \brief Throws a std::invalid_argument exception containing the appropriate error message. */ void throw_dimension_incompatible(const char* method, const char* name_var, Variable v) const; 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<=(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<(const Linear_Expression& e, Coefficient_traits::const_reference n); friend Constraint operator<(Coefficient_traits::const_reference n, const Linear_Expression& e); //! Copy constructor with given size. Constraint(const Constraint& c, dimension_type sz); /*! \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(); //! 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(); //! Sets the constraint type to EQUALITY. void set_is_equality(); //! Sets the constraint to be an inequality. /*! Whether the constraint type will become NONSTRICT_INEQUALITY or STRICT_INEQUALITY depends on the topology and the value of the low-level coefficients of the constraint. */ void set_is_inequality(); }; 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 } // 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 Constraint::Constraint(Linear_Expression& e, Type type, Topology topology) { PPL_ASSERT(type != STRICT_INEQUALITY || topology == NOT_NECESSARILY_CLOSED); Linear_Row::swap(e); flags() = Flags(topology, (type == EQUALITY ? LINE_OR_EQUALITY : RAY_OR_POINT_OR_INEQUALITY)); } inline Constraint::Constraint(const Constraint& c) : Linear_Row(c) { } inline Constraint::Constraint(const Constraint& c, const dimension_type sz) : Linear_Row(c, sz, sz) { } inline Constraint::~Constraint() { } inline Constraint& Constraint::operator=(const Constraint& c) { Linear_Row::operator=(c); return *this; } inline dimension_type Constraint::max_space_dimension() { return Linear_Row::max_space_dimension(); } inline dimension_type Constraint::space_dimension() const { return Linear_Row::space_dimension(); } 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; else return ((*this)[size() - 1] < 0) ? STRICT_INEQUALITY : 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 Linear_Row::coefficient(v.id()); } inline Coefficient_traits::const_reference Constraint::inhomogeneous_term() const { return Linear_Row::inhomogeneous_term(); } inline memory_size_type Constraint::external_memory_in_bytes() const { return Linear_Row::external_memory_in_bytes(); } inline memory_size_type Constraint::total_memory_in_bytes() const { return Linear_Row::total_memory_in_bytes(); } /*! \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 - e2; Constraint c(diff, Constraint::EQUALITY, NECESSARILY_CLOSED); // Enforce normalization. c.strong_normalize(); return c; } /*! \relates Constraint */ inline Constraint operator==(const Variable v1, const Variable v2) { Linear_Expression diff = (v1.space_dimension() < v2.space_dimension()) ? v1-v2 : v2-v1; return Constraint(diff, Constraint::EQUALITY, NECESSARILY_CLOSED); } /*! \relates Constraint */ inline Constraint operator>=(const Linear_Expression& e1, const Linear_Expression& e2) { Linear_Expression diff = e1 - e2; Constraint c(diff, Constraint::NONSTRICT_INEQUALITY, NECESSARILY_CLOSED); // Enforce normalization. c.normalize(); return c; } /*! \relates Constraint */ inline Constraint operator>=(const Variable v1, const Variable v2) { Linear_Expression diff = v1-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; // Setting the epsilon coefficient to -1. // NOTE: this also enforces normalization. const dimension_type e1_dim = e1.space_dimension(); const dimension_type e2_dim = e2.space_dimension(); if (e1_dim > e2_dim) diff -= Variable(e1_dim); else diff -= Variable(e2_dim); diff += e1; diff -= e2; Constraint c(diff, Constraint::STRICT_INEQUALITY, NOT_NECESSARILY_CLOSED); return c; } /*! \relates Constraint */ inline Constraint operator>(const Variable v1, const Variable v2) { Linear_Expression diff = v1-v2; diff -= Variable(std::max(v1.space_dimension(), v2.space_dimension())); return Constraint(diff, Constraint::STRICT_INEQUALITY, NOT_NECESSARILY_CLOSED); } /*! \relates Constraint */ inline Constraint operator==(Coefficient_traits::const_reference n, const Linear_Expression& e) { Linear_Expression diff = n - e; Constraint c(diff, Constraint::EQUALITY, NECESSARILY_CLOSED); // Enforce normalization. c.strong_normalize(); return c; } /*! \relates Constraint */ inline Constraint operator>=(Coefficient_traits::const_reference n, const Linear_Expression& e) { Linear_Expression diff = n - e; Constraint c(diff, Constraint::NONSTRICT_INEQUALITY, NECESSARILY_CLOSED); // Enforce normalization. c.normalize(); return c; } /*! \relates Constraint */ inline Constraint operator>(Coefficient_traits::const_reference n, const Linear_Expression& e) { Linear_Expression diff; // Setting the epsilon coefficient to -1. // NOTE: this also enforces normalization. diff -= Variable(e.space_dimension()); diff += n; diff -= e; Constraint c(diff, Constraint::STRICT_INEQUALITY, NOT_NECESSARILY_CLOSED); return c; } /*! \relates Constraint */ inline Constraint operator==(const Linear_Expression& e, Coefficient_traits::const_reference n) { Linear_Expression diff = e - n; Constraint c(diff, Constraint::EQUALITY, NECESSARILY_CLOSED); // Enforce normalization. c.strong_normalize(); return c; } /*! \relates Constraint */ inline Constraint operator>=(const Linear_Expression& e, Coefficient_traits::const_reference n) { Linear_Expression diff = e - n; Constraint c(diff, Constraint::NONSTRICT_INEQUALITY, NECESSARILY_CLOSED); // Enforce normalization. c.normalize(); return c; } /*! \relates Constraint */ inline Constraint operator>(const Linear_Expression& e, Coefficient_traits::const_reference n) { Linear_Expression diff; // Setting the epsilon coefficient to -1. // NOTE: this also enforces normalization. diff -= Variable(e.space_dimension()); diff += e; diff -= n; Constraint c(diff, Constraint::STRICT_INEQUALITY, NOT_NECESSARILY_CLOSED); c.set_not_necessarily_closed(); c.set_is_inequality(); 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::ascii_dump(std::ostream& s) const { Linear_Row::ascii_dump(s); } inline bool Constraint::ascii_load(std::istream& s) { return Linear_Row::ascii_load(s); } inline void Constraint::swap(Constraint& y) { Linear_Row::swap(y); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Constraint */ inline void swap(Parma_Polyhedra_Library::Constraint& x, Parma_Polyhedra_Library::Constraint& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/Constraint.defs.hh line 563. */ /* Automatically generated from PPL source file ../src/Generator.defs.hh line 1. */ /* Generator class declaration. */ /* Automatically generated from PPL source file ../src/Generator_System.defs.hh line 1. */ /* Generator_System class declaration. */ /* 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.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Poly_Con_Relation; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Poly_Con_Relation.defs.hh line 29. */ #include 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 namespace Parma_Polyhedra_Library { namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Generator_System Writes false 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 // Put it in the namespace here to declare it friend later. /*! \relates Polyhedron */ bool operator==(const Polyhedron& x, const Polyhedron& y); } // namespace Parma_Polyhedra_Library namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Generator_System */ void swap(Parma_Polyhedra_Library::Generator_System& x, Parma_Polyhedra_Library::Generator_System& y); } // namespace std //! 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 x and y 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 exact copy of them can be retrieved: in general, only an equivalent 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 : protected Linear_System { public: //! Default constructor: builds an empty system of generators. Generator_System(); //! Builds the singleton system containing only generator \p g. explicit Generator_System(const Generator& g); //! Ordinary copy constructor. Generator_System(const Generator_System& gs); //! Destructor. ~Generator_System(); //! Assignment operator. Generator_System& operator=(const Generator_System& y); //! 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; /*! \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); //! 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(); //! 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 ph: \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(cout, "\n")); \endcode */ class const_iterator : public std::iterator { 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 Generator& operator*() const; //! Indirect member selector. const Generator* operator->() const; //! Prefix increment operator. const_iterator& operator++(); //! Postfix increment operator. const_iterator operator++(int); /*! \brief Returns true if and only if \p *this and \p y are identical. */ bool operator==(const const_iterator& y) const; /*! \brief Returns true if and only if \p *this and \p y are different. */ bool operator!=(const const_iterator& y) const; private: friend class Generator_System; //! The const iterator over the Linear_System. Linear_System::const_iterator i; //! A const pointer to the Linear_System. const Linear_System* gsp; //! Constructor. const_iterator(const Linear_System::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(); }; //! Returns true 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. /*! Returns true if and only if \p *this is a valid Linear_System and each row in the system is a valid Generator. */ 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 true if successful, false 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 swap(Generator_System& y); private: /*! \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 const_iterator; friend class Parma_Polyhedra_Library::Polyhedron; friend class Parma_Polyhedra_Library::Grid_Generator_System; friend bool operator==(const Polyhedron& x, const Polyhedron& y); //! Builds an empty system of generators having the specified topology. explicit Generator_System(Topology topol); /*! \brief Builds a system of \p n_rows rays/points on a \p n_columns - 1 dimensional space (including the \f$\epsilon\f$ dimension, if \p topol is NOT_NECESSARILY_CLOSED). */ Generator_System(Topology topol, dimension_type n_rows, dimension_type n_columns); /*! \brief Adjusts \p *this so that it matches the topology and the number of space dimensions given as parameters (adding or removing columns if needed). Returns false if and only if \p topol is equal to NECESSARILY_CLOSED and \p *this contains closure points. */ bool adjust_topology_and_space_dimension(Topology topol, dimension_type num_dimensions); /*! \brief For each unmatched closure point in \p *this, adds the corresponding point. It is assumed that the topology of \p *this is NOT_NECESSARILY_CLOSED. */ void add_corresponding_points(); /*! \brief Returns true 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 NOT_NECESSARILY_CLOSED. */ void add_corresponding_closure_points(); /*! \brief Returns true 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; //! Returns the \p k- th generator of the system. Generator& operator[](dimension_type k); //! 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 true if all the generators satisfy \p c. bool satisfied_by_all_generators(const Constraint& c) const; //! Returns true if all the generators satisfy \p c. /*! It is assumed that c.is_necessarily_closed() holds. */ bool satisfied_by_all_generators_C(const Constraint& c) const; //! Returns true if all the generators satisfy \p c. /*! It is assumed that c.is_necessarily_closed() does not hold. */ bool satisfied_by_all_generators_NNC(const Constraint& c) const; //! Assigns to a given variable an affine expression. /*! \param v Index of the column 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 \p v -th 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(dimension_type 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); }; // Generator_System.inlines.hh is not included here on purpose. /* 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 struct Rectilinear_Distance_Specialization; template struct Euclidean_Distance_Specialization; template struct L_Infinity_Distance_Specialization; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/distances.defs.hh line 29. */ template 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 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 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 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 struct maybe_assign_struct { 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 inline Result maybe_assign(const To*& top, To& tmp, const From& from, Rounding_Dir dir) { return maybe_assign_struct::function(top, tmp, from, dir); } template inline void Rectilinear_Distance_Specialization::combine(Temp& running, const Temp& current, Rounding_Dir dir) { add_assign_r(running, running, current, dir); } template inline void Rectilinear_Distance_Specialization::finalize(Temp&, Rounding_Dir) { } template inline void Euclidean_Distance_Specialization::combine(Temp& running, Temp& current, Rounding_Dir dir) { mul_assign_r(current, current, current, dir); add_assign_r(running, running, current, dir); } template inline void Euclidean_Distance_Specialization::finalize(Temp& running, Rounding_Dir dir) { sqrt_assign_r(running, running, dir); } template inline void L_Infinity_Distance_Specialization::combine(Temp& running, const Temp& current, Rounding_Dir) { if (current > running) running = current; } template inline void L_Infinity_Distance_Specialization::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/Generator.defs.hh line 40. */ #include namespace Parma_Polyhedra_Library { // Put them in the namespace here to declare them friend later. namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Generator */ std::ostream& operator<<(std::ostream& s, const Generator& g); } // namespace IO_Operators } // namespace Parma_Polyhedra_Library namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Generator */ void swap(Parma_Polyhedra_Library::Generator& x, Parma_Polyhedra_Library::Generator& y); } // namespace std //! 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 not 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 (line, ray, point or closure_point) 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 divisor 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 x, y and z 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 point 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 point (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 g1 is a point having coordinates \f$(a_0, \ldots, a_{n-1})^\transpose\f$, we construct the closure point g2 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 coefficient with the notion of coordinate: these are equivalent only when the divisor of the (closure) point is 1. */ class Parma_Polyhedra_Library::Generator : private Linear_Row { public: //! 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); //! 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); //! 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()); //! 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()); //! Ordinary copy constructor. Generator(const Generator& g); //! Destructor. ~Generator(); //! Assignment operator. Generator& operator=(const Generator& g); //! 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; //! 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 true if and only if \p *this is a line. bool is_line() const; //! Returns true if and only if \p *this is a ray. bool is_ray() const; #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true 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 true if and only if \p *this is a point. bool is_point() const; //! Returns true 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 true 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; 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 true if successful, false otherwise. */ bool ascii_load(std::istream& s); //! Checks if all the invariants are satisfied. bool OK() const; //! Swaps \p *this with \p y. void swap(Generator& y); private: /*! \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. */ Generator(Linear_Expression& e, Type type, Topology topology); /*! \brief Throw a std::invalid_argument exception containing the appropriate error message. */ void throw_dimension_incompatible(const char* method, const char* name_var, Variable v) const; /*! \brief Throw a std::invalid_argument exception containing the appropriate error message. */ void throw_invalid_argument(const char* method, const char* reason) const; 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; friend class Parma_Polyhedra_Library::Grid_Generator; // This is for access to Row and Linear_Row in `insert'. friend class Parma_Polyhedra_Library::Grid_Generator_System; friend Parma_Polyhedra_Library ::Linear_Expression::Linear_Expression(const Generator& g); friend std::ostream& Parma_Polyhedra_Library::IO_Operators::operator<<(std::ostream& s, const Generator& g); //! Copy constructor with given space dimension. Generator(const Generator& g, dimension_type dimension); //! Returns true if and only if \p *this is not a line. bool is_ray_or_point() const; //! Sets the Linear_Row kind to LINE_OR_EQUALITY. void set_is_line(); //! Sets the Linear_Row kind to RAY_OR_POINT_OR_INEQUALITY. void set_is_ray_or_point(); /*! \brief Returns true 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; //! Default constructor: private and not implemented. Generator(); }; namespace Parma_Polyhedra_Library { //! Shorthand for Generator Generator::line(const Linear_Expression& e). /*! \relates Generator */ Generator line(const Linear_Expression& e); //! Shorthand for Generator Generator::ray(const Linear_Expression& e). /*! \relates Generator */ Generator ray(const Linear_Expression& e); /*! \brief Shorthand for Generator Generator::point(const Linear_Expression& e, Coefficient_traits::const_reference d). \relates Generator */ Generator point(const Linear_Expression& e = Linear_Expression::zero(), Coefficient_traits::const_reference d = Coefficient_one()); /*! \brief Shorthand for Generator Generator::closure_point(const Linear_Expression& e, Coefficient_traits::const_reference d). \relates Generator */ Generator closure_point(const Linear_Expression& e = Linear_Expression::zero(), Coefficient_traits::const_reference d = Coefficient_one()); //! Returns true if and only if \p x is equivalent to \p y. /*! \relates Generator */ bool operator==(const Generator& x, const Generator& y); //! Returns true 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. \note Distances are \e only defined between generators that are points and/or closure points; for rays or lines, \c false is returned. */ template bool rectilinear_distance_assign(Checked_Number& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. \note Distances are \e only defined between generators that are points and/or closure points; for rays or lines, \c false is returned. */ template bool rectilinear_distance_assign(Checked_Number& 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 true; returns false 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 bool rectilinear_distance_assign(Checked_Number& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. \note Distances are \e only defined between generators that are points and/or closure points; for rays or lines, \c false is returned. */ template bool euclidean_distance_assign(Checked_Number& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. \note Distances are \e only defined between generators that are points and/or closure points; for rays or lines, \c false is returned. */ template bool rectilinear_distance_assign(Checked_Number& 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 true; returns false 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 bool euclidean_distance_assign(Checked_Number& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. \note Distances are \e only defined between generators that are points and/or closure points; for rays or lines, \c false is returned. */ template bool l_infinity_distance_assign(Checked_Number& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. \note Distances are \e only defined between generators that are points and/or closure points; for rays or lines, \c false is returned. */ template bool l_infinity_distance_assign(Checked_Number& 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 true; returns false 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 bool l_infinity_distance_assign(Checked_Number& 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 Generator::Generator(Linear_Expression& e, Type type, Topology topology) { PPL_ASSERT(type != CLOSURE_POINT || topology == NOT_NECESSARILY_CLOSED); Linear_Row::swap(e); flags() = Flags(topology, (type == LINE ? LINE_OR_EQUALITY : RAY_OR_POINT_OR_INEQUALITY)); } inline Generator::Generator(const Generator& g) : Linear_Row(g) { } inline Generator::Generator(const Generator& g, dimension_type dimension) : Linear_Row(g, dimension, dimension) { } inline Generator::~Generator() { } inline Generator& Generator::operator=(const Generator& g) { Linear_Row::operator=(g); return *this; } inline dimension_type Generator::max_space_dimension() { return Linear_Row::max_space_dimension(); } inline dimension_type Generator::space_dimension() const { return Linear_Row::space_dimension(); } 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 (*this)[0] == 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. const Generator& g = *this; return (g[size() - 1] == 0) ? CLOSURE_POINT : 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 Linear_Row::coefficient(v.id()); } inline Coefficient_traits::const_reference Generator::divisor() const { Coefficient_traits::const_reference d = Linear_Row::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 memory_size_type Generator::external_memory_in_bytes() const { return Linear_Row::external_memory_in_bytes(); } inline memory_size_type Generator::total_memory_in_bytes() const { return Linear_Row::total_memory_in_bytes(); } 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) { return Generator::line(e); } /*! \relates Generator */ inline Generator ray(const Linear_Expression& e) { return Generator::ray(e); } /*! \relates Generator */ inline Generator point(const Linear_Expression& e, Coefficient_traits::const_reference d) { return Generator::point(e, d); } /*! \relates Generator */ inline Generator closure_point(const Linear_Expression& e, Coefficient_traits::const_reference d) { return Generator::closure_point(e, d); } /*! \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 { Linear_Row::ascii_dump(s); } inline bool Generator::ascii_load(std::istream& s) { return Linear_Row::ascii_load(s); } inline void Generator::swap(Generator& y) { Linear_Row::swap(y); } #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \relates Generator */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template inline bool l_m_distance_assign(Checked_Number& 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_TEMP0(mpq_class, x_coord); PPL_DIRTY_TEMP0(mpq_class, y_coord); PPL_DIRTY_TEMP0(mpq_class, x_div); PPL_DIRTY_TEMP0(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); 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 inline bool rectilinear_distance_assign(Checked_Number& r, const Generator& x, const Generator& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign > (r, x, y, dir, tmp0, tmp1, tmp2); } /*! \relates Generator */ template inline bool rectilinear_distance_assign(Checked_Number& r, const Generator& x, const Generator& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool rectilinear_distance_assign(Checked_Number& r, const Generator& x, const Generator& y, const Rounding_Dir dir) { return rectilinear_distance_assign(r, x, y, dir); } /*! \relates Generator */ template inline bool euclidean_distance_assign(Checked_Number& r, const Generator& x, const Generator& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign > (r, x, y, dir, tmp0, tmp1, tmp2); } /*! \relates Generator */ template inline bool euclidean_distance_assign(Checked_Number& r, const Generator& x, const Generator& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool euclidean_distance_assign(Checked_Number& r, const Generator& x, const Generator& y, const Rounding_Dir dir) { return euclidean_distance_assign(r, x, y, dir); } /*! \relates Generator */ template inline bool l_infinity_distance_assign(Checked_Number& r, const Generator& x, const Generator& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign > (r, x, y, dir, tmp0, tmp1, tmp2); } /*! \relates Generator */ template inline bool l_infinity_distance_assign(Checked_Number& r, const Generator& x, const Generator& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool l_infinity_distance_assign(Checked_Number& r, const Generator& x, const Generator& y, const Rounding_Dir dir) { return l_infinity_distance_assign(r, x, y, dir); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Generator */ inline void swap(Parma_Polyhedra_Library::Generator& x, Parma_Polyhedra_Library::Generator& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/Generator.defs.hh line 723. */ /* Automatically generated from PPL source file ../src/Congruence.defs.hh line 1. */ /* Congruence class declaration. */ /* Automatically generated from PPL source file ../src/Congruence_System.defs.hh line 1. */ /* Congruence_System class declaration. */ /* 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/Congruence_System.defs.hh line 35. */ #include namespace Parma_Polyhedra_Library { namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Congruence_System Writes true 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 } // namespace Parma_Polyhedra_Library namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Congruence_System */ void swap(Parma_Polyhedra_Library::Congruence_System& x, Parma_Polyhedra_Library::Congruence_System& y); } // namespace std //! 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 x and y 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 exact copy of them can be retrieved: in general, only an equivalent 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 : private Matrix { public: //! Default constructor: builds an empty system of congruences. Congruence_System(); //! Builds the singleton system containing only congruence \p cg. explicit Congruence_System(const Congruence& cg); /*! \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); //! Builds a system containing copies of any equalities in \p cs. explicit Congruence_System(const Constraint_System& cs); //! Ordinary copy constructor. Congruence_System(const Congruence_System& cgs); //! Destructor. ~Congruence_System(); //! Assignment operator. Congruence_System& operator=(const Congruence_System& cgs); //! 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 true if and only if \p *this is exactly equal to \p cgs. */ bool is_equal_to(const Congruence_System& cgs) const; /*! \brief Returns true 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 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 recycling_insert(cg). /*! \brief Inserts in \p *this a copy of the congruences in \p cgs, increasing the number of space dimensions if needed. The inserted copies will be strongly normalized. */ void insert(const Congruence_System& cgs); /*! \brief Inserts into \p *this the congruences in \p cgs, increasing the number of space dimensions if needed. */ void recycling_insert(Congruence_System& cgs); //! 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 gr: \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 { 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 true if and only if \p *this and \p y are identical. */ bool operator==(const const_iterator& y) const; /*! \brief Returns true 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 matrix of congruences. Matrix::const_iterator i; //! A const pointer to the matrix of congruences. const Matrix* csp; //! Constructor. const_iterator(const Matrix::const_iterator& iter, const Congruence_System& cgs); //! \p *this skips to the next non-trivial congruence. void skip_forward(); }; //! Returns true 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 true if and only if \p *this is a valid Matrix, each row in the system is a valid Congruence and the number of columns 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 true if successful, false 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 swap(Congruence_System& cgs); /*! \brief Adds \p dims rows and \p dims columns of zeroes to the matrix, initializing the added rows as in the unit congruence 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}{}{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_columns(dimension_type dims); protected: //! Returns true if \p g satisfies all the congruences. bool satisfies_all_congruences(const Grid_Generator& g) const; private: /*! \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; //! Builds an empty (i.e. zero rows) system of dimension \p d. explicit Congruence_System(dimension_type d); /*! \brief Concatenates copies of the congruences from \p cgs onto \p *this. \param cgs The congruence system to append to \p this. The number of rows in \p cgs 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 cgs in the lower right-hand side, and padding with zeroes. */ void concatenate(const Congruence_System& cgs); //! Adjusts all expressions to have the same moduli. void normalize_moduli(); //! Increase the number of space dimensions to \p new_space_dim. /*! \p new_space_dim must at least equal to the current space dimension. */ bool increase_space_dimension(dimension_type new_space_dim); /*! \brief Inserts in \p *this an exact copy of the congruence \p cg, increasing the number of space dimensions if needed. This method inserts a copy of \p cg in the given form, instead of first strong normalizing \p cg as \ref insert would do. */ void insert_verbatim(const Congruence& cg); friend class const_iterator; friend class Grid; friend class Grid_Certificate; friend void std::swap(Parma_Polyhedra_Library::Congruence_System& x, Parma_Polyhedra_Library::Congruence_System& y); friend bool operator==(const Congruence_System& x, const Congruence_System& y); //! Returns the \p k- th congruence of the system. Congruence& operator[](dimension_type k); //! Returns a constant reference to the \p k- th congruence of the system. const Congruence& operator[](dimension_type k) const; /*! \brief Returns true 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; /*! \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(dimension_type v, const Linear_Expression& expr, Coefficient_traits::const_reference denominator); /*! \brief Removes the higher dimensions of the system so that the resulting system will have dimension \p new_dimension. The value of \p new_dimension must be at most the space dimension of \p *this. */ void remove_higher_space_dimensions(dimension_type new_dimension); //! Resizes the system without worrying about the old contents. /*! \param new_num_rows The number of rows of the resized system; \param new_num_columns The number of columns of the resized system. The system is expanded to the specified dimensions avoiding reallocation whenever possible. The contents of the original system is lost. */ void resize_no_copy(dimension_type new_num_rows, dimension_type new_num_columns); }; // Congruence_System.inlines.hh is not included here on purpose. /* Automatically generated from PPL source file ../src/Congruence.defs.hh line 37. */ #include namespace Parma_Polyhedra_Library { namespace IO_Operators { //! Output operators. /*! \relates Parma_Polyhedra_Library::Congruence */ std::ostream& operator<<(std::ostream& s, const Congruence& c); // Put this in the namespace here to declare it a friend later. /*! \relates Parma_Polyhedra_Library::Congruence_System */ std::ostream& operator<<(std::ostream& s, const Congruence_System& cgs); } // namespace IO_Operators // Put these in the namespace here to declare them friend later. //! Returns true if and only if \p x and \p y are equivalent. /*! \relates Congruence */ bool operator==(const Congruence& x, const Congruence& y); //! Returns false if and only if \p x and \p y are equivalent. /*! \relates Congruence */ bool operator!=(const Congruence& x, const Congruence& y); //! 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); } // namespace Parma_Polyhedra_Library namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Congruence */ void swap(Parma_Polyhedra_Library::Congruence& x, Parma_Polyhedra_Library::Congruence& y); } // namespace std //! 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 `\%=' 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 `/'. 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 x, y and z 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 : private Row { public: //! Ordinary copy constructor. Congruence(const Congruence& cg); //! 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); //! Destructor. ~Congruence(); //! Assignment operator. Congruence& operator=(const Congruence& cg); //! 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; //! 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; //! 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 true 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 true 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 true if the modulus is greater than zero. /*! A congruence with a modulus of 0 is a linear equality. */ bool is_proper_congruence() const; //! Returns true if \p *this is an equality. /*! A modulus of zero denotes a linear equality. */ bool is_equality() const; /*! \brief Returns true if \p *this is equal to \p cg in dimension \p dim. */ bool is_equal_at_dimension(dimension_type dim, const Congruence& cg) 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 integrality congruence. */ 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); //! Returns the congruence \f$e = n \pmod{1}\f$. static Congruence create(const Linear_Expression& e, Coefficient_traits::const_reference n); //! Returns the congruence \f$n = e \pmod{1}\f$. static Congruence create(Coefficient_traits::const_reference n, const Linear_Expression& e); /*! \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 of the internal representation of \p *this. */ bool ascii_load(std::istream& s); //! Checks if all the invariants are satisfied. bool OK() const; protected: //! 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 their syntaxes (as output by operator<<) are equal. */ 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 integrality congruence. */ static const Congruence* zero_dim_integrality_p; //! Marks this congruence as a linear equality. void set_is_equality(); //! Negates the elements from index \p start to index \p end. void negate(dimension_type start, dimension_type end); //! Default constructor: private and not implemented. Congruence(); //! Copy-constructs with specified size and capacity. Congruence(const Congruence& cg, dimension_type sz, dimension_type capacity); //! Constructs from a constraint, with specified size and capacity. Congruence(const Constraint& c, dimension_type sz, dimension_type capacity); //! Copy-constructs from \p cg, multiplying \p k into the 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$. */ Congruence(const Congruence& cg, Coefficient_traits::const_reference k); //! Constructs from Linear_Expression \p le, using modulus \p m. /*! Builds a congruence with modulus \p m, stealing the coefficients from \p 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); //! Swaps \p *this with \p y. void swap(Congruence& y); /*! \brief Throws a std::invalid_argument exception containing error message \p message. */ void throw_invalid_argument(const char* method, const char* message) const; /*! \brief Throws a std::invalid_argument exception containing the appropriate error message. */ void throw_dimension_incompatible(const char* method, const char* v_name, Variable v) const; friend Congruence operator/(const Congruence& cg, Coefficient_traits::const_reference k); friend Congruence operator/(const Constraint& c, Coefficient_traits::const_reference m); friend bool operator==(const Congruence& x, const Congruence& y); friend bool operator!=(const Congruence& x, const Congruence& y); friend std::ostream& Parma_Polyhedra_Library::IO_Operators ::operator<<(std::ostream& s, const Congruence_System& cgs); friend class Parma_Polyhedra_Library::Scalar_Products; friend class Parma_Polyhedra_Library::Constraint; friend class Parma_Polyhedra_Library::Congruence_System; friend class Parma_Polyhedra_Library::Congruence_System::const_iterator; // FIXME: The following friend declaration is at least for // operator[] access in Grid::conversion. friend class Parma_Polyhedra_Library::Grid; friend class Parma_Polyhedra_Library::Linear_Expression; friend void std::swap(Parma_Polyhedra_Library::Congruence& x, Parma_Polyhedra_Library::Congruence& y); }; /* Automatically generated from PPL source file ../src/Congruence.inlines.hh line 1. */ /* Congruence class implementation: inline functions. */ /* Automatically generated from PPL source file ../src/Congruence.inlines.hh line 29. */ #include namespace Parma_Polyhedra_Library { inline Congruence::Congruence(const Congruence& cg) : Row(cg) { } inline Congruence::Congruence(const Congruence& cg, dimension_type sz, dimension_type capacity) : Row(cg, sz, capacity) { } inline Congruence::Congruence(const Congruence& cg, Coefficient_traits::const_reference k) : Row(cg) { if (k >= 0) (*this)[size()-1] *= k; else (*this)[size()-1] *= -k; } inline Congruence::~Congruence() { } inline Congruence::Congruence(Linear_Expression& le, Coefficient_traits::const_reference m) { Row::swap(static_cast(le)); PPL_ASSERT(m >= 0); (*this)[size()-1] = m; } inline Congruence Congruence::create(const Linear_Expression& e, Coefficient_traits::const_reference n) { // Ensure that diff has capacity for the modulus. Linear_Expression diff(e, e.space_dimension() + 2); diff -= n; Congruence cg(diff, 1); return cg; } inline Congruence Congruence::create(Coefficient_traits::const_reference n, const Linear_Expression& e) { // Ensure that diff has capacity for the modulus. Linear_Expression diff(e, e.space_dimension() + 2); diff -= n; Congruence cg(diff, 1); 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, 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& c) { Row::operator=(c); return *this; } /*! \relates Congruence */ inline Congruence operator/(const Constraint& c, Coefficient_traits::const_reference m) { Congruence ret(c); return ret / m; } inline Congruence& Congruence::operator/=(Coefficient_traits::const_reference k) { if (k >= 0) (*this)[size()-1] *= k; else (*this)[size()-1] *= -k; return *this; } /*! \relates Congruence */ inline bool operator==(const Congruence& x, const Congruence& y) { Congruence x_temp(x); Congruence y_temp(y); x_temp.strong_normalize(); y_temp.strong_normalize(); return static_cast(x_temp) == static_cast(y_temp); } /*! \relates Congruence */ inline bool operator!=(const Congruence& x, const Congruence& y) { return !(x == y); } inline dimension_type Congruence::max_space_dimension() { // The first coefficient holds the inhomogeneous term, while // the last coefficient is for the modulus. return max_size() - 2; } inline dimension_type Congruence::space_dimension() const { return size() - 2; } 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 (*this)[v.id()+1]; } inline Coefficient_traits::const_reference Congruence::inhomogeneous_term() const { return (*this)[0]; } inline Coefficient_traits::const_reference Congruence::modulus() const { PPL_ASSERT(size() > 1); return (*this)[size()-1]; } 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(dimension_type dim, const Congruence& cg) const { return operator[](dim) * cg.modulus() == cg[dim] * modulus(); } inline void Congruence::set_is_equality() { (*this)[size()-1] = 0; } inline void Congruence::negate(dimension_type start, dimension_type end) { while (start <= end) neg_assign(operator[](start++)); } inline memory_size_type Congruence::external_memory_in_bytes() const { return Row::external_memory_in_bytes(); } inline memory_size_type Congruence::total_memory_in_bytes() const { return Row::total_memory_in_bytes(); } inline void Congruence::swap(Congruence& y) { Row::swap(y); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Congruence */ inline void swap(Parma_Polyhedra_Library::Congruence& x, Parma_Polyhedra_Library::Congruence& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/Congruence.defs.hh line 479. */ /* 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_System.defs.hh line 1. */ /* Grid_Generator_System class declaration. */ /* 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/Grid_Generator_System.defs.hh line 32. */ #include namespace Parma_Polyhedra_Library { namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Grid_Generator_System Writes false 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 //! Returns true 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 namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Grid_Generator_System */ void swap(Parma_Polyhedra_Library::Grid_Generator_System& x, Parma_Polyhedra_Library::Grid_Generator_System& y); } // namespace std //! 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 x and y 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 exact copy of them can be retrieved: in general, only an equivalent 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 : private Generator_System { public: //! Default constructor: builds an empty system of generators. Grid_Generator_System(); //! Builds the singleton system containing only generator \p g. explicit Grid_Generator_System(const Grid_Generator& g); //! Builds an empty system of generators of dimension \p dim. explicit Grid_Generator_System(dimension_type dim); //! Ordinary copy constructor. Grid_Generator_System(const Grid_Generator_System& gs); //! Destructor. ~Grid_Generator_System(); //! Assignment operator. Grid_Generator_System& operator=(const Grid_Generator_System& y); //! 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 recycling_insert(Grid_Generator& g); /*! \brief Inserts into \p *this the generators in \p gs, increasing the number of space dimensions if needed. */ void recycling_insert(Grid_Generator_System& gs); //! 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 gr: \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(cout, "\n")); \endcode */ class const_iterator : public std::iterator, private Generator_System::const_iterator { 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 true if and only if \p *this and \p y are identical. */ bool operator==(const const_iterator& y) const; /*! \brief Returns true if and only if \p *this and \p y are different. */ bool operator!=(const const_iterator& y) const; private: friend class Grid_Generator_System; //! Copy constructor from Generator_System::const_iterator. const_iterator(const Generator_System::const_iterator& y); }; //! Returns true 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 true if and only if \p *this contains one or more points. */ bool has_points() const; //! Returns true 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. /*! Returns true if and only if \p *this is a valid Linear_System and each row in the system is a valid Grid_Generator. */ 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 true if successful, false 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 swap(Grid_Generator_System& y); private: /*! \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 class Grid; friend bool operator==(const Grid_Generator_System& x, const Grid_Generator_System& y); //! Sets the sortedness flag of the system to \p b. void set_sorted(bool b); //! 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); //! Returns the \p k- th generator of the system. Grid_Generator& operator[](dimension_type k); //! 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 Index of the column 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 each element of \p v -th 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(dimension_type v, const Linear_Expression& expr, Coefficient_traits::const_reference denominator); /*! \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); //! 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); /*! \brief Removes the higher dimensions of the system so that the resulting system will have dimension \p new_dimension. The value of \p new_dimension must be at most the space dimension of \p *this. */ void remove_higher_space_dimensions(dimension_type new_dimension); //! Resizes the system without worrying about the old contents. /*! \param new_num_rows The number of rows of the resized system; \param new_num_columns The number of columns of the resized system. The system is expanded to the specified dimensions avoiding reallocation whenever possible. The contents of the original system is lost. */ void resize_no_copy(dimension_type new_num_rows, dimension_type new_num_columns); /*! \brief Returns the number of columns of the matrix (i.e., the size of the rows). */ dimension_type num_columns() const; /*! \brief Erases from the matrix all the rows but those having an index less than \p first_to_erase. */ void erase_to_end(dimension_type first_to_erase); //! Permutes the columns of the matrix. /* \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 non-zero column indexes. Each cycle is terminated by zero. For example, assuming the matrix has 6 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. */ void permute_columns(const std::vector& cycles); }; // Grid_Generator_System.inlines.hh is not included here on purpose. /* Automatically generated from PPL source file ../src/Grid_Generator.defs.hh line 32. */ #include namespace Parma_Polyhedra_Library { // Put these in the namespace here to declare them friend later. namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Grid_Generator */ std::ostream& operator<<(std::ostream& s, const Grid_Generator& g); } // namespace IO_Operators } // namespace Parma_Polyhedra_Library namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Grid_Generator */ void swap(Parma_Polyhedra_Library::Grid_Generator& x, Parma_Polyhedra_Library::Grid_Generator& y); } // namespace std //! 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 (grid_line, parameter or grid_point) 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 divisor 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 x, y and z 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 point 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 grid_point (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 g1 is a grid point having coordinates \f$(a_0, \ldots, a_{n-1})^\transpose\f$, we construct the parameter g2 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 coefficient with the notion of coordinate: these are equivalent only when the divisor is 1. */ class Parma_Polyhedra_Library::Grid_Generator : private Generator { public: //! 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); //! Returns the parameter of direction \p e and size \p e/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 parameter(const Linear_Expression& e = Linear_Expression::zero(), Coefficient_traits::const_reference d = Coefficient_one()); //! 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()); //! Ordinary copy constructor. Grid_Generator(const Grid_Generator& g); //! Destructor. ~Grid_Generator(); //! Assignment operator. Grid_Generator& operator=(const Grid_Generator& g); //! Assignment operator. Grid_Generator& operator=(const Generator& g); //! 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; //! 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 true if and only if \p *this is a line. bool is_line() const; //! Returns true if and only if \p *this is a parameter. bool is_parameter() const; /*! \brief Returns true if and only if \p *this is a line or a parameter. */ bool is_line_or_parameter() const; //! Returns true if and only if \p *this is a point. bool is_point() const; /*! \brief Returns true 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 true 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 true if \p *this is exactly equal to \p y. bool is_equal_to(const Grid_Generator& y) const; /*! \brief Returns true 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 Returns true if and only if all the homogeneous terms of \p *this are \f$0\f$. */ bool all_homogeneous_terms_are_zero() 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 true if successful, false otherwise. */ bool ascii_load(std::istream& s); //! Checks if all the invariants are satisfied. bool OK() const; //! Swaps \p *this with \p y. void swap(Grid_Generator& y); /*! \brief Swaps \p *this with \p y, leaving \p *this with the original capacity. All elements up to and including the last element of the smaller of \p *this and \p y are swapped. The parameter divisor element of \p y is swapped with the divisor element of \p *this. */ void coefficient_swap(Grid_Generator& y); private: /*! \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; /*! \brief Scales \p *this to be represented with a divisor of \p d (if \*this is a parameter or point). It is assumed that \p d is a multiple of the current divisor. \exception std::invalid_argument Thrown if \p d is zero. */ void scale_to_divisor(Coefficient_traits::const_reference d); /*! \brief Constructs from polyhedron generator \p g, stealing the underlying data structures from \p g. The last column in \p g becomes the parameter divisor column of the new Grid_Generator. */ explicit Grid_Generator(Generator g); //! Returns the actual size of \p this. dimension_type size() const; //! Negates the elements from index \p start to index \p end. void negate(dimension_type start, dimension_type end); //! 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); //! Sets the Linear_Row kind to LINE_OR_EQUALITY. void set_is_line(); //! Sets the Linear_Row kind to RAY_OR_POINT_OR_INEQUALITY. void set_is_parameter_or_point(); //! Converts the Grid_Generator into a parameter. void set_is_parameter(); /*! \brief Strong normalization: ensures that different Grid_Generator objects represent different hyperplanes or hyperspaces. Applies both Linear_Row::normalize() and Linear_Row::sign_normalize(). This is simply a wrapper around the Generator::strong_normalize, which means applying it to a parameter may change the parameter. */ void strong_normalize(); //! 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; /*! \brief Throw a std::invalid_argument 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); // FIXME: The following friend declaration is for operator[] and // divisor() access in Grid::conversion, Grid::simplify, // Grid::relation_with(c) and Grid::Grid(box, *). friend class Grid; friend class Grid_Generator_System; friend class Grid_Generator_System::const_iterator; friend class Congruence_System; friend class Scalar_Products; friend class Topology_Adjusted_Scalar_Product_Sign; friend class Linear_Expression; }; namespace Parma_Polyhedra_Library { /*! \brief Shorthand for Grid_Generator Grid_Generator::grid_line(const Linear_Expression& e). */ /*! \relates Grid_Generator */ Grid_Generator grid_line(const Linear_Expression& e); /*! \brief Shorthand for Grid_Generator Grid_Generator::parameter(const Linear_Expression& e, Coefficient_traits::const_reference d). */ /*! \relates Grid_Generator */ Grid_Generator parameter(const Linear_Expression& e = Linear_Expression::zero(), Coefficient_traits::const_reference d = Coefficient_one()); /*! \brief Shorthand for Grid_Generator Grid_Generator::grid_point(const Linear_Expression& e, Coefficient_traits::const_reference d). */ /*! \relates Grid_Generator */ Grid_Generator grid_point(const Linear_Expression& e = Linear_Expression::zero(), Coefficient_traits::const_reference d = Coefficient_one()); //! Returns true 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 true 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 Grid_Generator::Grid_Generator(const Grid_Generator& g) : Generator(g) { } inline Grid_Generator::~Grid_Generator() { } inline Grid_Generator::Grid_Generator(Generator g) : Generator(Generator::point()) { Generator::swap(g); } inline dimension_type Grid_Generator::size() const { return Generator::size(); } inline dimension_type Grid_Generator::max_space_dimension() { return Generator::max_space_dimension() - 1; } inline Coefficient& Grid_Generator::operator[](dimension_type k) { return Generator::operator[](k); } inline Coefficient_traits::const_reference Grid_Generator::operator[](dimension_type k) const { return Generator::operator[](k); } inline dimension_type Grid_Generator::space_dimension() const { return Generator::space_dimension() - 1; } inline Grid_Generator::Type Grid_Generator::type() const { switch (Generator::type()) { case Generator::POINT: return POINT; case Generator::RAY: return PARAMETER; case Generator::LINE: return LINE; case Generator::CLOSURE_POINT: default: PPL_ASSERT(false); return POINT; } } inline bool Grid_Generator::is_line() const { return Generator::is_line(); } inline bool Grid_Generator::is_parameter() const { return is_ray(); } inline bool Grid_Generator::is_line_or_parameter() const { return is_line_or_ray(); } inline bool Grid_Generator::is_point() const { return Generator::is_point(); } 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()) Generator::operator[](size() - 1) = d; else Generator::operator[](0) = 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 Generator::operator[](size() - 1); else return Generator::operator[](0); } inline bool Grid_Generator::is_equal_at_dimension(dimension_type dim, const Grid_Generator& gg) const { return operator[](dim) * gg.divisor() == gg[dim] * divisor(); } inline void Grid_Generator::set_is_line() { Generator::set_is_line(); } inline void Grid_Generator::set_is_parameter_or_point() { Generator::set_is_ray_or_point(); } inline Grid_Generator& Grid_Generator::operator=(const Grid_Generator& g) { Generator::operator=(g); return *this; } inline Grid_Generator& Grid_Generator::operator=(const Generator& g) { Generator::operator=(g); return *this; } inline void Grid_Generator::negate(dimension_type start, dimension_type end) { while (start <= end) neg_assign(operator[](start++)); } 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 Generator::coefficient(v); } inline memory_size_type Grid_Generator::total_memory_in_bytes() const { return Generator::total_memory_in_bytes(); } inline memory_size_type Grid_Generator::external_memory_in_bytes() const { return Generator::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() { Generator::strong_normalize(); } inline void Grid_Generator::swap(Grid_Generator& y) { Generator::swap(y); } /*! \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) { return Grid_Generator::grid_line(e); } /*! \relates Grid_Generator */ inline Grid_Generator parameter(const Linear_Expression& e, Coefficient_traits::const_reference d) { return Grid_Generator::parameter(e, d); } /*! \relates Grid_Generator */ inline Grid_Generator grid_point(const Linear_Expression& e, Coefficient_traits::const_reference d) { return Grid_Generator::grid_point(e, d); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Grid_Generator */ inline void swap(Parma_Polyhedra_Library::Grid_Generator& x, Parma_Polyhedra_Library::Grid_Generator& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/Grid_Generator.defs.hh line 535. */ /* Automatically generated from PPL source file ../src/Scalar_Products.inlines.hh line 33. */ namespace Parma_Polyhedra_Library { inline int Scalar_Products::sign(const Linear_Row& x, const Linear_Row& y) { PPL_DIRTY_TEMP_COEFFICIENT(z); assign(z, x, y); return sgn(z); } inline int Scalar_Products::reduced_sign(const Linear_Row& x, const Linear_Row& y) { PPL_DIRTY_TEMP_COEFFICIENT(z); reduced_assign(z, x, y); return sgn(z); } inline int Scalar_Products::homogeneous_sign(const Linear_Row& x, const Linear_Row& 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(static_cast(c), static_cast(g)); } inline int Scalar_Products::sign(const Generator& g, const Constraint& c) { return sign(static_cast(g), static_cast(c)); } 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) { return reduced_sign(static_cast(c), static_cast(g)); } inline int Scalar_Products::reduced_sign(const Generator& g, const Constraint& c) { return reduced_sign(static_cast(g), static_cast(c)); } inline void Scalar_Products::homogeneous_assign(Coefficient& z, const Linear_Expression& e, const Generator& g) { homogeneous_assign(z, static_cast(e), static_cast(g)); } inline void Scalar_Products::homogeneous_assign(Coefficient& z, const Linear_Expression& e, const Grid_Generator& g) { homogeneous_assign(z, static_cast(e), static_cast(g)); } inline int Scalar_Products::homogeneous_sign(const Linear_Expression& e, const Generator& g) { return homogeneous_sign(static_cast(e), static_cast(g)); } inline int Scalar_Products::homogeneous_sign(const Linear_Expression& e, const Grid_Generator& g) { return homogeneous_sign(static_cast(e), static_cast(g)); } 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(&Scalar_Products::sign) : static_cast(&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(&Scalar_Products::sign) : static_cast(&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(&Scalar_Products::sign) : static_cast(&Scalar_Products::reduced_sign))); return sps_fp(static_cast(c), static_cast(g)); } 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(&Scalar_Products::sign) : static_cast(&Scalar_Products::reduced_sign))); return sps_fp(static_cast(g), static_cast(c)); } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Scalar_Products.defs.hh line 197. */ /* 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/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() : Linear_System(NECESSARILY_CLOSED) { } inline Constraint_System::Constraint_System(const Constraint& c) : Linear_System(c.topology()) { Linear_System::insert(c); } inline Constraint_System::Constraint_System(const Constraint_System& cs) : Linear_System(cs) { } inline Constraint_System::Constraint_System(const Topology topol) : Linear_System(topol) { } inline Constraint_System::Constraint_System(const Topology topol, const dimension_type n_rows, const dimension_type n_columns) : Linear_System(topol, n_rows, n_columns) { } inline Constraint_System::~Constraint_System() { } inline Constraint_System& Constraint_System::operator=(const Constraint_System& y) { Linear_System::operator=(y); return *this; } inline Constraint& Constraint_System::operator[](const dimension_type k) { return static_cast(Linear_System::operator[](k)); } inline const Constraint& Constraint_System::operator[](const dimension_type k) const { return static_cast(Linear_System::operator[](k)); } inline dimension_type Constraint_System::max_space_dimension() { return Linear_System::max_space_dimension(); } inline dimension_type Constraint_System::space_dimension() const { return Linear_System::space_dimension(); } inline void Constraint_System::clear() { Linear_System::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::const_iterator() : i(), csp(0) { } inline Constraint_System::const_iterator::const_iterator(const const_iterator& y) : i(y.i), csp(y.csp) { } inline Constraint_System::const_iterator::~const_iterator() { } inline Constraint_System::const_iterator& Constraint_System::const_iterator::operator=(const const_iterator& y) { i = y.i; csp = y.csp; return *this; } inline const Constraint& Constraint_System::const_iterator::operator*() const { return static_cast(*i); } inline const Constraint* Constraint_System::const_iterator::operator->() const { return static_cast(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 const_iterator tmp = *this; operator++(); return tmp; } inline bool Constraint_System::const_iterator::operator==(const const_iterator& y) const { return i == y.i; } inline bool Constraint_System::const_iterator::operator!=(const const_iterator& y) const { return i != y.i; } inline Constraint_System::const_iterator:: const_iterator(const Linear_System::const_iterator& iter, const Constraint_System& csys) : i(iter), csp(&csys) { } inline Constraint_System::const_iterator Constraint_System::begin() const { const_iterator i(Linear_System::begin(), *this); i.skip_forward(); return i; } inline Constraint_System::const_iterator Constraint_System::end() const { const const_iterator i(Linear_System::end(), *this); return i; } inline bool Constraint_System::empty() const { return begin() == end(); } inline void Constraint_System::add_low_level_constraints() { if (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::swap(Constraint_System& y) { Linear_System::swap(y); } inline memory_size_type Constraint_System::external_memory_in_bytes() const { return Linear_System::external_memory_in_bytes(); } inline memory_size_type Constraint_System::total_memory_in_bytes() const { return Linear_System::total_memory_in_bytes(); } inline void Constraint_System::simplify() { Linear_System::simplify(); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Constraint_System */ inline void swap(Parma_Polyhedra_Library::Constraint_System& x, Parma_Polyhedra_Library::Constraint_System& y) { x.swap(y); } } // namespace std /* 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 #include namespace Parma_Polyhedra_Library { namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Variables_Set */ std::ostream& operator<<(std::ostream& s, const Variables_Set& v); } // namespace IO_Operators } // namespace Parma_Polyhedra_Library //! An std::set of variables' indexes. class Parma_Polyhedra_Library::Variables_Set : public std::set { private: typedef std::set Base; public: //! Builds the empty set of variable indexes. Variables_Set(); //! Builds the singleton set of indexes containing v.id(); explicit Variables_Set(const Variable& v); /*! \brief Builds the set of variables's indexes in the range from v.id() to w.id(). If v.id() <= w.id(), this constructor builds the set of variables' indexes v.id(), v.id()+1, ..., w.id(). 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 true if successful, false 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 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/PIP_Problem.defs.hh line 1. */ /* PIP_Problem class declaration. */ /* 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/PIP_Problem.defs.hh line 35. */ #include #include #include namespace Parma_Polyhedra_Library { namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::PIP_Problem */ std::ostream& operator<<(std::ostream& s, const PIP_Problem& p); } // namespace IO_Operators } // 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 feasible region, if they involve one or more problem variable (and maybe some parameters); - the initial context, if they only involve the parameters; - optionally, the so-called big parameter, 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 7*n >= 10. 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 {i,j} 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 Variable::set_output_function): \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 contraints 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 dim+1, 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 recusrively 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 max_space_dimension(). */ 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 max_space_dimension(). \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 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_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 m_vars + m_params 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 m_vars + m_params new space dimensions would cause the vector space to exceed dimension max_space_dimension(). 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, unsigned 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 true if successful, false 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 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 the row which generates the lexico-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; /*! \brief The initial context Contains problem constraints on parameters only */ Matrix 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; }; namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::PIP_Problem */ void swap(Parma_Polyhedra_Library::PIP_Problem& x, Parma_Polyhedra_Library::PIP_Problem& y); } // namespace std /* 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::swap(PIP_Problem& y) { std::swap(external_space_dim, y.external_space_dim); std::swap(internal_space_dim, y.internal_space_dim); std::swap(status, y.status); std::swap(current_solution, y.current_solution); std::swap(input_cs, y.input_cs); std::swap(first_pending_constraint, y.first_pending_constraint); std::swap(parameters, y.parameters); std::swap(initial_context, y.initial_context); for (dimension_type i = CONTROL_PARAMETER_NAME_SIZE; i-- > 0; ) std::swap(control_parameters[i], y.control_parameters[i]); std::swap(big_parameter_dimension, y.big_parameter_dimension); } inline PIP_Problem& PIP_Problem::operator=(const PIP_Problem& y) { PIP_Problem tmp(y); swap(tmp); return *this; } inline PIP_Problem::Control_Parameter_Value PIP_Problem::get_control_parameter(Control_Parameter_Name n) const { PPL_ASSERT(n >= 0 && n < CONTROL_PARAMETER_NAME_SIZE); return control_parameters[n]; } inline dimension_type PIP_Problem::get_big_parameter_dimension() const { return big_parameter_dimension; } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::PIP_Problem */ inline void swap(Parma_Polyhedra_Library::PIP_Problem& x, Parma_Polyhedra_Library::PIP_Problem& y) { x.swap(y); } } // namespace std /* 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 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 830. */ /* Automatically generated from PPL source file ../src/PIP_Tree.defs.hh line 37. */ 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 is owned by \p *pip. */ virtual bool check_ownership(const PIP_Problem* owner) const = 0; public: //! 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; //! Returns \p this if \p *this is a solution node, 0 otherwise. virtual const PIP_Solution_Node* as_solution() const; //! Returns \p this if \p *this is a decision node, 0 otherwise. virtual const PIP_Decision_Node* as_decision() const; /*! \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_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, unsigned 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 true if successful, false 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_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& context, const Variables_Set& params, dimension_type space_dim, unsigned indent_level) = 0; //! Inserts a new parametric constraint in internal Row format void add_constraint(const Row& x, 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, unsigned indent, const std::vector& pip_dim_is_param, dimension_type first_art_dim) const; //! A helper function used when printing PIP trees. static void indent_and_print(std::ostream& s, unsigned 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& 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& 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{\mathit{expr}}{\mathit{den}}\f$. \param expr The expression that, after normalization, will form the numerator of the artificial parameter. \param den The integer constant thatm after normalization, will form the denominator of the artificial parameter. \exception std::invalid_argument Thrown if \p den is zero. Normalization will ensure that the denominator is positive. */ Artificial_Parameter(const Linear_Expression& expr, Coefficient_traits::const_reference den); //! 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 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 true if successful, false 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 //! 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; /*! \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 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 true if successful, false 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: //! The type for parametric simplex tableau. struct Tableau { //! The matrix of simplex coefficients. Matrix s; //! The matrix of parameter coefficients. Matrix 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$th column from the \f$s\f$ matrix and \f$s_{*j'}\f$ is the \f$j'\f$th 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 i the row number for the first pivot row and column pair to be compared \param j the column number for the first pivot row and column pair to be compared \param i_ the row number for the second pivot row and column pair to be compared \param j_ the column number for the second pivot row and column pair to be compared */ bool is_better_pivot(const std::vector& mapping, const std::vector& basis, const dimension_type i, const dimension_type j, const dimension_type i_, const dimension_type j_) const; //! Returns the value of the denominator. Coefficient_traits::const_reference denominator() 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 \c true if successful, \c false otherwise. */ bool ascii_load(std::istream& s); //! 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 n+m-1, 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 n-1 correspond to the original variables. Indices from \p n to n+m-1 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 basis[i] is: - \b true if variable \p i is basic, - \b false if variable \p i is nonbasic. */ std::vector basis; /*! \brief A mapping between the tableau rows/columns and the original variables. The value of mapping[i] depends of the value of basis[i]. - If basis[i] is \b true, mapping[i] encodes the column index of variable \p i in the \p s matrix of the tableau. - If basis[i] is \b false, mapping[i] encodes the row index of variable \p i in the tableau. */ std::vector mapping; /*! \brief The variable identifiers associated to the rows of the simplex tableau. */ std::vector var_row; /*! \brief The variable identifiers associated to the columns of the simplex tableau. */ std::vector var_column; /*! \brief The variable number of the special inequality used for modelling 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 modelized the following way: - \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 modelize 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 sign; //! Parametric values for the solution. std::vector 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& 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& context, const Variables_Set& params, dimension_type space_dim, unsigned indent_level); /*! \brief Generate a Gomory cut using non-integer tableau row \p i. \param i 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 i, Variables_Set& parameters, Matrix& context, dimension_type& space_dimension, unsigned indent_level); //! Prints on \p s the tree rooted in \p *this. virtual void print_tree(std::ostream& s, unsigned indent, const std::vector& 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 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 true if successful, false 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& context, const Variables_Set& params, dimension_type space_dim, unsigned indent_level); //! Prints on \p s the tree rooted in \p *this. virtual void print_tree(std::ostream& s, unsigned indent, const std::vector& 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 PIP_Tree_Node::~PIP_Tree_Node() { } 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 v) const { return v ? true_child : false_child; } inline PIP_Tree_Node* PIP_Decision_Node::child_node(bool v) { return v ? 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::swap(Artificial_Parameter& y) { Linear_Expression::swap(y); std::swap(denom, y.denom); } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/PIP_Tree.defs.hh line 815. */ /* 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.types.hh line 1. */ namespace Parma_Polyhedra_Library { class BHRZ03_Certificate; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/BHRZ03_Certificate.defs.hh line 31. */ #include //! 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 true 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 true 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 num_rays_null_coord; }; /* Automatically generated from PPL source file ../src/BHRZ03_Certificate.inlines.hh line 1. */ /* BHRZ03_Certificate class implementation: inline functions. */ 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 117. */ /* 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.types.hh line 1. */ namespace Parma_Polyhedra_Library { class H79_Certificate; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/H79_Certificate.defs.hh line 31. */ #include //! 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 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 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 true 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/Polyhedron.defs.hh line 1. */ /* Polyhedron class declaration. */ /* 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() : Linear_System(NECESSARILY_CLOSED) { } inline Generator_System::Generator_System(const Generator& g) : Linear_System(g.topology()) { Linear_System::insert(g); } inline Generator_System::Generator_System(const Generator_System& gs) : Linear_System(gs) { } inline Generator_System::Generator_System(const Topology topol) : Linear_System(topol) { } inline Generator_System::Generator_System(const Topology topol, const dimension_type n_rows, const dimension_type n_columns) : Linear_System(topol, n_rows, n_columns) { } inline Generator_System::~Generator_System() { } inline Generator_System& Generator_System::operator=(const Generator_System& y) { Linear_System::operator=(y); return *this; } inline dimension_type Generator_System::max_space_dimension() { return Linear_System::max_space_dimension(); } inline dimension_type Generator_System::space_dimension() const { return Linear_System::space_dimension(); } inline void Generator_System::clear() { Linear_System::clear(); } inline Generator& Generator_System::operator[](const dimension_type k) { return static_cast(Linear_System::operator[](k)); } inline const Generator& Generator_System::operator[](const dimension_type k) const { return static_cast(Linear_System::operator[](k)); } inline Generator_System::const_iterator::const_iterator() : i(), gsp(0) { } inline Generator_System::const_iterator::const_iterator(const const_iterator& y) : i(y.i), gsp(y.gsp) { } inline Generator_System::const_iterator::~const_iterator() { } inline Generator_System::const_iterator& Generator_System::const_iterator::operator=(const const_iterator& y) { i = y.i; gsp = y.gsp; return *this; } inline const Generator& Generator_System::const_iterator::operator*() const { return static_cast(*i); } inline const Generator* Generator_System::const_iterator::operator->() const { return static_cast(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 const_iterator tmp = *this; operator++(); return tmp; } inline bool Generator_System::const_iterator::operator==(const const_iterator& y) const { return i == y.i; } inline bool Generator_System::const_iterator::operator!=(const const_iterator& y) const { return i != y.i; } inline Generator_System::const_iterator:: const_iterator(const Linear_System::const_iterator& iter, const Generator_System& gsys) : i(iter), gsp(&gsys) { } inline bool Generator_System::empty() const { return Linear_System::has_no_rows(); } inline Generator_System::const_iterator Generator_System::begin() const { const_iterator i(Linear_System::begin(), *this); if (!is_necessarily_closed()) i.skip_forward(); return i; } inline Generator_System::const_iterator Generator_System::end() const { const const_iterator i(Linear_System::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::swap(Generator_System& y) { Linear_System::swap(y); } inline memory_size_type Generator_System::external_memory_in_bytes() const { return Linear_System::external_memory_in_bytes(); } inline memory_size_type Generator_System::total_memory_in_bytes() const { return Linear_System::total_memory_in_bytes(); } inline void Generator_System::simplify() { Linear_System::simplify(); remove_invalid_lines_and_rays(); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Constraint_System */ inline void swap(Parma_Polyhedra_Library::Generator_System& x, Parma_Polyhedra_Library::Generator_System& y) { x.swap(y); } } // namespace std /* 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 Congruence& Congruence_System::operator[](const dimension_type k) { return static_cast(Matrix::operator[](k)); } inline const Congruence& Congruence_System::operator[](const dimension_type k) const { return static_cast(Matrix::operator[](k)); } inline void Congruence_System::insert(const Congruence& cg) { insert_verbatim(cg); static_cast(operator[](rows.size()-1)).strong_normalize(); PPL_ASSERT(OK()); } inline Congruence_System::Congruence_System() : Matrix(0, 2) { } inline Congruence_System::Congruence_System(const Congruence& cg) : Matrix(0, 2) { insert(cg); } inline Congruence_System::Congruence_System(const Constraint& c) : Matrix(0, 2) { insert(c); } inline Congruence_System::Congruence_System(const Congruence_System& cs) : Matrix(cs) { } inline Congruence_System::Congruence_System(const dimension_type d) : Matrix(0, d + 2) { } inline Congruence_System::~Congruence_System() { } inline Congruence_System& Congruence_System::operator=(const Congruence_System& y) { Matrix::operator=(y); return *this; } inline dimension_type Congruence_System::max_space_dimension() { return Matrix::max_num_columns() - 2; } inline dimension_type Congruence_System::space_dimension() const { return Matrix::num_columns() - 2; } inline void Congruence_System::clear() { Matrix::clear(); add_zero_columns(2); // Modulus and constant term. } inline void Congruence_System::resize_no_copy(const dimension_type new_num_rows, const dimension_type new_num_columns) { Matrix::resize_no_copy(new_num_rows, new_num_columns, Row::Flags()); } 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 static_cast(*i); } inline const Congruence* Congruence_System::const_iterator::operator->() const { return static_cast(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 Matrix::const_iterator& iter, const Congruence_System& csys) : i(iter), csp(&csys) { } inline Congruence_System::const_iterator Congruence_System::begin() const { const_iterator i(Matrix::begin(), *this); i.skip_forward(); return i; } inline Congruence_System::const_iterator Congruence_System::end() const { const const_iterator i(Matrix::end(), *this); return i; } inline bool Congruence_System::empty() const { return begin() == end(); } inline void Congruence_System::swap(Congruence_System& y) { Matrix::swap(y); } inline memory_size_type Congruence_System::external_memory_in_bytes() const { return Matrix::external_memory_in_bytes(); } inline memory_size_type Congruence_System::total_memory_in_bytes() const { return Matrix::total_memory_in_bytes(); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Congruence_System */ inline void swap(Parma_Polyhedra_Library::Congruence_System& x, Parma_Polyhedra_Library::Congruence_System& y) { x.swap(y); } } // namespace std /* 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 29. */ namespace Parma_Polyhedra_Library { inline void Grid_Generator_System::set_sorted(bool b) { Generator_System::set_sorted(b); } inline void Grid_Generator_System::unset_pending_rows() { Generator_System::unset_pending_rows(); } inline void Grid_Generator_System::set_index_first_pending_row(const dimension_type i) { Generator_System::set_index_first_pending_row(i); } inline void Grid_Generator_System::resize_no_copy(const dimension_type new_num_rows, const dimension_type new_num_columns) { Generator_System::resize_no_copy(new_num_rows, new_num_columns); } inline dimension_type Grid_Generator_System::num_columns() const { return Generator_System::num_columns(); } inline void Grid_Generator_System::erase_to_end(dimension_type first_to_erase) { return Generator_System::erase_to_end(first_to_erase); } inline void Grid_Generator_System ::permute_columns(const std::vector& cycles) { return Generator_System::permute_columns(cycles); } inline bool Grid_Generator_System::is_equal_to(const Grid_Generator_System& y) const { return operator==(static_cast(*this), static_cast(y)); } inline Grid_Generator_System::Grid_Generator_System() : Generator_System(NECESSARILY_CLOSED) { adjust_topology_and_space_dimension(NECESSARILY_CLOSED, 1); set_sorted(false); } inline Grid_Generator_System::Grid_Generator_System(const Grid_Generator_System& gs) : Generator_System(gs) { } inline Grid_Generator_System::Grid_Generator_System(dimension_type dim) : Generator_System(NECESSARILY_CLOSED) { adjust_topology_and_space_dimension(NECESSARILY_CLOSED, dim + 1); set_sorted(false); } inline Grid_Generator_System::Grid_Generator_System(const Grid_Generator& g) : Generator_System(g) { set_sorted(false); } inline Grid_Generator_System::~Grid_Generator_System() { } inline Grid_Generator_System& Grid_Generator_System::operator=(const Grid_Generator_System& y) { Generator_System::operator=(y); return *this; } inline dimension_type Grid_Generator_System::max_space_dimension() { // Grid generators use an extra column for the parameter divisor. return Generator_System::max_space_dimension() - 1; } inline dimension_type Grid_Generator_System::space_dimension() const { PPL_ASSERT(Generator_System::space_dimension() > 0); // Grid generators use an extra column for the parameter divisor. return Generator_System::space_dimension() - 1; } 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() { Generator_System::clear(); // For grid generators, two extra columns are needed. add_zero_columns(2); set_sorted(false); unset_pending_rows(); } inline void Grid_Generator_System::swap(Grid_Generator_System& y) { Generator_System::swap(y); } inline memory_size_type Grid_Generator_System::external_memory_in_bytes() const { return Generator_System::external_memory_in_bytes(); } inline memory_size_type Grid_Generator_System::total_memory_in_bytes() const { return Generator_System::total_memory_in_bytes(); } inline dimension_type Grid_Generator_System::num_rows() const { return Generator_System::num_rows(); } inline dimension_type Grid_Generator_System::num_parameters() const { return Generator_System::num_rays(); } inline dimension_type Grid_Generator_System::num_lines() const { return Generator_System::num_lines(); } inline Grid_Generator_System::const_iterator::const_iterator() : Generator_System::const_iterator() { } inline Grid_Generator_System::const_iterator::const_iterator(const const_iterator& y) : Generator_System::const_iterator(y) { } inline Grid_Generator_System::const_iterator::~const_iterator() { } inline Grid_Generator_System::const_iterator& Grid_Generator_System::const_iterator::operator=(const const_iterator& y) { return static_cast (Generator_System::const_iterator::operator=(y)); } inline const Grid_Generator& Grid_Generator_System::const_iterator::operator*() const { return static_cast (Generator_System::const_iterator::operator*()); } inline const Grid_Generator* Grid_Generator_System::const_iterator::operator->() const { return static_cast (Generator_System::const_iterator::operator->()); } inline Grid_Generator_System::const_iterator& Grid_Generator_System::const_iterator::operator++() { return static_cast (Generator_System::const_iterator::operator++()); } 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 Generator_System::const_iterator::operator==(y); } inline bool Grid_Generator_System ::const_iterator::operator!=(const const_iterator& y) const { return Generator_System::const_iterator::operator!=(y); } inline bool Grid_Generator_System::empty() const { return Generator_System::empty(); } inline Grid_Generator_System ::const_iterator::const_iterator(const Generator_System::const_iterator& y) : Generator_System::const_iterator::const_iterator(y) { } inline Grid_Generator_System::const_iterator Grid_Generator_System::begin() const { return static_cast (Generator_System::begin()); } inline Grid_Generator_System::const_iterator Grid_Generator_System::end() const { return static_cast (Generator_System::end()); } inline bool Grid_Generator_System::has_points() const { return Generator_System::has_points(); } inline Grid_Generator& Grid_Generator_System::operator[](const dimension_type k) { return static_cast(Generator_System::operator[](k)); } inline const Grid_Generator& Grid_Generator_System::operator[](const dimension_type k) const { return static_cast(Generator_System::operator[](k)); } /*! \relates Grid_Generator_System */ inline bool operator==(const Grid_Generator_System& x, const Grid_Generator_System& y) { return x.is_equal_to(y); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Constraint_System */ inline void swap(Parma_Polyhedra_Library::Grid_Generator_System& x, Parma_Polyhedra_Library::Grid_Generator_System& y) { x.swap(y); } } // namespace std /* 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 #include #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 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 true 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); //! Erases the rows from the \p first_to_erase -th to the last one. void rows_erase_to_end(dimension_type first_to_erase); //! Erases the columns from the \p first_to_erase -th to the last one. void columns_erase_to_end(dimension_type first_to_erase); //! 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 true if successful, false 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 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; }; friend void Parma_Polyhedra_Library:: Linear_System::sort_and_remove_with_sat(Bit_Matrix& sat); }; namespace Parma_Polyhedra_Library { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true 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 true 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 namespace std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Bit_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) void swap(Parma_Polyhedra_Library::Bit_Matrix& x, Parma_Polyhedra_Library::Bit_Matrix& y); } // namespace std /* Automatically generated from PPL source file ../src/Bit_Matrix.inlines.hh line 1. */ /* Bit_Matrix class implementation: inline functions. */ #include /* Automatically generated from PPL source file ../src/Bit_Matrix.inlines.hh line 29. */ namespace Parma_Polyhedra_Library { inline Bit_Matrix::Bit_Matrix() : rows(), row_size(0) { } inline dimension_type Bit_Matrix::max_num_rows() { return std::vector().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::rows_erase_to_end(const dimension_type first_to_erase) { // The first row to be erased cannot be greater // than the actual number of the rows of the matrix. PPL_ASSERT(first_to_erase <= rows.size()); if (first_to_erase < rows.size()) rows.erase(rows.begin() + first_to_erase, rows.end()); PPL_ASSERT(OK()); } inline void Bit_Matrix::columns_erase_to_end(const dimension_type first_to_erase) { // The first column to be erased cannot be greater // than the actual number of the columns of the matrix. PPL_ASSERT(first_to_erase <= row_size); row_size = first_to_erase; PPL_ASSERT(OK()); } inline void Bit_Matrix::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().swap(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); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Bit_Matrix */ inline void swap(Parma_Polyhedra_Library::Bit_Matrix& x, Parma_Polyhedra_Library::Bit_Matrix& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/Bit_Matrix.defs.hh line 185. */ /* 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.types.hh line 1. */ namespace Parma_Polyhedra_Library { class Poly_Gen_Relation; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Poly_Gen_Relation.defs.hh line 29. */ #include 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/Box.types.hh line 1. */ namespace Parma_Polyhedra_Library { template class Box; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/BD_Shape.types.hh line 1. */ namespace Parma_Polyhedra_Library { template class BD_Shape; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Octagonal_Shape.types.hh line 1. */ namespace Parma_Polyhedra_Library { template class Octagonal_Shape; } /* Automatically generated from PPL source file ../src/Polyhedron.defs.hh line 51. */ #include #include namespace Parma_Polyhedra_Library { namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Polyhedron Writes a textual representation of \p ph on \p s: false is written if \p ph is an empty polyhedron; true 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 /*! \brief Returns true 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 false is returned. */ bool operator==(const Polyhedron& x, const Polyhedron& y); /*! \brief Returns true 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 true 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 ph.topology() == NECESSARILY_CLOSED. */ #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: - explicit 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 x and 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 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 add_space_dimensions_and_embed: \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 add_space_dimensions_and_project: \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 add_space_dimensions_and_embed. 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 affine_image: \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 affine_preimage: \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 remove_space_dimensions: \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 incrementally: since dimensions are automatically renamed after each application of the remove_space_dimensions operator, unexpected results can be obtained. For instance, by using the following code we would obtain a different result: \code set vars1; vars1.insert(y); ph.remove_space_dimensions(vars1); set 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 const 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 const 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 Polyhedron(Topology topol, const Box& 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; //! Returns a universe system of grid generators. Grid_Generator_System grid_generators() const; //! Returns a universe system of grid generators. Grid_Generator_System minimized_grid_generators() 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 true if and only if \p *this is an empty polyhedron. */ bool is_empty() const; /*! \brief Returns true if and only if \p *this is a universe polyhedron. */ bool is_universe() const; /*! \brief Returns true if and only if \p *this is a topologically closed subset of the vector space. */ bool is_topologically_closed() const; //! Returns true 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 true if and only if \p *this is discrete. bool is_discrete() const; /*! \brief Returns true if and only if \p *this is a bounded polyhedron. */ bool is_bounded() const; /*! \brief Returns true if and only if \p *this contains at least one integer point. */ bool contains_integer_point() const; /*! \brief Returns true 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 true 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 true 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 true 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 true 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, false 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 true 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 true 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, false 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 true 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 true 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, false 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 true 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 true 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, false 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 true if and only if there exist a unique value \p val such that \p *this saturates the equality expr = val. \param expr The linear expression for which the frequency is needed; \param freq_n If true 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 true 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 false 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 true 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 true 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 true if and only if \p *this satisfies all the invariants and either \p check_not_empty is false or \p *this is not empty. \param check_not_empty true 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 std::cerr 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 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()); /*! \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 \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 pcs Possibly null pointer to a constraint system whose variables are contained in \p vars. If *pcs 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 *pcs. \param complexity_threshold A precision parameter of the \ref Wrapping_Operator "wrapping operator": higher values result in possibly improved precision. \param wrap_individually true 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 *pcs is dimension-incompatible with \p vars, or if \p *this is dimension-incompatible \p vars or with *pcs. */ 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* pcs = 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 ANY_COMPLEXITY. */ 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 ANY_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 BHRZ03_widening "BHRZ03-widening" between \p *this and \p y. \param y A polyhedron that must 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 must 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 must 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 must 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 must 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 must 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 max_space_dimension(). 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 max_space_dimension(). 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 max_space_dimension(). */ 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 true if and only if the represented partial function has an empty codomain (i.e., it is always undefined). The has_empty_codomain() method will always be called before the methods below. However, if has_empty_codomain() returns true, 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 max_in_codomain() 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 true is returned. If \f$f\f$ is undefined in \f$k\f$, then false 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 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 max_space_dimension(). If \p *this has space dimension \f$n\f$, with \f$n > 0\f$, and var 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$, dest 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 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 true if successful, false 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 x == y, then x.hash_code() == y.hash_code(). */ int32_t hash_code() const; //@} // Miscellaneous Member Functions private: //! 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: - zero-dim universe: the polyhedron is the zero-dimension vector space \f$\Rset^0 = \{\cdot\}\f$; - empty: the polyhedron is the empty set; - constraints pending: 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; - generators pending: 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; - constraints up-to-date: the polyhedron is correctly characterized by the attached system of constraints, modulo the processing of pending generators; - generators up-to-date: the polyhedron is correctly characterized by the attached system of generators, modulo the processing of pending constraints; - constraints minimized: the non-pending part of the system of constraints attached to the polyhedron is in minimal form; - generators minimized: the non-pending part of the system of generators attached to the polyhedron is in minimal form; - constraints' saturation matrix up-to-date: 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; - generators' saturation matrix up-to-date: 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: - zero-dim universe excludes any other assertion; - empty: excludes any other assertion; - constraints pending and generators pending are mutually exclusive; - constraints pending implies both constraints minimized and generators minimized; - generators pending implies both constraints minimized and generators minimized; - constraints minimized implies constraints up-to-date; - generators minimized implies generators up-to-date; - constraints' saturation matrix up-to-date implies both constraints up-to-date and generators up-to-date; - generators' saturation matrix up-to-date implies both constraints up-to-date and generators up-to-date. */ class Status { public: //! By default Status is the zero-dim universe 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 true if successful, false 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 all bits in \p mask are set. bool test_all(flags_t mask) const; //! Check whether at least one 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 1930. */ #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 true 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 true if the polyhedron is known to be empty. /*! The return value false does not necessarily implies that \p *this is non-empty. */ bool marked_empty() const; //! Returns true if the system of constraints is up-to-date. bool constraints_are_up_to_date() const; //! Returns true if the system of generators is up-to-date. bool generators_are_up_to_date() const; //! Returns true 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 true 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 true if there are pending constraints. bool has_pending_constraints() const; //! Returns true if there are pending generators. bool has_pending_generators() const; /*! \brief Returns true if there are either pending constraints or pending generators. */ bool has_something_pending() const; //! Returns true if the polyhedron can have something pending. bool can_have_something_pending() const; /*! \brief Returns true if the saturation matrix \p sat_c is up-to-date. */ bool sat_c_is_up_to_date() const; /*! \brief Returns true 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 false 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 false 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 false 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 false 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 false 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 false 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 false 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 true 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 true if and only if \p from_above is true and \p expr is bounded from above in \p *this, or \p from_above is false and \p expr is bounded from below in \p *this. \param expr The linear expression to test; \param from_above true 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 true 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 true 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, false 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_selected) 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_con_sys); 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); //@} // Widening- and Extrapolation-Related Functions //! Adds new space dimensions to the given matrices. /*! \param mat1 The matrix to which columns are added; \param mat2 The matrix to which rows and columns are added; \param sat1 The saturation matrix whose columns are indexed by the rows of matrix \p mat1. On entry it is up-to-date; \param sat2 The saturation matrix whose columns are indexed by the rows of \p mat2; \param add_dim The number of space dimensions to add. Adds new space dimensions to the vector space modifying the matrices. This function is invoked only by add_space_dimensions_and_embed() and add_space_dimensions_and_project(), passing the matrix of constraints and that of generators (and the corresponding saturation matrices) in different order (see those methods for details). */ static void add_space_dimensions(Linear_System& mat1, Linear_System& mat2, 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. static bool minimize(bool con_to_gen, Linear_System& source, 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. static bool add_and_minimize(bool con_to_gen, Linear_System& source1, Linear_System& dest, Bit_Matrix& sat, const Linear_System& 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. static bool add_and_minimize(bool con_to_gen, Linear_System& source, 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. static dimension_type conversion(Linear_System& source, dimension_type start, Linear_System& dest, Bit_Matrix& sat, dimension_type num_lines_or_equalities); /*! \brief Uses Gauss' elimination method to simplify the result of conversion(). */ // Detailed Doxygen comment to be found in file simplify.cc. static dimension_type simplify(Linear_System& mat, 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 friend class Parma_Polyhedra_Library::Box; template friend class Parma_Polyhedra_Library::BD_Shape; template 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 Convexity Recognition of the Union of Polyhedra 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_runtime_error(const char* method) const; 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* e_name, const Linear_Expression& e) 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; 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: it has to be a static method, because it can be called inside // constructors (before actually constructing the polyhedron object). static void throw_space_dimension_overflow(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 *pvars. \param pvars When nonzero, points with non-integer coordinates for the variables/space-dimensions contained in \p *pvars 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 ANY_COMPLEXITY. */ void drop_some_non_integer_points(const Variables_Set* pvars, Complexity_Class complexity); }; namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Polyhedron */ void swap(Parma_Polyhedra_Library::Polyhedron& x, Parma_Polyhedra_Library::Polyhedron& y); } // namespace std /* 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; } 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 #include 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 space_dimension() & 0x7fffffff; } 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::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::swap(Polyhedron& y) { if (topology() != y.topology()) throw_topology_incompatible("swap(y)", "y", y); std::swap(con_sys, y.con_sys); std::swap(gen_sys, y.gen_sys); std::swap(sat_c, y.sat_c); std::swap(sat_g, y.sat_g); std::swap(status, y.status); std::swap(space_dim, y.space_dim); } 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()); Polyhedron& x = const_cast(*this); if (x.has_pending_constraints()) return x.process_pending_constraints(); PPL_ASSERT(x.has_pending_generators()); x.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 Grid_Generator_System Polyhedron::minimized_grid_generators() const { return grid_generators(); } inline void Polyhedron::add_recycled_congruences(Congruence_System& cgs) { add_congruences(cgs); } /*! \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* 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 namespace std { /*! \relates Parma_Polyhedra_Library::Polyhedron */ inline void swap(Parma_Polyhedra_Library::Polyhedron& x, Parma_Polyhedra_Library::Polyhedron& y) { x.swap(y); } } // namespace std /* 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/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/MIP_Problem.defs.hh line 36. */ #include #include #include namespace Parma_Polyhedra_Library { namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::MIP_Problem */ std::ostream& operator<<(std::ostream& s, const MIP_Problem& lp); } // namespace IO_Operators } // 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 max_space_dimension(). */ 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 MAXIMIZATION). \exception std::length_error Thrown if \p dim exceeds max_space_dimension(). \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 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 MAXIMIZATION). \exception std::length_error Thrown if \p dim exceeds max_space_dimension(). \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 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 MAXIMIZATION). \exception std::length_error Thrown if \p dim exceeds max_space_dimension(). \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_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; //! 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 max_space_dimension(). 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 true 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 den so that \f$\frac{num}{den}\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 num On exit will contain the numerator of the evaluated value. \param den 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& num, Coefficient& den) 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 doesn't not have an optimizing point, i.e., if the MIP problem is unbounded or not satisfiable. */ const Generator& optimizing_point() const; /*! \brief Sets \p num and \p den so that \f$\frac{num}{den}\f$ is the solution of the optimization problem. \exception std::domain_error Thrown if \p *this doesn't not have an optimizing point, i.e., if the MIP problem is unbounded or not satisfiable. */ void optimal_value(Coefficient& num, Coefficient& den) 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 true if successful, false 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 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; //! The matrix encoding the current feasible region in tableau form. Matrix tableau; //! The working cost function. Row 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; mapping[i].second not a zero, 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 > mapping; //! The current basic solution. std::vector 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. Constraint_Sequence input_cs; //! 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). std::swap(i_vars, lp.i_variables); } ~RAII_Temporary_Real_Relaxation() { // Restore the original set of integer variables. std::swap(i_vars, lp.i_variables); } }; friend class RAII_Temporary_Real_Relaxation; //! Processes the pending constraints of \p *this. /*! \return true if and only if the MIP problem is satisfiable after processing the pending constraints, false otherwise. */ bool 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 UNFEASIBLE_MIP_PROBLEM if the constraint system contains any trivially unfeasible constraint (tableau was not computed); UNBOUNDED_MIP_PROBLEM if the problem is trivially unbounded (the computed tableau contains no constraints); OPTIMIZED_MIP_PROBLEM> if the problem is neither trivially unfeasible nor trivially unbounded (the tableau was computed successfully). */ MIP_Problem_Status compute_tableau(std::vector& 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 i - first_pending_constraint) 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 i - first_pending_constraint) 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& is_tableau_constraint, std::deque& is_satisfied_inequality, std::deque& is_nonnegative_variable, std::deque& 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 *this[k] 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 Linear_Row to \p x and normalizes it. */ static void linear_combine(Row& x, const Row& y, const dimension_type k); /*! \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 0 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 0 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 doesn't 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 true 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 true 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 nonfeasible); 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 true if and only if \p c is satisfied by \p g. static bool is_satisfied(const Constraint& c, const Generator& g); //! Returns true 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 LP problem \p lp is satisfiable when variables in \p i_vars can only take integral values. \param lp The LP problem. This is assumed to have no integral space dimension. \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& lp, const Variables_Set& i_vars, Generator& p); /*! \brief Returns \c true if and if only \c mip.last_generator satisfies all the integrality coditions implicitly stated using by \p i_vars. \param lp The LP problem. This is assumed to have no integral space dimension. \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& lp, const Variables_Set& i_vars, dimension_type& branching_index); }; namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::MIP_Problem */ void swap(Parma_Polyhedra_Library::MIP_Problem& x, Parma_Polyhedra_Library::MIP_Problem& y); } // namespace std /* 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 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(y.input_cs), 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 MIP_Problem::~MIP_Problem() { } 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& num, Coefficient& den) const { const Generator& g = optimizing_point(); evaluate_objective_function(g, num, den); } inline MIP_Problem::const_iterator MIP_Problem::constraints_begin() const { return input_cs.begin(); } inline MIP_Problem::const_iterator MIP_Problem::constraints_end() const { return 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 { 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::swap(MIP_Problem& y) { std::swap(external_space_dim, y.external_space_dim); std::swap(internal_space_dim, y.internal_space_dim); std::swap(tableau, y.tableau); std::swap(working_cost, y.working_cost); std::swap(mapping, y.mapping); std::swap(initialized, y.initialized); std::swap(base, y.base); std::swap(status, y.status); std::swap(pricing, y.pricing); std::swap(input_cs, y.input_cs); std::swap(first_pending_constraint, y.first_pending_constraint); std::swap(input_obj_function, y.input_obj_function); std::swap(opt_mode, y.opt_mode); std::swap(last_generator, y.last_generator); std::swap(i_variables, y.i_variables); } inline MIP_Problem& MIP_Problem::operator=(const MIP_Problem& y) { MIP_Problem tmp(y); swap(tmp); return *this; } inline void MIP_Problem::clear() { MIP_Problem tmp; swap(tmp); } inline memory_size_type MIP_Problem::external_memory_in_bytes() const { memory_size_type n = tableau.external_memory_in_bytes() + working_cost.external_memory_in_bytes() + input_obj_function.external_memory_in_bytes() + last_generator.external_memory_in_bytes(); // Adding the external memory for `input_cs'. n += input_cs.capacity() * sizeof(Constraint); for (const_iterator i = input_cs.begin(), i_end = input_cs.end(); i != i_end; ++i) n += (i->external_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); return n; } inline memory_size_type MIP_Problem::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::MIP_Problem */ inline void swap(Parma_Polyhedra_Library::MIP_Problem& x, Parma_Polyhedra_Library::MIP_Problem& y) { x.swap(y); } } // namespace std /* 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 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, Row::Flags()), mapping(), base(), status(PARTIALLY_SATISFIABLE), pricing(PRICING_STEEPEST_EDGE_FLOAT), initialized(false), input_cs(), 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. 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()); } input_cs.push_back(*i); } PPL_ASSERT(OK()); } template 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, Row::Flags()), mapping(), base(), status(PARTIALLY_SATISFIABLE), pricing(PRICING_STEEPEST_EDGE_FLOAT), initialized(false), input_cs(), 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. 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()); } input_cs.push_back(*i); } PPL_ASSERT(OK()); } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/MIP_Problem.defs.hh line 854. */ /* 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/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 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 struct Has_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 inline typename Enable_If::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 inline typename Enable_If::value && !Slow_Copy::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 std::swap(). */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template inline typename Enable_If::value && Slow_Copy::value, void>::type assign_or_swap(T& to, T& from) { std::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 28. */ #include /* Automatically generated from PPL source file ../src/intervals.defs.hh line 31. */ namespace Parma_Polyhedra_Library { enum I_Result { //! The resulting set may be empty I_EMPTY = 1, //! The resulting set may have only one value I_SINGLETON = 2, //! The resulting set may have more than one value and to be not the domain universe I_SOME = 4, //! The resulting set may be the domain universe I_UNIVERSE = 8, //! The resulting set is not empty I_NOT_EMPTY = I_SINGLETON | I_SOME | I_UNIVERSE, //! The resulting set may be empty or not empty I_ANY = I_EMPTY | I_NOT_EMPTY, //! The resulting set may be empty or not empty I_NOT_UNIVERSE = I_EMPTY | I_SINGLETON | I_SOME, //! The resulting set can'be empty or the domain universe I_NOT_DEGENERATE = I_SINGLETON | I_SOME, //! The resulting set is definitely exact I_EXACT = 16, //! The resulting set is definitely inexact I_INEXACT = 32, //! The operation has definitely changed the set I_CHANGED = 64, //! The operation has left the set definitely unchanged I_UNCHANGED = 128, //! The operation is undefined for some combination of values I_SINGULARITIES = 256 }; inline I_Result operator|(I_Result a, I_Result b) { return static_cast((unsigned)a | (unsigned)b); } inline I_Result operator&(I_Result a, I_Result b) { return static_cast((unsigned)a & (unsigned)b); } inline I_Result operator-(I_Result a, I_Result b) { return static_cast((unsigned)a & ~(unsigned)b); } template struct Use_By_Ref; struct Use_Slow_Copy; template struct Use_By_Ref : public Bool::value> { }; struct By_Value; template struct Use_By_Ref : public False { }; struct By_Ref; template struct Use_By_Ref : public True { }; template class Val_Or_Ref; template class Val_Or_Ref::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 class Val_Or_Ref::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 class I_Constraint_Common : public I_Constraint_Base { public: template Result convert_real(T& to) const { const Derived& c = static_cast(*this); Result r = c.rel(); switch (r) { case V_EMPTY: case V_LGE: return r; case V_LE: r = assign_r(to, c.value(), static_cast(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(), static_cast(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_ASSERT(false); return V_EMPTY; } template Result convert_real(T& to1, Result& rel2, T& to2) const { const Derived& c = static_cast(*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_ASSERT(false); return V_EMPTY; } switch (rel2) { case V_LE: case V_LT: case V_LT_PLUS_INFINITY: return rel1; default: PPL_ASSERT(false); return V_EMPTY; } } template 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), static_cast(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), static_cast(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((Result)r) { } operator Result() const { return rel; } }; template class I_Constraint : public I_Constraint_Common > { typedef Val_Or_Ref 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 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 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 inline I_Constraint i_constraint(I_Constraint_Rel rel, const T& v) { return I_Constraint(rel, v); } template inline I_Constraint i_constraint(I_Constraint_Rel rel, const T& v, bool force) { return I_Constraint(rel, v, force); } template inline I_Constraint i_constraint(I_Constraint_Rel rel, T& v) { return I_Constraint(rel, v); } template inline I_Constraint i_constraint(I_Constraint_Rel rel, const T& v, const Val_Or_Ref_Criteria&) { return I_Constraint(rel, v); } template inline I_Constraint i_constraint(I_Constraint_Rel rel, const T& v, bool force, const Val_Or_Ref_Criteria&) { return I_Constraint(rel, v, force); } template inline I_Constraint i_constraint(I_Constraint_Rel rel, T& v, const Val_Or_Ref_Criteria&) { return I_Constraint(rel, v); } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Interval.types.hh line 1. */ namespace Parma_Polyhedra_Library { template class Interval; } // 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_, NORMALIZED_ }; 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_); static const Property NORMALIZED(Property::NORMALIZED_); 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(t | ROUND_STRICT_RELATION); else return static_cast(t); } template 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 inline bool special_is_open(Boundary_Type, const T&, const Info&) { return !Info::may_contain_infinity; } template 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 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 inline Result set_unbounded(Boundary_Type type, T& x, Info& info) { PPL_COMPILE_TIME_CHECK(Info::store_special || std::numeric_limits::is_bounded || std::numeric_limits::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 inline Result set_minus_infinity(Boundary_Type type, T& x, Info& info, bool open = false) { /* PPL_COMPILE_TIME_CHECK(Info::store_special || std::numeric_limits::has_infinity, "minus infinity is not representable"); */ 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 inline Result set_plus_infinity(Boundary_Type type, T& x, Info& info, bool open = false) { /* PPL_COMPILE_TIME_CHECK(Info::store_special || std::numeric_limits::has_infinity, "minus infinity is not representable"); */ 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 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 inline Result shrink(Boundary_Type type, T& x, Info& info, bool check) { Result r; PPL_ASSERT(!info.get_boundary_property(type, SPECIAL)); if (type == LOWER) { r = info.restrict(round_dir_check(type, check), x, V_GT); if (r != V_GT) return r; } else { r = info.restrict(round_dir_check(type, check), x, V_LT); if (r != V_LT) return r; } info.set_boundary_property(type, OPEN); return r; } template 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::has_infinity) return Parma_Polyhedra_Library::is_minus_infinity(x); else if (std::numeric_limits::is_bounded) return x == std::numeric_limits::min(); else return false; } template 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::has_infinity) return Parma_Polyhedra_Library::is_plus_infinity(x); else if (std::numeric_limits::is_bounded) return x == std::numeric_limits::max(); else return false; } template inline bool normal_is_boundary_infinity(Boundary_Type type, const T& x, const Info&) { if (!std::numeric_limits::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 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 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 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 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 inline bool is_reverse_infinity(Boundary_Type type, const T& x, const Info& info) { return normal_is_reverse_infinity(type, x, info); } template inline int is_infinity(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 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 inline bool boundary_infinity_is_open(Boundary_Type type, const Info& info) { return !Info::may_contain_infinity || info.get_boundary_property(type, OPEN); } template 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 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 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 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 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 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 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 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) shrink(type, x, info, false); // FIXME: what to return? return r; default: PPL_ASSERT(false); 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) shrink(type, x, info, false); // FIXME: what to return? return r; default: PPL_ASSERT(false); return V_NAN; } } } template 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 shrink; if (info.get_boundary_property(type, SPECIAL)) { shrink = !special_is_open(type, x, info); if (type == LOWER) return set_minus_infinity(to_type, to, to_info, shrink); else return set_plus_infinity(to_type, to, to_info, shrink); } shrink = !normal_is_open(type, x, info); bool check = (To_Info::check_inexact || (!shrink && (To_Info::store_open || to_info.has_restriction()))); Result r = assign_r(to, x, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, shrink, r); } template inline Result assign(Boundary_Type to_type, To& to, To_Info& to_info, Boundary_Type type, const T& x, const Info& info, bool shrink = false) { PPL_ASSERT(to_type == type); if (info.get_boundary_property(type, SPECIAL)) { shrink = shrink || special_is_open(type, x, info); return set_boundary_infinity(to_type, to, to_info, shrink); } shrink = shrink || normal_is_open(type, x, info); bool check = (To_Info::check_inexact || (!shrink && (To_Info::store_open || to_info.has_restriction()))); Result r = assign_r(to, x, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, shrink, r); } template 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 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 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 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 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 shrink; if (info.get_boundary_property(type, SPECIAL)) { shrink = special_is_open(type, x, info); return set_boundary_infinity(to_type, to, to_info, shrink); } shrink = normal_is_open(type, x, info); bool check = (To_Info::check_inexact || (!shrink && (To_Info::store_open || to_info.has_restriction()))); Result r = neg_assign_r(to, x, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, shrink, r); } template 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 shrink; if (is_boundary_infinity(type1, x1, info1)) { shrink = boundary_infinity_is_open(type1, info1) && !is_boundary_infinity_closed(type2, x2, info2); return set_boundary_infinity(to_type, to, to_info, shrink); } else if (is_boundary_infinity(type2, x2, info2)) { shrink = boundary_infinity_is_open(type2, info2) && !is_boundary_infinity_closed(type1, x1, info1); return set_boundary_infinity(to_type, to, to_info, shrink); } shrink = normal_is_open(type1, x1, info1) || normal_is_open(type2, x2, info2); bool check = (To_Info::check_inexact || (!shrink && (To_Info::store_open || to_info.has_restriction()))); // 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, shrink, r); } template 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 shrink; if (is_boundary_infinity(type1, x1, info1)) { shrink = boundary_infinity_is_open(type1, info1) && !is_boundary_infinity_closed(type2, x2, info2); return set_boundary_infinity(to_type, to, to_info, shrink); } else if (is_boundary_infinity(type2, x2, info2)) { shrink = boundary_infinity_is_open(type2, info2) && !is_boundary_infinity_closed(type1, x1, info1); return set_boundary_infinity(to_type, to, to_info, shrink); } shrink = normal_is_open(type1, x1, info1) || normal_is_open(type2, x2, info2); bool check = (To_Info::check_inexact || (!shrink && (To_Info::store_open || to_info.has_restriction()))); // 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, shrink, r); } template 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 shrink; if (is_boundary_infinity(type1, x1, info1)) { shrink = boundary_infinity_is_open(type1, info1) && !is_boundary_infinity_closed(type2, x2, info2); return set_boundary_infinity(to_type, to, to_info, shrink); } else if (is_boundary_infinity(type2, x2, info2)) { shrink = boundary_infinity_is_open(type2, info2) && !is_boundary_infinity_closed(type1, x1, info1); return set_boundary_infinity(to_type, to, to_info, shrink); } shrink = normal_is_open(type1, x1, info1) || normal_is_open(type2, x2, info2); bool check = (To_Info::check_inexact || (!shrink && (To_Info::store_open || to_info.has_restriction()))); 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, shrink, r); } template inline Result set_zero(Boundary_Type to_type, To& to, To_Info& to_info, bool shrink) { bool check = (To_Info::check_inexact || (!shrink && (To_Info::store_open || to_info.has_restriction()))); Result r = assign_r(to, Constant<0>::value, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, shrink, r); } template 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 shrink; if (x1s != 0) { if (x2s != 0) return mul_assign(to_type, to, to_info, type1, x1, info1, type2, x2, info2); else shrink = info2.get_boundary_property(type2, OPEN); } else { shrink = info1.get_boundary_property(type1, OPEN) && (x2s != 0 || info2.get_boundary_property(type2, OPEN)); } return set_zero(to_type, to, to_info, shrink); } template 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 shrink; if (is_boundary_infinity(type1, x1, info1)) { shrink = boundary_infinity_is_open(type1, info1); return set_boundary_infinity(to_type, to, to_info, shrink); } else if (is_boundary_infinity(type2, x2, info2)) { shrink = boundary_infinity_is_open(type2, info2); return set_zero(to_type, to, to_info, shrink); } shrink = normal_is_open(type1, x1, info1) || normal_is_open(type2, x2, info2); bool check = (To_Info::check_inexact || (!shrink && (To_Info::store_open || to_info.has_restriction()))); 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, shrink, r); } template 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) { bool shrink; if (x1s != 0) { if (x2s != 0) return div_assign(to_type, to, to_info, type1, x1, info1, type2, x2, info2); else { // FIXME: restrictions return set_boundary_infinity(to_type, to, to_info, true); } } else { shrink = info1.get_boundary_property(type1, OPEN) && !is_boundary_infinity_closed(type2, x2, info2); return set_zero(to_type, to, to_info, shrink); } } template 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 shrink; if (is_boundary_infinity(type, x, info)) { shrink = boundary_infinity_is_open(type, info); return set_boundary_infinity(to_type, to, to_info, shrink); } shrink = normal_is_open(type, x, info); bool check = (To_Info::check_inexact || (!shrink && (To_Info::store_open || to_info.has_restriction()))); Result r = umod_2exp_assign_r(to, x, exp, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, shrink, r); } template 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 shrink; if (is_boundary_infinity(type, x, info)) { shrink = boundary_infinity_is_open(type, info); return set_boundary_infinity(to_type, to, to_info, shrink); } shrink = normal_is_open(type, x, info); bool check = (To_Info::check_inexact || (!shrink && (To_Info::store_open || to_info.has_restriction()))); Result r = smod_2exp_assign_r(to, x, exp, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, shrink, r); } } // namespace Boundary_NS } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Interval_Restriction.defs.hh line 1. */ /* Interval_Restriction class declaration. */ /* Automatically generated from PPL source file ../src/Interval_Restriction.defs.hh line 33. */ namespace Parma_Polyhedra_Library { struct Interval_Base; template struct Boundary_Value { typedef T type; }; template struct Boundary_Value::value>::type > { typedef typename T::boundary_type type; }; class Interval_Restriction_None_Base { public: bool has_restriction() const { return false; } void normalize() const { } template Result restrict(Rounding_Dir, T&, Result dir) const { return dir; } }; inline bool eq_restriction(const Interval_Restriction_None_Base&, const Interval_Restriction_None_Base) { return true; } template inline bool contains_restriction(const Interval_Restriction_None_Base&, const T&) { return true; } template inline bool assign_restriction(Interval_Restriction_None_Base&, const T&) { return true; } template inline bool join_restriction(Interval_Restriction_None_Base&, const T1&, const T2&) { return true; } template inline bool intersect_restriction(Interval_Restriction_None_Base&, const T1&, const T2&) { return true; } template inline bool diff_restriction(Interval_Restriction_None_Base&, const T1&, const T2&) { return true; } template inline bool neg_restriction(Interval_Restriction_None_Base&, const T&) { return true; } template inline bool add_restriction(Interval_Restriction_None_Base&, const T1&, const T2&) { return true; } template inline bool sub_restriction(Interval_Restriction_None_Base&, const T1&, const T2&) { return true; } template inline bool mul_restriction(Interval_Restriction_None_Base&, const T1&, const T2&) { return true; } template inline bool div_restriction(Interval_Restriction_None_Base&, const T1&, const T2&) { return true; } inline void output_restriction(std::ostream&, const Interval_Restriction_None_Base&) { } template class Interval_Restriction_None : public Interval_Restriction_None_Base, public Base { public: Interval_Restriction_None() { } template Interval_Restriction_None(const T& init) : Base(init) { } }; class Interval_Restriction_Integer_Base { }; template class Interval_Restriction_Integer : public Interval_Restriction_Integer_Base, public Base { public: Interval_Restriction_Integer() { } void set_integer(bool v = true) { return set_bit(Base::bitset, integer_bit, v); } bool get_integer() const { return get_bit(Base::bitset, integer_bit); } const_int_nodef(integer_bit, Base::next_bit); const_int_nodef(next_bit, integer_bit + 1); bool has_restriction() const { return get_integer(); } void normalize() const { } template Result restrict(Rounding_Dir rdir, T& x, Result dir) const { if (!has_restriction()) return dir; switch (dir) { case V_GT: if (is_integer(x)) return add_assign_r(x, x, static_cast(1), rdir); /* Fall through */ case V_GE: return ceil_assign_r(x, x, rdir); case V_LT: if (is_integer(x)) return sub_assign_r(x, x, static_cast(1), rdir); /* Fall through */ case V_LE: return floor_assign_r(x, x, rdir); default: PPL_ASSERT(false); return dir; } } }; class Simple_Restriction_Integer : public Interval_Restriction_Integer_Base { public: Simple_Restriction_Integer(bool i) : integer(i) { } bool get_integer() const { return integer; } private: bool integer; }; template struct Restriction_Integer; template struct Restriction_Integer::value>::type> { typedef Simple_Restriction_Integer type; static type get(const From& x) { return Simple_Restriction_Integer(is_integer(x)); } }; template struct Restriction_Integer::value>::type> { typedef Simple_Restriction_Integer type; static type get(const From& x) { return Simple_Restriction_Integer(x.is_singleton() && is_integer(x.lower())); } }; template struct Restriction_Integer::value>::type> { typedef Interval_Restriction_Integer type; static const type& get(const From& x) { return x.info(); } }; template inline typename Enable_If::value && Is_Same_Or_Derived::value, bool>::type eq_restriction(const T1& x, const T2& y) { return x.get_integer() == y.get_integer(); } template inline typename Enable_If::value && Is_Same_Or_Derived::value, bool>::type contains_restriction(const T1& x, const T2& y) { return !x.get_integer() || y.get_integer(); } template inline bool assign_restriction(Interval_Restriction_Integer& to, const From& x) { to.set_integer(Restriction_Integer::get(x).get_integer()); return true; } template inline bool join_restriction(Interval_Restriction_Integer& to, const From1& x, const From2& y) { to.set_integer(Restriction_Integer::get(x).get_integer() && Restriction_Integer::get(y).get_integer()); return true; } template inline bool intersect_restriction(Interval_Restriction_Integer& to, const From1& x, const From2& y) { to.set_integer(Restriction_Integer::get(x).get_integer() || Restriction_Integer::get(y).get_integer()); return true; } template inline bool diff_restriction(Interval_Restriction_Integer& to, const From1& x, const From2&) { to.set_integer(Restriction_Integer::get(x).get_integer()); return true; } template inline bool neg_restriction(Interval_Restriction_Integer& to, const From& x) { to.set_integer(Restriction_Integer::get(x).get_integer()); return true; } template inline bool add_restriction(Interval_Restriction_Integer& to, const From1& x, const From2& y) { to.set_integer(Restriction_Integer::get(x).get_integer() && Restriction_Integer::get(y).get_integer()); return true; } template inline bool sub_restriction(Interval_Restriction_Integer& to, const From1& x, const From2& y) { to.set_integer(Restriction_Integer::get(x).get_integer() && Restriction_Integer::get(y).get_integer()); return true; } template inline bool mul_restriction(Interval_Restriction_Integer& to, const From1& x, const From2& y) { to.set_integer(Restriction_Integer::get(x).get_integer() && Restriction_Integer::get(y).get_integer()); return true; } template inline bool div_restriction(Interval_Restriction_Integer& to, const From1&, const From2&) { to.set_integer(false); return true; } template inline void output_restriction(std::ostream& s, const Interval_Restriction_Integer& x) { if (x.get_integer()) s << "i"; } class Interval_Restriction_Integer_Modulo_Base { }; template class Interval_Restriction_Integer_Modulo : public Interval_Restriction_Integer_Modulo_Base, public Base { public: PPL_COMPILE_TIME_CHECK(std::numeric_limits::is_exact, "type for modulo values must be exact"); Interval_Restriction_Integer_Modulo() { // FIXME: would we have speed benefits with uninitialized info? // (Dirty_Temp) clear(); } bool has_restriction() const { return divisor != 0; } void clear() { remainder = 0; divisor = 0; Base::clear(); } void normalize() const { } template Result restrict(Rounding_Dir rdir, V& x, Result dir) const { if (!has_restriction()) return dir; PPL_DIRTY_TEMP(V, n); PPL_DIRTY_TEMP(V, div); Result r; r = assign_r(div, divisor, ROUND_CHECK); PPL_ASSERT(r == V_EQ); int s; r = rem_assign_r(n, x, div, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); s = sgn(n); switch (dir) { case V_GT: if (s >= 0) { r = sub_assign_r(n, div, n, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); return add_assign_r(x, x, n, rdir); } else return sub_assign_r(x, x, n, rdir); case V_GE: if (s > 0) { r = sub_assign_r(n, div, n, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); return add_assign_r(x, x, n, rdir); } else if (s < 0) return sub_assign_r(x, x, n, rdir); else return V_EQ; case V_LT: if (s <= 0) { r = add_assign_r(n, div, n, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); return sub_assign_r(x, x, n, rdir); } else return sub_assign_r(x, x, n, rdir); case V_LE: if (s < 0) { r = add_assign_r(n, div, n, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); return sub_assign_r(x, x, n, rdir); } else if (s > 0) return sub_assign_r(x, x, n, rdir); else return V_EQ; default: PPL_ASSERT(false); return dir; } } void assign_or_swap(Interval_Restriction_Integer_Modulo& x) { Parma_Polyhedra_Library::assign_or_swap(remainder, x.remainder); Parma_Polyhedra_Library::assign_or_swap(divisor, x.divisor); } typedef T modulo_type; T remainder; T divisor; }; template struct Slow_Copy > : public Bool::value> {}; template struct Restriction_Integer::value>::type> { typedef Simple_Restriction_Integer type; static type get(const From& x) { return Simple_Restriction_Integer(x.info().divisor != 0); } }; template struct Simple_Restriction_Integer_Modulo : public Interval_Restriction_Integer_Modulo_Base { template Simple_Restriction_Integer_Modulo(const From& r, const From& d) : remainder(r), divisor(d) { } typedef T modulo_type; T remainder; T divisor; }; template struct Restriction_Integer_Modulo; template struct Restriction_Integer_Modulo::value>::type> { typedef Simple_Restriction_Integer_Modulo type; static const type& get(const From& x) { static const type integer(0, 1); static const type not_integer(0, 0); if (is_integer(x)) return integer; else return not_integer; } }; template struct Restriction_Integer_Modulo::value>::type> { typedef Simple_Restriction_Integer_Modulo type; static const type& get(const From& x) { static const type integer(0, 1); static const type not_integer(0, 0); if (x.is_singleton() && is_integer(x.lower())) return integer; else return not_integer; } }; template struct Restriction_Integer_Modulo::value>::type> { typedef Simple_Restriction_Integer_Modulo type; static const type& get(const From& x) { static const type integer(0, 1); static const type not_integer(0, 0); if (x.info().get_integer()) return integer; else return not_integer; } }; template struct Restriction_Integer_Modulo::value>::type> { typedef Interval_Restriction_Integer_Modulo type; static const type& get(const From& x) { return x.info(); } }; template inline typename Enable_If::value && Is_Same_Or_Derived::value, bool>::type eq_restriction(const T1& x, const T2& y) { return x.remainder == y.remainder && x.divisor == y.divisor; } template inline typename Enable_If::value && Is_Same_Or_Derived::value, bool>::type contains_restriction(const T1& x, const T2& y) { if (x.divisor == 0) return true; if (y.divisor == 0) return false; if (x.divisor == y.divisor) return x.remainder == y.remainder; PPL_DIRTY_TEMP(typename T1::modulo_type, v); Result r; r = rem_assign_r(v, y.divisor, x.divisor, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); if (v != 0) return false; r = rem_assign_r(v, y.remainder, x.divisor, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); return v == x.remainder; } template inline bool set_unrestricted(Interval_Restriction_Integer_Modulo& to) { to.remainder = 0; to.divisor = 0; return true; } template inline bool set_integer(Interval_Restriction_Integer_Modulo& to) { to.remainder = 0; to.divisor = 1; return true; } template inline bool assign_restriction(Interval_Restriction_Integer_Modulo& to, const From& x) { to.remainder = Restriction_Integer_Modulo::get(x).remainder; to.divisor = Restriction_Integer_Modulo::get(x).divisor; return true; } template inline bool join_restriction(Interval_Restriction_Integer_Modulo& to, const From1& x, const From2& y) { typedef Restriction_Integer_Modulo Rx; const typename Rx::type& rx = Rx::get(x); if (rx.divisor == 0) return set_unrestricted(to); typedef Restriction_Integer_Modulo Ry; const typename Ry::type& ry = Ry::get(y); if (ry.divisor == 0) return set_unrestricted(to); else if (rx.divisor == 1 && ry.divisor == 1 && is_singleton(x) && is_singleton(y)) { PPL_DIRTY_TEMP(typename Boundary_Value::type, a); PPL_DIRTY_TEMP(typename Boundary_Value::type, b); Result r; r = abs_assign_r(a, f_lower(x), ROUND_CHECK); if (r != V_EQ) return set_integer(to); r = abs_assign_r(b, f_lower(y), ROUND_CHECK); if (r != V_EQ) return set_integer(to); if (a > b) r = sub_assign_r(a, a, b, ROUND_CHECK); else r = sub_assign_r(a, b, a, ROUND_CHECK); if (r != V_EQ) return set_integer(to); r = assign_r(to.divisor, a, ROUND_CHECK); if (r != V_EQ) return set_integer(to); r = rem_assign_r(b, b, a, ROUND_CHECK); if (r != V_EQ) return set_integer(to); r = assign_r(to.remainder, b, ROUND_CHECK); if (r != V_EQ) return set_integer(to); } else if (contains_restriction(rx, ry)) { to.remainder = rx.remainder; to.divisor = rx.divisor; } else if (contains_restriction(ry, rx)) { to.remainder = ry.remainder; to.divisor = ry.divisor; } else return set_integer(to); return true; } template inline bool intersect_restriction(Interval_Restriction_Integer_Modulo& to, const From1& x, const From2& y) { typedef Restriction_Integer_Modulo Rx; const typename Rx::type& rx = Rx::get(x); typedef Restriction_Integer_Modulo Ry; const typename Ry::type& ry = Ry::get(y); if (rx.divisor == 0) { to.remainder = ry.remainder; to.divisor = ry.divisor; return true; } if (ry.divisor == 0) { to.remainder = rx.remainder; to.divisor = rx.divisor; return true; } PPL_DIRTY_TEMP(T, g); Result r; r = gcd_assign_r(g, rx.divisor, ry.divisor, ROUND_DIRECT); if (r != V_EQ) return set_integer(to); PPL_DIRTY_TEMP(T, d); if (rx.remainder > ry.remainder) r = sub_assign_r(d, rx.remainder, ry.remainder, ROUND_DIRECT); else r = sub_assign_r(d, ry.remainder, rx.remainder, ROUND_DIRECT); if (r != V_EQ) return set_integer(to); r = div_assign_r(d, d, g, ROUND_DIRECT); if (r != V_EQ) return false; r = lcm_assign_r(to.divisor, rx.divisor, ry.divisor, ROUND_DIRECT); if (r != V_EQ) return set_integer(to); // FIXME: to be completed return true; } template inline bool diff_restriction(Interval_Restriction_Integer_Modulo& to, const From1& x, const From2& y) { // FIXME: to be written return true; } template inline bool neg_restriction(Interval_Restriction_Integer_Modulo& to, const From& x) { return assign_restriction(to, x); } template inline void addmod(T& to, const T& x, const T& y, const T& to_m, const T& y_m) { Result r; if (std::numeric_limits::is_bounded) { r = sub_assign_r(to, y_m, y, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); if (x <= to) { r = add_assign_r(to, x, y, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); } else { r = sub_assign_r(to, x, to, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); } } else { r = add_assign_r(to, x, y, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); } r = rem_assign_r(to, to, to_m, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); } template inline bool assign_rem(M& rem, const T& n, const M& div) { PPL_DIRTY_TEMP(T, divisor); PPL_DIRTY_TEMP(T, remainder); Result r; r = assign_r(divisor, div, ROUND_CHECK); if (r != V_EQ) return false; r = rem_assign_r(remainder, n, divisor, ROUND_CHECK); if (r != V_EQ) return false; if (sgn(remainder) < 0) { r = add_assign_r(remainder, remainder, divisor, ROUND_CHECK); if (r != V_EQ) return false; } r = assign_r(rem, remainder, ROUND_CHECK); return r == V_EQ; } template inline bool add_restriction(Interval_Restriction_Integer_Modulo& to, const From1& x, const From2& y) { typedef Restriction_Integer_Modulo Rx; const typename Rx::type& rx = Rx::get(x); if (rx.divisor == 0) return set_unrestricted(to); typedef Restriction_Integer_Modulo Ry; const typename Ry::type& ry = Ry::get(y); if (ry.divisor == 0) return set_unrestricted(to); Result r; PPL_DIRTY_TEMP(T, rem); if (is_singleton(x)) { if (is_singleton(y)) return set_integer(to); if (!assign_rem(rem, f_lower(x), ry.divisor)) return set_integer(to); r = assign_r(to.divisor, ry.divisor, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); addmod(to.remainder, rem, ry.remainder, to.divisor, ry.divisor); } else if (is_singleton(y)) { if (!assign_rem(rem, f_lower(y), rx.divisor)) return set_integer(to); r = assign_r(to.divisor, rx.divisor, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); addmod(to.remainder, rx.remainder, rem, to.divisor, to.divisor); } else { r = gcd_assign_r(to.divisor, rx.divisor, ry.divisor, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); addmod(to.remainder, rx.remainder, ry.remainder, to.divisor, ry.divisor); } return true; } template inline void submod(T& to, const T& x, const T& y, const T& to_m, const T& y_m) { Result r; if (x >= y) { r = sub_assign_r(to, x, y, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); } else { r = sub_assign_r(to, y_m, y, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); r = add_assign_r(to, x, to, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); } r = rem_assign_r(to, to, to_m, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); } template inline bool sub_restriction(Interval_Restriction_Integer_Modulo& to, const From1& x, const From2& y) { typedef Restriction_Integer_Modulo Rx; const typename Rx::type& rx = Rx::get(x); if (rx.divisor == 0) return set_unrestricted(to); typedef Restriction_Integer_Modulo Ry; const typename Ry::type& ry = Ry::get(y); if (ry.divisor == 0) return set_unrestricted(to); Result r; PPL_DIRTY_TEMP(T, rem); if (is_singleton(x)) { if (is_singleton(y)) return set_integer(to); if (!assign_rem(rem, f_lower(x), ry.divisor)) return set_integer(to); r = assign_r(to.divisor, ry.divisor, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); submod(to.remainder, rem, ry.remainder, to.divisor, ry.divisor); } else if (is_singleton(y)) { if (!assign_rem(rem, f_lower(y), rx.divisor)) return set_integer(to); r = assign_r(to.divisor, rx.divisor, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); submod(to.remainder, rx.remainder, rem, to.divisor, to.divisor); } else { r = gcd_assign_r(to.divisor, rx.divisor, ry.divisor, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); submod(to.remainder, rx.remainder, ry.remainder, to.divisor, ry.divisor); } return true; } template inline void mulmod(T& to, const T& x, const T& y, const T& to_m) { Result r; if (std::numeric_limits::is_bounded) { PPL_DIRTY_TEMP0(mpz_class, a); PPL_DIRTY_TEMP0(mpz_class, b); r = assign_r(a, x, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); r = assign_r(b, y, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); r = mul_assign_r(a, a, b, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); r = assign_r(b, to_m, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); r = rem_assign_r(a, a, b, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); r = assign_r(to, a, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); } else { r = mul_assign_r(to, x, y, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); r = rem_assign_r(to, to, to_m, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); } } template inline bool mul_restriction(Interval_Restriction_Integer_Modulo& to, const From1& x, const From2& y) { typedef Restriction_Integer_Modulo Rx; const typename Rx::type& rx = Rx::get(x); if (rx.divisor == 0) return set_unrestricted(to); typedef Restriction_Integer_Modulo Ry; const typename Ry::type& ry = Ry::get(y); if (ry.divisor == 0) return set_unrestricted(to); Result r; PPL_DIRTY_TEMP(T, mul); if (is_singleton(x)) { if (is_singleton(y)) return set_integer(to); PPL_DIRTY_TEMP(typename Boundary_Value::type, n); r = abs_assign_r(n, f_lower(x), ROUND_CHECK); if (r != V_EQ) return set_integer(to); r = assign_r(mul, n, ROUND_CHECK); if (r != V_EQ) return set_integer(to); r = mul_assign_r(to.remainder, mul, ry.remainder, ROUND_NOT_NEEDED); if (r != V_EQ) return set_integer(to); r = mul_assign_r(to.divisor, mul, ry.divisor, ROUND_NOT_NEEDED); if (r != V_EQ) return set_integer(to); } else if (is_singleton(y)) { PPL_DIRTY_TEMP(typename Boundary_Value::type, n); r = abs_assign_r(n, f_lower(y), ROUND_CHECK); if (r != V_EQ) return set_integer(to); r = assign_r(mul, n, ROUND_CHECK); if (r != V_EQ) return set_integer(to); r = mul_assign_r(to.remainder, rx.remainder, mul, ROUND_NOT_NEEDED); if (r != V_EQ) return set_integer(to); r = mul_assign_r(to.divisor, rx.divisor, mul, ROUND_NOT_NEEDED); if (r != V_EQ) return set_integer(to); } else { r = gcd_assign_r(to.divisor, rx.divisor, ry.divisor, ROUND_NOT_NEEDED); PPL_ASSERT(r == V_EQ); mulmod(to.remainder, rx.remainder, ry.remainder, to.divisor); } return true; } template inline bool div_restriction(Interval_Restriction_Integer_Modulo& to, const From1& x, const From2& y) { if (is_singleton(y)) { if (is_singleton(x)) { // FIXME: to be written } } return set_unrestricted(to); } template inline void output_restriction(std::ostream& s, const Interval_Restriction_Integer_Modulo& x) { if (x.divisor == 1) s << "i"; else if (x.divisor != 0) s << "{" << x.remainder << "%" << x.divisor << "}"; } } /* Automatically generated from PPL source file ../src/Interval_Info.defs.hh line 29. */ #include 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 inline void reset_bits(T& bits) { bits = 0; } template inline void reset_bit(T& bits, unsigned int bit) { bits &= ~(static_cast(1) << bit); } template inline void set_bit(T& bits, unsigned int bit, bool value) { if (value) bits |= static_cast(1) << bit; else reset_bit(bits, bit); } template inline bool get_bit(const T& bits, unsigned int bit) { return bits & (static_cast(1) << bit); } template inline void set_bits(T& bits, unsigned int start, unsigned int len, T value) { bits &= ~(((static_cast(1) << len) - 1) << start); bits |= value << start; } template inline T get_bits(T& bits, unsigned int start, unsigned int len) { return (bits >> start) & ((static_cast(1) << len) - 1); } } // namespace Interval_NS using namespace Interval_NS; using namespace Boundary_NS; template 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_normalized, false); const_bool_nodef(cache_empty, false); const_bool_nodef(cache_singleton, false); void clear() { } void clear_boundary_properties(Boundary_Type) { } template void set_boundary_property(Boundary_Type, const Property&, typename Property::Value = Property::default_value) { } template typename Property::Value get_boundary_property(Boundary_Type, const Property&) const { return Property::unsupported_value; } template void set_interval_property(const Property&, typename Property::Value = Property::default_value) { } template typename Property::Value get_interval_property(const Property&) const { return Property::unsupported_value; } //! Swaps \p *this with \p y. void swap(Interval_Info_Null& y); void ascii_dump(std::ostream& s) const; bool ascii_load(std::istream& s); }; template class Interval_Info_Null_Open : public Interval_Info_Null { 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 { switch (p.type) { case Boundary_NS::Property::OPEN_: return open; default: return Boundary_NS::Property::unsupported_value; } } void ascii_dump(std::ostream& s) const; bool ascii_load(std::istream& s); private: bool open; }; template 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_normalized, Policy::cache_normalized); 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); const_int_nodef(lower_normalized_bit, lower_open_bit + store_open); const_int_nodef(upper_special_bit, lower_normalized_bit + cache_normalized); const_int_nodef(upper_open_bit, upper_special_bit + store_special); const_int_nodef(upper_normalized_bit, upper_open_bit + store_open); const_int_nodef(cardinality_is_bit, upper_normalized_bit + cache_normalized); const_int_nodef(cardinality_0_bit, cardinality_is_bit + (cache_empty || cache_singleton)); const_int_nodef(cardinality_1_bit, cardinality_0_bit + cache_empty); const_int_nodef(next_bit, cardinality_1_bit + cache_singleton); 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; case Boundary_NS::Property::NORMALIZED_: if (cache_normalized) { if (t == LOWER) set_bit(bitset, lower_normalized_bit, value); else set_bit(bitset, upper_normalized_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); case Boundary_NS::Property::NORMALIZED_: if (!cache_normalized) return false; else if (t == LOWER) return get_bit(bitset, lower_normalized_bit); else return get_bit(bitset, upper_normalized_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 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 namespace Parma_Polyhedra_Library { template inline void Interval_Info_Null::swap(Interval_Info_Null&) { } template inline void Interval_Info_Null::ascii_dump(std::ostream& s) const { } template inline bool Interval_Info_Null::ascii_load(std::istream& s) { return true; } template inline void Interval_Info_Null_Open::ascii_dump(std::ostream& s) const { s << (open ? "open" : "closed"); } template inline bool Interval_Info_Null_Open::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 inline void Interval_Info_Bitset::swap(Interval_Info_Bitset& y) { std::swap(bitset, y.bitset); } template inline void Interval_Info_Bitset::ascii_dump(std::ostream& s) const { std::ios_base::fmtflags old = s.flags(); s << std::hex << bitset; s.flags(old); } template inline bool Interval_Info_Bitset::ascii_load(std::istream& s) { std::ios_base::fmtflags old = s.flags(); if (s >> std::hex >> bitset) { s.flags(old); return s; } else return false; } } // namespace Parma_Polyhedra_Library namespace std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \relates Parma_Polyhedra_Library::Interval_Info_Null */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template inline void swap(Parma_Polyhedra_Library::Interval_Info_Null& x, Parma_Polyhedra_Library::Interval_Info_Null& y) { x.swap(y); } #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \relates Parma_Polyhedra_Library::Interval_Info_Bitset */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template inline void swap(Parma_Polyhedra_Library::Interval_Info_Bitset& x, Parma_Polyhedra_Library::Interval_Info_Bitset& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/Interval_Info.defs.hh line 299. */ /* Automatically generated from PPL source file ../src/Interval.defs.hh line 33. */ #include // Temporary! #include namespace Parma_Polyhedra_Library { enum Ternary { T_YES, T_NO, T_MAYBE }; inline I_Result combine(Result l, Result u) { return static_cast(l | (u << 6)); } struct Interval_Base { }; using namespace Boundary_NS; using namespace Interval_NS; template struct Is_Singleton : public Is_Native_Or_Checked {}; template struct Is_Interval : public Is_Same_Or_Derived {}; // FIXME: This has been added as a workaraound. template typename Enable_If::value, I_Result>::type neg_assign(From& x); //! 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 signed char to long long and from int8_t to int64_t); - a bounded precision floating point type (float, double or long double); - an unbounded integer or rational type, as provided by the C++ interface of GMP (mpz_class or mpq_class). 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; - the ability to support (independently from the type of the boundaries) plain intervals of real numbers and intervals subject to generic restrictions (e.g., intervals of integer numbers). */ template class Interval : public Interval_Base, private Info { private: PPL_COMPILE_TIME_CHECK(!Info::store_special || !std::numeric_limits::has_infinity, "store_special is meaningless" " when boundary type may contains infinity"); Info& w_info() const { return const_cast(*this); } Result lower_normalize() const { Result r; if (info().get_boundary_property(LOWER, NORMALIZED) || info().get_boundary_property(LOWER, SPECIAL)) r = V_EQ; else { Boundary& l = const_cast(lower()); if (info().get_boundary_property(LOWER, OPEN)) { r = info().restrict(round_dir_check(LOWER, true), l, V_GT); if (r != V_GT) w_info().set_boundary_property(LOWER, OPEN, false); } else { r = info().restrict(round_dir_check(LOWER, true), l, V_GE); if (r == V_GT) w_info().set_boundary_property(LOWER, OPEN); } w_info().set_boundary_property(LOWER, NORMALIZED); } return r; } Result upper_normalize() const { Result r; if (info().get_boundary_property(UPPER, NORMALIZED) || info().get_boundary_property(UPPER, SPECIAL)) r = V_EQ; else { Boundary& u = const_cast(upper()); if (info().get_boundary_property(UPPER, OPEN)) { r = info().restrict(round_dir_check(UPPER, true), u, V_LT); if (r != V_LT) w_info().set_boundary_property(UPPER, OPEN, false); } else { r = info().restrict(round_dir_check(UPPER, true), u, V_LE); if (r == V_LT) w_info().set_boundary_property(UPPER, OPEN); } w_info().set_boundary_property(UPPER, NORMALIZED); } return r; } public: typedef Boundary boundary_type; typedef Info info_type; typedef Interval_NS::Property Property; template typename Enable_If::value || Is_Interval::value, Interval&>::type operator=(const T& x) { assign(x); return *this; } template typename Enable_If::value || Is_Interval::value, Interval&>::type operator+=(const T& x) { add_assign(*this, x); return *this; } template typename Enable_If::value || Is_Interval::value, Interval&>::type operator-=(const T& x) { sub_assign(*this, x); return *this; } template typename Enable_If::value || Is_Interval::value, Interval&>::type operator*=(const T& x) { mul_assign(*this, x); return *this; } template typename Enable_If::value || Is_Interval::value, Interval&>::type operator/=(const T& x) { div_assign(*this, x); return *this; } //! Swaps \p *this with \p y. void 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 lower_constraint() const { PPL_ASSERT(!is_empty()); if (info().get_boundary_property(LOWER, SPECIAL)) return I_Constraint(); return i_constraint(lower_is_open() ? GREATER_THAN : GREATER_OR_EQUAL, lower(), true); } I_Constraint upper_constraint() const { PPL_ASSERT(!is_empty()); if (info().get_boundary_property(UPPER, SPECIAL)) return I_Constraint(); return i_constraint(upper_is_open() ? LESS_THAN : LESS_OR_EQUAL, upper(), true); } bool has_restriction() const { return info().has_restriction(); } I_Result normalize() const { PPL_ASSERT(OK()); if (has_restriction()) { Result rl = lower_normalize(); Result ru = upper_normalize(); info().normalize(); PPL_ASSERT(OK()); return combine(rl, ru); } else return combine(V_EQ, V_EQ); } 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() && !has_restriction(); } I_Result lower_extend() { info().clear_boundary_properties(LOWER); set_unbounded(LOWER, lower(), info()); return I_ANY; } template typename Enable_If::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 Enable_If::value, I_Result>::type upper_extend(const C& c); I_Result build() { return assign(UNIVERSE); } template typename Enable_If::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 = (Relation_Symbol) c.rel(); return refine_existential(rs, c.value()); } } template typename Enable_If::value && Is_Same_Or_Derived::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); I_Result r = add_constraint(c2); return r - (I_CHANGED | I_UNCHANGED); } template typename Enable_If::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) { default: PPL_ASSERT(0); /* Fall through */ 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; } PPL_ASSERT(OK()); return r; } template typename Enable_If::value, I_Result>::type assign(const From&) { info().clear(); Result rl, 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_ASSERT(0); rl = V_NAN; ru = V_NAN; } PPL_ASSERT(OK()); return combine(rl, ru); } I_Result set_infinities() { info().clear(); // FIXME: what about restrictions? 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); } bool is_infinity() 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 Enable_If::value || Is_Interval::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) && 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_ASSERT(false); 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 0 if (!Info::may_be_empty && is_empty()) { #ifndef NDEBUG std::cerr << "The interval is unexpectedly empty.\n"; #endif return false; } #endif 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 0 if (info().get_boundary_property(LOWER, NORMALIZED) && !info().is_restricted(lower())) { #ifndef NDEBUG std::cerr << "The lower boundary is marked to be normalized, " << "but it is not.\n"; #endif return false; } #endif } 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; } #if 0 if (info().get_boundary_property(UPPER, NORMALIZED) && !info().is_restricted(upper())) { #ifndef NDEBUG std::cerr << "The upper boundary is marked to be normalized, " << "but it is not.\n"; #endif return false; } #endif } // Everything OK. return true; } Interval() { } template 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 Enable_If::value || Is_Interval::value, bool>::type contains(const T& y) const; template typename Enable_If::value || Is_Interval::value, bool>::type strictly_contains(const T& y) const; template typename Enable_If::value || Is_Interval::value, bool>::type is_disjoint_from(const T& y) const; template typename Enable_If::value || Is_Interval::value, I_Result>::type assign(const From& x); template typename Enable_If::value || Is_Interval::value, bool>::type can_be_exactly_joined_to(const Type& x) const; template typename Enable_If::value || Is_Interval::value, I_Result>::type join_assign(const From& x); template typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type join_assign(const From1& x, const From2& y); template typename Enable_If::value || Is_Interval::value, I_Result>::type intersect_assign(const From& x); template typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::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 Enable_If::value || Is_Interval::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 Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::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 Enable_If::value || Is_Interval::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 Enable_If::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. \note Depending on interval restrictions, there could be many maximal intervals all inconsistent with respect to \p y. */ template typename Enable_If::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 Enable_If::value || Is_Interval::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 Enable_If::value || Is_Interval::value, I_Result>::type refine_universal(Relation_Symbol rel, const From& x); template typename Enable_If::value || Is_Interval::value, I_Result>::type neg_assign(const From& x); template typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type add_assign(const From1& x, const From2& y); template typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type sub_assign(const From1& x, const From2& y); template typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type mul_assign(const From1& x, const From2& y); template typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type div_assign(const From1& x, const From2& y); template typename Enable_If::value, void>::type CC76_widening_assign(const From& y, Iterator first, Iterator last); private: Boundary lower_; Boundary upper_; }; } // 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 Enable_If::value, I_Result>::type neg_assign(From& x) { // FIXME: Avoid the creation of a temporary. From y; typename Enable_If::value, I_Result>::type res = y.neg_assign(x); x = y; return res; } template inline memory_size_type Interval::external_memory_in_bytes() const { return Parma_Polyhedra_Library::external_memory_in_bytes(lower()) + Parma_Polyhedra_Library::external_memory_in_bytes(upper()); } template inline memory_size_type Interval::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } template inline void Interval::swap(Interval& y) { std::swap(lower(), y.lower()); std::swap(upper(), y.upper()); std::swap(info(), y.info()); } template inline bool f_is_empty(const Interval& x) { return x.is_empty(); } template inline bool f_is_singleton(const Interval& x) { return x.is_singleton(); } template inline int is_infinity(const Interval& x) { return x.is_infinity(); } namespace Interval_NS { template inline const Boundary& f_lower(const Interval& x) { return x.lower(); } template inline const Boundary& f_upper(const Interval& x) { return x.upper(); } template inline const Info& f_info(const Interval& 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_Restriction_None > Scalar_As_Interval_Info; const Scalar_As_Interval_Info SCALAR_INFO; typedef Interval_Restriction_None > Scalar_As_Interval_Info_Open; template inline typename Enable_If::value, const T&>::type f_lower(const T& x) { return x; } template inline typename Enable_If::value, const T&>::type f_upper(const T& x) { return x; } template inline typename Enable_If::value, const Scalar_As_Interval_Info&>::type f_info(const T&) { return SCALAR_INFO; } template inline typename Enable_If::value, Scalar_As_Interval_Info_Open>::type f_info(const T&, bool open) { return Scalar_As_Interval_Info_Open(open); } template inline typename Enable_If::value, bool>::type f_is_empty(const T& x) { return is_not_a_number(x); } template inline typename Enable_If::value, bool>::type f_is_singleton(const T& x) { return !f_is_empty(x); } } // namespace Interval_NS template inline typename Enable_If::value || Is_Interval::value, bool>::type is_singleton_integer(const T& x) { return is_singleton(x) && is_integer(f_lower(x)); } template inline typename Enable_If::value || Is_Interval::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 inline typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value) && (Is_Interval::value || Is_Interval::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; // FIXME: the two restrictions should be evaluated in the context of // the specific interval return eq_restriction(f_info(x), f_info(y)) && 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 inline typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value) && (Is_Interval::value || Is_Interval::value)), bool>::type operator!=(const T1& x, const T2& y) { return !(x == y); } template template inline typename Enable_If::value || Is_Interval::value, bool>::type Interval::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; // FIXME: the two restrictions should be evaluated in the context of // the specific interval if (!contains_restriction(info(), f_info(y))) 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 template inline typename Enable_If::value || Is_Interval::value, bool>::type Interval::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; // FIXME: the two restrictions should be evaluated in the context of // the specific interval if (!contains_restriction(info(), f_info(y))) return false; else if (!eq_restriction(info(), f_info(y))) return le(LOWER, lower(), info(), LOWER, f_lower(y), f_info(y)) && ge(UPPER, upper(), info(), UPPER, f_upper(y), f_info(y)); 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 template inline typename Enable_If::value || Is_Interval::value, bool>::type Interval::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; // CHECKME. // if (!contains_restriction(info(), f_info(y))) // return false; return gt(LOWER, lower(), info(), UPPER, f_upper(y), f_info(y)) || lt(UPPER, upper(), info(), LOWER, f_lower(y), f_info(y)); } template template inline typename Enable_If::value || Is_Interval::value, I_Result>::type Interval::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(); if (!assign_restriction(to_info, x)) return assign(EMPTY); Result rl = Boundary_NS::assign(LOWER, lower(), to_info, LOWER, f_lower(x), f_info(x)); 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 template inline typename Enable_If::value || Is_Interval::value, I_Result>::type Interval::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); if (!join_restriction(info(), *this, x)) return assign(EMPTY); Result rl, 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 template inline typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type Interval::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(); if (!join_restriction(to_info, x, y)) return assign(EMPTY); Result rl, 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 template inline typename Enable_If::value || Is_Interval::value, bool>::type Interval::can_be_exactly_joined_to(const Type& x) const { // FIXME: the two restrictions should be evaluated in the context of // the specific interval if (!eq_restriction(info(), f_info(x))) return false; PPL_DIRTY_TEMP(Boundary, b); if (gt(LOWER, lower(), info(), UPPER, f_upper(x), f_info(x))) { b = lower(); return info().restrict(round_dir_check(LOWER, true), b, V_LT) == V_EQ && 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 info().restrict(round_dir_check(UPPER, true), b, V_GT) == V_EQ && eq(UPPER, b, info(), LOWER, f_lower(x), f_info(x)); } return true; } template template inline typename Enable_If::value || Is_Interval::value, I_Result>::type Interval::intersect_assign(const From& x) { PPL_ASSERT(f_OK(x)); if (!intersect_restriction(info(), *this, x)) return assign(EMPTY); Result rl, ru; rl = max_assign(LOWER, lower(), info(), LOWER, f_lower(x), f_info(x)); ru = min_assign(UPPER, upper(), info(), UPPER, f_upper(x), f_info(x)); PPL_ASSERT(OK()); return I_ANY; } template template inline typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type Interval::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(); if (!intersect_restriction(to_info, x, y)) return assign(EMPTY); Result rl, ru; rl = max_assign(LOWER, lower(), to_info, LOWER, f_lower(x), f_info(x), LOWER, f_lower(y), f_info(y)); ru = 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 template inline typename Enable_If::value || Is_Interval::value, I_Result>::type Interval::difference_assign(const From& x) { PPL_ASSERT(f_OK(x)); // FIXME: restrictions 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, 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 template inline typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type Interval::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(); // FIXME: restrictions 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, 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 template inline typename Enable_If::value || Is_Interval::value, I_Result>::type Interval ::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); normalize(); 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)); normalize(); 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); normalize(); 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)); normalize(); 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(); normalize(); return I_ANY; } default: PPL_ASSERT(false); return I_EMPTY; } } template template inline typename Enable_If::value || Is_Interval::value, I_Result>::type Interval::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))); normalize(); 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); normalize(); 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))); normalize(); 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); normalize(); 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(); normalize(); return I_ANY; } default: PPL_ASSERT(false); return I_EMPTY; } } template template inline typename Enable_If::value || Is_Interval::value, I_Result>::type Interval::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(); if (!neg_restriction(to_info, x)) return assign(EMPTY); Result rl, 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 template inline typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type Interval::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 = Parma_Polyhedra_Library::is_infinity(x); if (inf) { if (Parma_Polyhedra_Library::is_infinity(y) == -inf) return assign(EMPTY); } else inf = Parma_Polyhedra_Library::is_infinity(y); if (inf < 0) return assign(MINUS_INFINITY); else if (inf > 0) return assign(PLUS_INFINITY); PPL_DIRTY_TEMP(To_Info, to_info); to_info.clear(); if (!add_restriction(to_info, x, y)) return assign(EMPTY); 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 template inline typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type Interval::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 = Parma_Polyhedra_Library::is_infinity(x); if (inf) { if (Parma_Polyhedra_Library::is_infinity(y) == inf) return assign(EMPTY); } else inf = -Parma_Polyhedra_Library::is_infinity(y); if (inf < 0) return assign(MINUS_INFINITY); else if (inf > 0) return assign(PLUS_INFINITY); PPL_DIRTY_TEMP(To_Info, to_info); to_info.clear(); if (!sub_restriction(to_info, x, y)) return assign(EMPTY); Result rl, 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 template inline typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type Interval::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 = Parma_Polyhedra_Library::is_infinity(x); int ls, us; if (inf) { ls = yls; us = yus; goto inf; } else { inf = Parma_Polyhedra_Library::is_infinity(y); if (inf) { 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 = -inf; if (inf < 0) return assign(MINUS_INFINITY); else return assign(PLUS_INFINITY); } } PPL_DIRTY_TEMP(To_Info, to_info); to_info.clear(); if (!mul_restriction(to_info, x, y)) return assign(EMPTY); Result rl, 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 template inline typename Enable_If<((Is_Singleton::value || Is_Interval::value) && (Is_Singleton::value || Is_Interval::value)), I_Result>::type Interval::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 = Parma_Polyhedra_Library::is_infinity(x); if (inf) { if (Parma_Polyhedra_Library::is_infinity(y)) return assign(EMPTY); if (yls == -yus) return set_infinities(); if (yls < 0 || yus < 0) inf = -inf; if (inf < 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(); if (!div_restriction(to_info, x, y)) return assign(EMPTY); Result rl, 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 { // FIXME: restrictions return static_cast(assign(UNIVERSE) | I_SINGULARITIES); } assign_or_swap(lower(), to_lower); assign_or_swap(info(), to_info); PPL_ASSERT(OK()); return combine(rl, ru); } template inline typename Enable_If::value, Interval >::type operator+(const Interval& x, const T& y) { Interval z; z.add_assign(x, y); return z; } template inline typename Enable_If::value, Interval >::type operator+(const T& x, const Interval& y) { Interval z; z.add_assign(x, y); return z; } template inline Interval operator+(const Interval& x, const Interval& y) { Interval z; z.add_assign(x, y); return z; } template inline typename Enable_If::value, Interval >::type operator-(const Interval& x, const T& y) { Interval z; z.sub_assign(x, y); return z; } template inline typename Enable_If::value, Interval >::type operator-(const T& x, const Interval& y) { Interval z; z.sub_assign(x, y); return z; } template inline Interval operator-(const Interval& x, const Interval& y) { Interval z; z.sub_assign(x, y); return z; } template inline typename Enable_If::value, Interval >::type operator*(const Interval& x, const T& y) { Interval z; z.mul_assign(x, y); return z; } template inline typename Enable_If::value, Interval >::type operator*(const T& x, const Interval& y) { Interval z; z.mul_assign(x, y); return z; } template inline Interval operator*(const Interval& x, const Interval& y) { Interval z; z.mul_assign(x, y); return z; } template inline typename Enable_If::value, Interval >::type operator/(const Interval& x, const T& y) { Interval z; z.div_assign(x, y); return z; } template inline typename Enable_If::value, Interval >::type operator/(const T& x, const Interval& y) { Interval z; z.div_assign(x, y); return z; } template inline Interval operator/(const Interval& x, const Interval& y) { Interval z; z.div_assign(x, y); return z; } template inline std::ostream& operator<<(std::ostream& os, const Interval& x) { // PPL_ASSERT(x.OK()); 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() ? ")" : "]"); output_restriction(os, x.info()); return os; } template inline void Interval::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 inline bool Interval::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 struct Select_Temp_Boundary_Type; template struct Select_Temp_Boundary_Type { typedef Interval_Boundary_Type type; }; template <> struct Select_Temp_Boundary_Type { typedef double type; }; template <> struct Select_Temp_Boundary_Type { typedef signed long long type; }; template <> struct Select_Temp_Boundary_Type { typedef signed long long type; }; template <> struct Select_Temp_Boundary_Type { typedef signed long long type; }; template <> struct Select_Temp_Boundary_Type { typedef signed long long type; }; template <> struct Select_Temp_Boundary_Type { typedef signed long long type; }; template <> struct Select_Temp_Boundary_Type { typedef signed long long type; }; template <> struct Select_Temp_Boundary_Type { typedef signed long long type; }; template <> struct Select_Temp_Boundary_Type { typedef signed long long type; }; template <> struct Select_Temp_Boundary_Type { typedef signed long long type; }; template <> struct Select_Temp_Boundary_Type { typedef signed long long type; }; } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Interval */ template inline void swap(Parma_Polyhedra_Library::Interval& x, Parma_Polyhedra_Library::Interval& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/Interval.templates.hh line 1. */ /* Interval class implementation: non-inline template functions. */ #include namespace Parma_Polyhedra_Library { template template typename Enable_If::value, I_Result>::type Interval::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: case V_EQ: open = false; break; default: PPL_ASSERT(false); } min_assign(LOWER, lower(), info(), LOWER, c.value(), f_info(c.value(), open)); PPL_ASSERT(OK()); return I_ANY; } template template typename Enable_If::value, I_Result>::type Interval::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: case V_EQ: open = false; break; default: PPL_ASSERT(false); } max_assign(UPPER, upper(), info(), UPPER, c.value(), f_info(c.value(), open)); PPL_ASSERT(OK()); return I_ANY; } template template typename Enable_If::value, void>::type Interval::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& 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 Interval::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); // Buld 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: case V_GE: break; case V_GT: lower_open = true; break; case V_GT_MINUS_INFINITY: lower_open = true; case V_EQ_MINUS_INFINITY: lower_boundary_infinity = true; break; case V_EQ_PLUS_INFINITY: 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_ASSERT(false); } switch (upper_r) { case V_EQ: case V_LE: break; case V_LT: upper_open = true; break; case V_EQ_MINUS_INFINITY: 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; case V_EQ_PLUS_INFINITY: upper_boundary_infinity = true; break; default: PPL_ASSERT(false); } 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 inline std::istream& operator>>(std::istream& is, Interval& x) { // Eat leading white space. int c; do { c = is.get(); } while (isspace(c)); // Get the opening parenthesis and handle the empty interval case. bool lower_open = false; if (c == '(') lower_open = true; else if (c == '[') { c = is.get(); if (c == ']') { // Empty interval. x.assign(EMPTY); return is; } else is.unget(); } else { is.unget(); is.setstate(std::ios_base::failbit); return is; } // Get the lower bound. Boundary lower_bound; Result lower_r = input(lower_bound, is, ROUND_DOWN); if (lower_r == V_CVT_STR_UNK || lower_r == V_NAN) { is.setstate(std::ios_base::failbit); return is; } lower_r = result_relation_class(lower_r); // Match the comma separating the lower and upper bounds. do { c = is.get(); } while (isspace(c)); if (c != ',') { is.unget(); is.setstate(std::ios_base::failbit); return is; } // Get the upper bound. Boundary upper_bound; Result upper_r = input(upper_bound, is, ROUND_UP); if (upper_r == V_CVT_STR_UNK || upper_r == V_NAN) { is.setstate(std::ios_base::failbit); return is; } upper_r = result_relation_class(upper_r); // Get the closing parenthesis. do { c = is.get(); } while (isspace(c)); bool upper_open = false; if (c == ')') upper_open = true; else if (c != ']') { is.unget(); is.setstate(std::ios_base::failbit); return is; } // Buld interval. bool lower_boundary_infinity = false; bool upper_boundary_infinity = false; switch (lower_r) { case V_EQ: case V_GE: break; case V_GT: lower_open = true; break; case V_GT_MINUS_INFINITY: lower_open = true; case V_EQ_MINUS_INFINITY: lower_boundary_infinity = true; break; case V_EQ_PLUS_INFINITY: 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_ASSERT(false); } switch (upper_r) { case V_EQ: case V_LE: break; case V_LT: upper_open = true; break; case V_GT_MINUS_INFINITY: upper_open = true; 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: case V_LT_PLUS_INFINITY: upper_boundary_infinity = true; break; default: PPL_ASSERT(false); } 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 template typename Enable_If::value, bool>::type Interval::simplify_using_context_assign(const From& y) { // FIXME: the following code wrongly assumes that intervals are closed // and have no restrictions. It must be generalized. 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 template typename Enable_If::value, void>::type Interval::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 846. */ /* Automatically generated from PPL source file ../src/Polyhedron.templates.hh line 30. */ #include #include namespace Parma_Polyhedra_Library { template Polyhedron::Polyhedron(Topology topol, const Box& box, Complexity_Class) : con_sys(topol), gen_sys(topol), 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; } // Insert a dummy constraint of the highest dimension to avoid the // need of resizing the matrix of constraints later; // this constraint will be removed at the end. con_sys.insert(Variable(space_dim - 1) >= 0); 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; ) { // See if we have a valid lower bound. bool l_closed = false; bool l_bounded = box.get_lower_bound(k, l_closed, l_n, l_d); // See if we have a valid upper bound. bool u_closed = false; bool u_bounded = box.get_upper_bound(k, u_closed, u_n, u_d); // 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 * Variable(k) == l_n); } else { if (l_bounded) // Add the constraint `l_d*v_k >= l_n'. con_sys.insert(l_d * Variable(k) >= l_n); if (u_bounded) // Add the constraint `u_d*v_k <= u_n'. con_sys.insert(u_d * Variable(k) <= u_n); } } } else { // topol == NOT_NECESSARILY_CLOSED for (dimension_type k = space_dim; k-- > 0; ) { // See if we have a valid lower bound. bool l_closed = false; bool l_bounded = box.get_lower_bound(k, l_closed, l_n, l_d); // See if we have a valid upper bound. bool u_closed = false; bool u_bounded = box.get_upper_bound(k, u_closed, u_n, u_d); // 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 * Variable(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 * Variable(k) >= l_n); else // Add the constraint `l_d*v_k > l_n'. con_sys.insert(l_d * Variable(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 * Variable(k) <= u_n); else // Add the constraint `u_d*v_k < u_n'. con_sys.insert(u_d * Variable(k) < u_n); } } } } // Adding the low-level constraints. con_sys.add_low_level_constraints(); // Now removing the dummy constraint inserted before. dimension_type n_rows = con_sys.num_rows() - 1; con_sys[0].swap(con_sys[n_rows]); con_sys.set_sorted(false); // NOTE: here there are no pending constraints. con_sys.set_index_first_pending_row(n_rows); con_sys.erase_to_end(n_rows); // Constraints are up-to-date. set_constraints_up_to_date(); PPL_ASSERT_HEAVY(OK()); } template 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. // We first compute suitable permutation cycles for the columns of // the `con_sys' and `gen_sys' matrices. We will represent them // with a linear array, using 0 as a terminator for each cycle // (notice that the columns with index 0 of `con_sys' and // `gen_sys' represent the inhomogeneous terms, and thus are // unaffected by the permutation of dimensions). // Cycles of length 1 will be omitted so that, in the worst case, // we will have `space_dim' elements organized in `space_dim/2' // cycles, which means we will have at most `space_dim/2' // terminators. std::vector cycles; cycles.reserve(space_dim + space_dim/2); // Used to mark elements as soon as they are inserted in a cycle. std::deque 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) // Cycle of length 1: skip it. goto skip; cycles.push_back(j+1); // Go along the cycle. j = k; } while (!visited[j]); // End of cycle: mark it. cycles.push_back(0); skip: ; } } // If `cycles' is empty then `pfunc' is the identity. if (cycles.empty()) return; // 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_columns(cycles); if (generators_are_up_to_date()) gen_sys.permute_columns(cycles); 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); std::swap(*this, new_polyhedron); PPL_ASSERT_HEAVY(OK()); return; } // Make a local copy of the partial function. std::vector 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; Linear_Expression e(0 * Variable(new_space_dimension-1)); bool all_zeroes = true; for (dimension_type j = space_dim; j-- > 0; ) { if (old_g.coefficient(Variable(j)) != 0 && pfunc_maps[j] != not_a_dimension()) { e += Variable(pfunc_maps[j]) * old_g.coefficient(Variable(j)); all_zeroes = false; } } switch (old_g.type()) { case Generator::LINE: if (!all_zeroes) new_gensys.insert(line(e)); break; case Generator::RAY: if (!all_zeroes) new_gensys.insert(ray(e)); break; case Generator::POINT: // A point in the origin has all zero homogeneous coefficients. new_gensys.insert(point(e, old_g.divisor())); break; case Generator::CLOSURE_POINT: // A closure point in the origin has all zero homogeneous coefficients. new_gensys.insert(closure_point(e, old_g.divisor())); break; } } Polyhedron new_polyhedron(topology(), new_gensys); std::swap(*this, new_polyhedron); PPL_ASSERT_HEAVY(OK(true)); } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Polyhedron.defs.hh line 2627. */ /* 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 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 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 97. */ /* 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 32. */ #include //! 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 true 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 true 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. */ 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 103. */ /* 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 #ifndef NDEBUG #include #endif #include 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 vec; dimension_type max; #ifndef NDEBUG std::set 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. */ #include #include 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::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; 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 class Widening_Function; template 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 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 (x.*wm)(y, tp), 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 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 (x.*lwm)(y, cs, tp), 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 Widening_Function 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 Limited_Widening_Function 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 Widening_Function::Widening_Function(Widening_Method wm) : w_method(wm) { } template inline void Widening_Function:: operator()(PSET& x, const PSET& y, unsigned* tp) const { (x.*w_method)(y, tp); } template Limited_Widening_Function:: Limited_Widening_Function(Limited_Widening_Method lwm, const CSYS& cs) : lw_method(lwm), limiting_cs(cs) { } template inline void Limited_Widening_Function:: operator()(PSET& x, const PSET& y, unsigned* tp) const { (x.*lw_method)(y, limiting_cs, tp); } /*! \relates Pointset_Powerset */ template inline Widening_Function widen_fun_ref(void (PSET::* wm)(const PSET&, unsigned*)) { return Widening_Function(wm); } /*! \relates Pointset_Powerset */ template inline Limited_Widening_Function widen_fun_ref(void (PSET::* lwm)(const PSET&, const CSYS&, unsigned*), const CSYS& cs) { return Limited_Widening_Function(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/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 topologically closed 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 strict inequality constraint. Similarly, an exception is thrown when building a closed polyhedron starting from a system of generators containing a closure point. \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 const 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 const 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 const 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 explicit C_Polyhedron(const Box& 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 explicit C_Polyhedron(const BD_Shape& 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 explicit C_Polyhedron(const Octagonal_Shape& 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 true is returned, otherwise false 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); }; /* Automatically generated from PPL source file ../src/C_Polyhedron.inlines.hh line 1. */ /* C_Polyhedron class implementation: inline functions. */ #include #include namespace Parma_Polyhedra_Library { inline C_Polyhedron::~C_Polyhedron() { } inline C_Polyhedron::C_Polyhedron(dimension_type num_dimensions, Degenerate_Element kind) : Polyhedron(NECESSARILY_CLOSED, num_dimensions <= max_space_dimension() ? num_dimensions : (throw_space_dimension_overflow(NECESSARILY_CLOSED, "C_Polyhedron(n, k)", "n exceeds the maximum " "allowed space dimension"), num_dimensions), kind) { } inline C_Polyhedron::C_Polyhedron(const Constraint_System& cs) : Polyhedron(NECESSARILY_CLOSED, cs.space_dimension() <= max_space_dimension() ? cs : (throw_space_dimension_overflow(NECESSARILY_CLOSED, "C_Polyhedron(cs)", "the space dimension of cs " "exceeds the maximum allowed " "space dimension"), cs)) { } inline C_Polyhedron::C_Polyhedron(Constraint_System& cs, Recycle_Input) : Polyhedron(NECESSARILY_CLOSED, cs.space_dimension() <= max_space_dimension() ? cs : (throw_space_dimension_overflow(NECESSARILY_CLOSED, "C_Polyhedron(cs, recycle)", "the space dimension of cs " "exceeds the maximum allowed " "space dimension"), cs), Recycle_Input()) { } inline C_Polyhedron::C_Polyhedron(const Generator_System& gs) : Polyhedron(NECESSARILY_CLOSED, gs.space_dimension() <= max_space_dimension() ? gs : (throw_space_dimension_overflow(NECESSARILY_CLOSED, "C_Polyhedron(gs)", "the space dimension of gs " "exceeds the maximum allowed " "space dimension"), gs)) { } inline C_Polyhedron::C_Polyhedron(Generator_System& gs, Recycle_Input) : Polyhedron(NECESSARILY_CLOSED, gs.space_dimension() <= max_space_dimension() ? gs : (throw_space_dimension_overflow(NECESSARILY_CLOSED, "C_Polyhedron(gs, recycle)", "the space dimension of gs " "exceeds the maximum allowed " "space dimension"), gs), Recycle_Input()) { } template inline C_Polyhedron::C_Polyhedron(const Box& box, Complexity_Class) : Polyhedron(NECESSARILY_CLOSED, box.space_dimension() <= max_space_dimension() ? box : (throw_space_dimension_overflow(NECESSARILY_CLOSED, "C_Polyhedron(box): ", "the space dimension of box " "exceeds the maximum allowed " "space dimension"), box)) { } template inline C_Polyhedron::C_Polyhedron(const BD_Shape& bd, Complexity_Class) : Polyhedron(NECESSARILY_CLOSED, bd.space_dimension() <= max_space_dimension() ? bd.space_dimension() : (throw_space_dimension_overflow(NECESSARILY_CLOSED, "C_Polyhedron(bd): ", "the space dimension of bd " "exceeds the maximum allowed " "space dimension"), 0), UNIVERSE) { add_constraints(bd.constraints()); } template inline C_Polyhedron::C_Polyhedron(const Octagonal_Shape& os, Complexity_Class) : Polyhedron(NECESSARILY_CLOSED, os.space_dimension() <= max_space_dimension() ? os.space_dimension() : (throw_space_dimension_overflow(NECESSARILY_CLOSED, "C_Polyhedron(os): ", "the space dimension of os " "exceeds the maximum allowed " "space dimension"), 0), 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); 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 279. */ /* 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 not necessarily closed (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 const 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 const 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 const 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 const 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 explicit NNC_Polyhedron(const Box& 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 explicit NNC_Polyhedron(const BD_Shape& 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 explicit NNC_Polyhedron(const Octagonal_Shape& 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 true is returned, otherwise false 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); }; /* 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, num_dimensions <= max_space_dimension() ? num_dimensions : (throw_space_dimension_overflow(NOT_NECESSARILY_CLOSED, "NNC_Polyhedron(n, k)", "n exceeds the maximum " "allowed space dimension"), num_dimensions), kind) { } inline NNC_Polyhedron::NNC_Polyhedron(const Constraint_System& cs) : Polyhedron(NOT_NECESSARILY_CLOSED, cs.space_dimension() <= max_space_dimension() ? cs : (throw_space_dimension_overflow(NOT_NECESSARILY_CLOSED, "NNC_Polyhedron(cs)", "the space dimension of cs " "exceeds the maximum allowed " "space dimension"), cs)) { } inline NNC_Polyhedron::NNC_Polyhedron(Constraint_System& cs, Recycle_Input) : Polyhedron(NOT_NECESSARILY_CLOSED, cs.space_dimension() <= max_space_dimension() ? cs : (throw_space_dimension_overflow(NOT_NECESSARILY_CLOSED, "NNC_Polyhedron(cs, recycle)", "the space dimension of cs " "exceeds the maximum allowed " "space dimension"), cs), Recycle_Input()) { } inline NNC_Polyhedron::NNC_Polyhedron(const Generator_System& gs) : Polyhedron(NOT_NECESSARILY_CLOSED, gs.space_dimension() <= max_space_dimension() ? gs : (throw_space_dimension_overflow(NOT_NECESSARILY_CLOSED, "NNC_Polyhedron(gs)", "the space dimension of gs " "exceeds the maximum allowed " "space dimension"), gs)) { } inline NNC_Polyhedron::NNC_Polyhedron(Generator_System& gs, Recycle_Input) : Polyhedron(NOT_NECESSARILY_CLOSED, gs.space_dimension() <= max_space_dimension() ? gs : (throw_space_dimension_overflow(NOT_NECESSARILY_CLOSED, "NNC_Polyhedron(gs, recycle)", "the space dimension of gs " "exceeds the maximum allowed " "space dimension"), gs), Recycle_Input()) { } template inline NNC_Polyhedron::NNC_Polyhedron(const Box& box, Complexity_Class) : Polyhedron(NOT_NECESSARILY_CLOSED, box.space_dimension() <= max_space_dimension() ? box : (throw_space_dimension_overflow(NOT_NECESSARILY_CLOSED, "NNC_Polyhedron(box)", "the space dimension of box " "exceeds the maximum allowed " "space dimension"), box)) { } template inline NNC_Polyhedron::NNC_Polyhedron(const BD_Shape& bd, Complexity_Class) : Polyhedron(NOT_NECESSARILY_CLOSED, bd.space_dimension() <= max_space_dimension() ? bd.space_dimension() : (throw_space_dimension_overflow(NECESSARILY_CLOSED, "NNC_Polyhedron(bd): ", "the space dimension of bd " "exceeds the maximum allowed " "space dimension"), 0), UNIVERSE) { add_constraints(bd.constraints()); } template inline NNC_Polyhedron::NNC_Polyhedron(const Octagonal_Shape& os, Complexity_Class) : Polyhedron(NOT_NECESSARILY_CLOSED, os.space_dimension() <= max_space_dimension() ? os.space_dimension() : (throw_space_dimension_overflow(NECESSARILY_CLOSED, "NNC_Polyhedron(os): ", "the space dimension of os " "exceeds the maximum allowed " "space dimension"), 0), 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); 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); } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/NNC_Polyhedron.defs.hh line 255. */ /* Automatically generated from PPL source file ../src/Grid.defs.hh line 1. */ /* Grid class declaration. */ /* Automatically generated from PPL source file ../src/Grid.defs.hh line 47. */ #include #include namespace Parma_Polyhedra_Library { namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Grid Writes a textual representation of \p gr on \p s: false is written if \p gr is an empty grid; true 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 /*! \brief Returns true 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 false is returned. */ bool operator==(const Grid& x, const Grid& y); /*! \brief Returns true 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 true 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 optimally supports: - 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 explicit 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 x and 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 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 add_space_dimensions_and_embed: \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 add_space_dimensions_and_project: \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 add_space_dimensions_and_embed. 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 affine_image: \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 affine_preimage: \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 remove_space_dimensions: \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 incrementally: since dimensions are automatically renamed after each application of the remove_space_dimensions operator, unexpected results can be obtained. For instance, by using the following code we would obtain a different result: \code set vars1; vars1.insert(y); gr.remove_space_dimensions(vars1); set 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 const_gs 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& const_gs); //! Builds a grid, recycling a system of grid generators. /*! The grid inherits the space dimension of the generator system. \param gs 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& gs, 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 explicit Grid(const Box& 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 explicit Grid(const BD_Shape& 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 explicit Grid(const Octagonal_Shape& 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 true 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 true 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 true 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 true if and only if \p *this is bounded. bool is_bounded() const; /*! \brief Returns true if and only if \p *this contains at least one integer point. */ bool contains_integer_point() const; /*! \brief Returns true 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 true 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 true 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 true 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 true if the supremum value can be reached in \p this. Always true 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, false 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 true 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 true if the supremum value can be reached in \p this. Always true 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, false 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 true 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 true if the is the infimum value can be reached in \p this. Always true 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, false 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 true 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 true if the is the infimum value can be reached in \p this. Always true 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, false 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 true 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 false 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 true 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 true 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 true if and only if \p *this satisfies all the invariants and either \p check_not_empty is false or \p *this is not empty. \param check_not_empty true 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 std::cerr 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 whcih 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 whcih 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 true is returned, otherwise false 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 pcs 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 true 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 *pcs. \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* pcs = 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 must 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 must 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 must 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 must 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 must 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 must 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 max_space_dimension(). 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 max_space_dimension(). 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 max_space_dimension(). */ 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 true if and only if the represented partial function has an empty codomain (i.e., it is always undefined). The has_empty_codomain() method will always be called before the methods below. However, if has_empty_codomain() returns true, 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 max_in_codomain() 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 true is returned. If \f$f\f$ is undefined in \f$k\f$, then false 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 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 max_space_dimension(). If \p *this has space dimension \f$n\f$, with \f$n > 0\f$, and var 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$, dest 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 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 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 true if successful, false 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 x == y, then x.hash_code() == y.hash_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: - zero-dim universe: the grid is the zero-dimension vector space \f$\Rset^0 = \{\cdot\}\f$; - empty: the grid is the empty set; - congruences up-to-date: the grid is correctly characterized by the attached system of congruences, modulo the processing of pending generators; - generators up-to-date: the grid is correctly characterized by the attached system of generators, modulo the processing of pending congruences; - congruences minimized: the non-pending part of the system of congruences attached to the grid is in minimal form; - generators minimized: the non-pending part of the system of generators attached to the grid is in minimal form. Other supported assertions are: - congruences pending - generators pending - congruences' saturation matrix up-to-date - generators' saturation matrix up-to-date. Not all the conjunctions of these elementary assertions constitute a legal Status. In fact: - zero-dim universe excludes any other assertion; - empty: excludes any other assertion; - congruences pending and generators pending are mutually exclusive; - congruences pending implies both congruences minimized and generators minimized; - generators pending implies both congruences minimized and generators minimized; - congruences minimized implies congruences up-to-date; - generators minimized implies generators up-to-date; - congruences' saturation matrix up-to-date implies both congruences up-to-date and generators up-to-date; - generators' saturation matrix up-to-date implies both congruences up-to-date and generators up-to-date. */ class Status { public: //! By default Status is the zero-dim universe 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 true if successful, false 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 all bits in \p mask are set. bool test_all(flags_t mask) const; //! Check whether at least one 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 1973. */ #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, LINE, GEN_VIRTUAL, PROPER_CONGRUENCE = PARAMETER, CON_VIRTUAL = LINE, EQUALITY = GEN_VIRTUAL }; typedef std::vector 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 true if the grid is known to be empty. /*! The return value false does not necessarily implies that \p *this is non-empty. */ bool marked_empty() const; //! Returns true if the system of congruences is up-to-date. bool congruences_are_up_to_date() const; //! Returns true if the system of generators is up-to-date. bool generators_are_up_to_date() const; //! Returns true if the system of congruences is minimized. bool congruences_are_minimized() const; //! Returns true 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 false 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 false 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 true 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 true if and only if \p from_above is true and \p expr is bounded from above in \p *this, or \p from_above is false 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 true 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, false 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 true 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 false 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 true if and only if \p from_above is true and \p expr is bounded from above in \p *this, or \p from_above is false 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 add_space_dimensions_and_embed(). */ 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 add_space_dimensions_and_project(). */ 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 true if \p cgs represents the empty set, otherwise returns false. */ 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& gs, 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 col of \p row is zero. */ // A member of Grid for access to Matrix::rows. static void reduce_line_with_line(Grid_Generator& row, Grid_Generator& pivot, dimension_type col); //! 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 col of \p row is zero. */ // A member of Grid for access to Matrix::rows. static void reduce_equality_with_equality(Congruence& row, const Congruence& pivot, dimension_type col); //! 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 col 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). */ // Part of Grid for access to Matrix::rows. template static void reduce_pc_with_pc(R& row, R& pivot, dimension_type col, 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 col of \p row is zero. */ // A member of Grid for access to Matrix::rows. static void reduce_parameter_with_line(Grid_Generator& row, const Grid_Generator& pivot, dimension_type col, Grid_Generator_System& sys); //! 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 col of \p row is zero. */ // A member of Grid for access to Matrix::rows. static void reduce_congruence_with_equality(Congruence& row, const Congruence& pivot, dimension_type col, Congruence_System& 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). Only consider from index \p start to index \p end of the row at \p pivot_index. Flag \p generators indicates whether \p sys is a congruence or generator system. */ template static void reduce_reduced(M& sys, dimension_type dim, dimension_type pivot_index, dimension_type start, dimension_type end, const Dimension_Kinds& dim_kinds, bool generators = true); //! Multiply the elements of \p dest by \p multiplier. // A member of Grid for access to Matrix::rows and cgs::operator[]. static void multiply_grid(const Coefficient& multiplier, Congruence& cg, Congruence_System& dest, dimension_type num_rows, dimension_type num_dims); //! Multiply the elements of \p dest by \p multiplier. // A member of Grid for access to Grid_Generator::operator[]. static void multiply_grid(const Coefficient& multiplier, Grid_Generator& gen, Grid_Generator_System& dest, dimension_type num_rows, dimension_type num_dims); /*! \brief If \p sys is lower triangular return true, else return false. */ static bool lower_triangular(const Congruence_System& sys, const Dimension_Kinds& dim_kinds); /*! \brief If \p sys is upper triangular return true, else return false. */ 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 true, else return false. \p row_size gives the number of columns in each row. This method is only used in assertions in the simplify methods. */ template 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_runtime_error(const char* method) const; void throw_invalid_argument(const char* method, const char* reason) 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* gr_name, const Grid& gr) const; void throw_dimension_incompatible(const char* method, const char* e_name, const Linear_Expression& e) 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; // Note: it has to be a static method, because it can be called inside // constructors (before actually constructing the grid object). static void throw_space_dimension_overflow(const char* method, const char* reason); void throw_invalid_constraint(const char* method, const char* c_name) const; void throw_invalid_constraints(const char* method, const char* cs_name) const; 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) }; namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Grid */ void swap(Parma_Polyhedra_Library::Grid& x, Parma_Polyhedra_Library::Grid& y); } // namespace std /* 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; } 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 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::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(num_dimensions > max_space_dimension() ? (throw_space_dimension_overflow("Grid(n, k)", "n exceeds the maximum " "allowed space dimension"), 0) : num_dimensions) { construct(num_dimensions, kind); PPL_ASSERT(OK()); } inline Grid::Grid(const Congruence_System& cgs) : con_sys(cgs.space_dimension() > max_space_dimension() ? throw_space_dimension_overflow("Grid(cgs)", "the space dimension of cgs " "exceeds the maximum allowed " "space dimension"), 0 : cgs.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(cgs.space_dimension() > max_space_dimension() ? throw_space_dimension_overflow("Grid(cgs, recycle)", "the space dimension of cgs " "exceeds the maximum allowed " "space dimension"), 0 : cgs.space_dimension()), gen_sys(cgs.space_dimension()) { construct(cgs); } inline Grid::Grid(const Grid_Generator_System& ggs) : con_sys(ggs.space_dimension() > max_space_dimension() ? throw_space_dimension_overflow("Grid(ggs)", "the space dimension of ggs " "exceeds the maximum allowed " "space dimension"), 0 : ggs.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(ggs.space_dimension() > max_space_dimension() ? throw_space_dimension_overflow("Grid(ggs, recycle)", "the space dimension of ggs " "exceeds the maximum allowed " "space dimension"), 0 : ggs.space_dimension()), gen_sys(ggs.space_dimension()) { construct(ggs); } template inline Grid::Grid(const BD_Shape& bd, Complexity_Class) : con_sys(bd.space_dimension() > max_space_dimension() ? throw_space_dimension_overflow("Grid(bd)", "the space dimension of bd " "exceeds the maximum allowed " "space dimension"), 0 : bd.space_dimension()), gen_sys(bd.space_dimension()) { Congruence_System cgs = bd.congruences(); construct(cgs); } template inline Grid::Grid(const Octagonal_Shape& os, Complexity_Class) : con_sys(os.space_dimension() > max_space_dimension() ? throw_space_dimension_overflow("Grid(os)", "the space dimension of os " "exceeds the maximum allowed " "space dimension"), 0 : os.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 space_dimension() & 0x7fffffff; } 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::swap(Grid& y) { std::swap(con_sys, y.con_sys); std::swap(gen_sys, y.gen_sys); std::swap(status, y.status); std::swap(space_dim, y.space_dim); std::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() { } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Grid */ inline void swap(Parma_Polyhedra_Library::Grid& x, Parma_Polyhedra_Library::Grid& y) { x.swap(y); } } // namespace std /* 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 #include namespace Parma_Polyhedra_Library { template Grid::Grid(const Box& box, Complexity_Class) : con_sys(), gen_sys() { if (box.space_dimension() > max_space_dimension()) throw_space_dimension_overflow("Grid(box, from_bounding_box)", "the space dimension of box " "exceeds the maximum allowed " "space dimension"); space_dim = box.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.increase_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(0*Variable(space_dim-1))); for (dimension_type k = space_dim; k-- > 0; ) { // 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[0]; bool closed = false; // TODO: Consider producing the system(s) in minimized form. if (box.get_lower_bound(k, closed, l_n, l_d)) { if (box.get_upper_bound(k, closed, u_n, u_d)) 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 * Variable(k) == l_n); // 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[k + 1] = l_n * u_n; continue; } } // A universe interval allows any value in dimension k. gen_sys.insert(grid_line(Variable(k))); } set_congruences_up_to_date(); set_generators_up_to_date(); gen_sys.unset_pending_rows(); gen_sys.set_sorted(false); } PPL_ASSERT(OK()); } template 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. // We first compute suitable permutation cycles for the columns of // the `con_sys' and `gen_sys' matrices. We will represent them // with a linear array, using 0 as a terminator for each cycle // (notice that the columns with index 0 of `con_sys' and // `gen_sys' represent the inhomogeneous terms, and thus are // unaffected by the permutation of dimensions). // Cycles of length 1 will be omitted so that, in the worst case, // we will have `space_dim' elements organized in `space_dim/2' // cycles, which means we will have at most `space_dim/2' // terminators. std::vector cycles; cycles.reserve(space_dim + space_dim/2); // Used to mark elements as soon as they are inserted in a cycle. std::deque 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) // Cycle of length 1: skip it. goto skip; cycles.push_back(j+1); // Go along the cycle. j = k; } while (!visited[j]); // End of cycle: mark it. cycles.push_back(0); skip: ; } } // If `cycles' is empty then `pfunc' is the identity. if (cycles.empty()) return; // Permute all that is up-to-date. if (congruences_are_up_to_date()) { con_sys.permute_columns(cycles); clear_congruences_minimized(); } if (generators_are_up_to_date()) { gen_sys.permute_columns(cycles); clear_generators_minimized(); } 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); std::swap(*this, new_grid); PPL_ASSERT(OK()); return; } // Make a local copy of the partial function. std::vector 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; Linear_Expression e(0 * Variable(new_space_dimension-1)); bool all_zeroes = true; for (dimension_type j = space_dim; j-- > 0; ) { if (old_g.coefficient(Variable(j)) != 0 && pfunc_maps[j] != not_a_dimension()) { e += Variable(pfunc_maps[j]) * old_g.coefficient(Variable(j)); all_zeroes = false; } } switch (old_g.type()) { case Grid_Generator::LINE: if (!all_zeroes) new_gensys.insert(grid_line(e)); break; case Grid_Generator::PARAMETER: if (!all_zeroes) new_gensys.insert(parameter(e, system_divisor)); break; case Grid_Generator::POINT: new_gensys.insert(grid_point(e, old_g.divisor())); break; default: PPL_ASSERT(0); } } Grid new_grid(new_gensys); std::swap(*this, new_grid); PPL_ASSERT(OK(true)); } // Needed for converting the congruence or grid_generator system // to "strong minimal form". template void Grid::reduce_reduced(M& sys, const dimension_type dim, const dimension_type pivot_index, const dimension_type start, const dimension_type end, const Dimension_Kinds& dim_kinds, const bool generators) { R& pivot = sys[pivot_index]; const Coefficient& pivot_dim = pivot[dim]; if (pivot_dim == 0) return; PPL_DIRTY_TEMP_COEFFICIENT(pivot_dim_half); pivot_dim_half = (pivot_dim + 1) / 2; Dimension_Kind row_kind = dim_kinds[dim]; Dimension_Kind line_or_equality, virtual_kind; int jump; if (generators) { line_or_equality = LINE; virtual_kind = GEN_VIRTUAL; jump = -1; } else { line_or_equality = EQUALITY; virtual_kind = CON_VIRTUAL; jump = 1; } PPL_DIRTY_TEMP_COEFFICIENT(num_rows_to_subtract); PPL_DIRTY_TEMP_COEFFICIENT(row_dim_remainder); for (dimension_type row_index = pivot_index, kinds_index = dim + jump; row_index-- > 0; kinds_index += jump) { // Move over any virtual rows. while (dim_kinds[kinds_index] == virtual_kind) kinds_index += jump; // row_kind CONGRUENCE is included as PARAMETER if (row_kind == line_or_equality || (row_kind == PARAMETER && dim_kinds[kinds_index] == PARAMETER)) { R& row = sys[row_index]; const Coefficient& row_dim = row[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) for (dimension_type col = start; col <= end; ++col) sub_mul_assign(row[col], num_rows_to_subtract, pivot[col]); } } } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Grid.defs.hh line 2639. */ /* 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_Interval.hh line 1. */ /* Rational_Interval class declaration and implementation. */ /* Automatically generated from PPL source file ../src/Rational_Interval.hh line 28. */ #include 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_Restriction_None > 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 Rational_Interval; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Box.defs.hh line 1. */ /* Box class declaration. */ /* Automatically generated from PPL source file ../src/Partially_Reduced_Product.types.hh line 1. */ namespace Parma_Polyhedra_Library { template class Smash_Reduction; template class Constraints_Reduction; template class Congruences_Reduction; template class Shape_Preserving_Reduction; template class No_Reduction; template class Partially_Reduced_Product; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Box.defs.hh line 48. */ #include #include namespace Parma_Polyhedra_Library { struct Interval_Base; //! Returns true 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 false is returned. */ template bool operator==(const Box& x, const Box& y); //! Returns true if and only if \p x and \p y aren't the same box. /*! \relates Box Note that \p x and \p y may be dimension-incompatible boxes: in this case, the value true is returned. */ template bool operator!=(const Box& x, const Box& y); namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Box */ template std::ostream& operator<<(std::ostream& s, const Box& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool rectilinear_distance_assign(Checked_Number& r, const Box& x, const Box& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool rectilinear_distance_assign(Checked_Number& r, const Box& x, const Box& 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 true; returns false 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 bool rectilinear_distance_assign(Checked_Number& r, const Box& x, const Box& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool euclidean_distance_assign(Checked_Number& r, const Box& x, const Box& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool euclidean_distance_assign(Checked_Number& r, const Box& x, const Box& 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 true; returns false 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 bool euclidean_distance_assign(Checked_Number& r, const Box& x, const Box& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool l_infinity_distance_assign(Checked_Number& r, const Box& x, const Box& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool l_infinity_distance_assign(Checked_Number& r, const Box& x, const Box& 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 true; returns false 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 bool l_infinity_distance_assign(Checked_Number& r, const Box& x, const Box& 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 bool l_m_distance_assign(Checked_Number& r, const Box& x, const Box& 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 interval constraint (resp., interval congruence) is a syntactic constraint (resp., congruence) that only mentions a single space dimension. The Box domain optimally supports: - 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 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 explicit Box(const Box& 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 explicit Box(const BD_Shape& 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 explicit Box(const Octagonal_Shape& 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& ph, 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 explicit Box(const Partially_Reduced_Product& 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 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 true if and only if \p *this is an empty box. bool is_empty() const; //! Returns true if and only if \p *this is a universe box. bool is_universe() const; /*! \brief Returns true if and only if \p *this is a topologically closed subset of the vector space. */ bool is_topologically_closed() const; //! Returns true if and only if \p *this is discrete. bool is_discrete() const; //! Returns true if and only if \p *this is a bounded box. bool is_bounded() const; /*! \brief Returns true if and only if \p *this contains at least one integer point. */ bool contains_integer_point() const; /*! \brief Returns true 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 true 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 true 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 true 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 true 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, false 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 true 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 true 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, false 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 true 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 true 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, false 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 true 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 true 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, false 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 true if and only if there exist a unique value \p val such that \p *this saturates the equality expr = val. \param expr The linear expression for which the frequency is needed; \param freq_n If true 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 true 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 false 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 true 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 true 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 true 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 true 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 refine_with_constraint(const Constraint& c) 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 propagagion 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 propagations 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 true is returned, otherwise false 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()); /*! \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 pcs 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 *pcs. \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 *pcs. */ 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* pcs = 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 ANY_COMPLEXITY. */ 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 ANY_COMPLEXITY. */ 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 must 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 Enable_If::value && Is_Same_Or_Derived::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 must 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 Enable_If::value && Is_Same_Or_Derived::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 must 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 must 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 decreasing iteration sequence). Hence, the call x.CC76_narrowing_assign(y) will assign to \p x the result of the computation \f$\mathtt{y} \Delta \mathtt{x}\f$. */ template typename Enable_If::value && Is_Same_Or_Derived::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 true if and only if the represented partial function has an empty co-domain (i.e., it is always undefined). The has_empty_codomain() method will always be called before the methods below. However, if has_empty_codomain() returns true, 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 true is returned. If \f$f\f$ is undefined in \f$k\f$, then false 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 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 max_space_dimension(). If \p *this has space dimension \f$n\f$, with \f$n > 0\f$, and var 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$, dest 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 k-th space dimension is unbounded below, returns false. Otherwise returns true and set \p closed, \p n and \p d accordingly. Let \f$I\f$ the interval corresponding to the k-th space dimension. If \f$I\f$ is not bounded from below, simply return false. Otherwise, set closed, n and d as follows: closed is set to true if the the lower boundary of \f$I\f$ is closed and is set to false otherwise; n and d are assigned the integers \f$n\f$ and \f$d\f$ such that the canonical 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 if and only if \f$n\f$ and \f$d\f$ have no common factors and \f$d\f$ is positive, \f$0/1\f$ being the unique representation for zero. An undefined behavior is obtained if \p k is greater than or equal to the space dimension of \p *this. */ bool get_lower_bound(dimension_type k, bool& closed, Coefficient& n, Coefficient& d) const; /*! \brief If the k-th space dimension is unbounded above, returns false. Otherwise returns true and set \p closed, \p n and \p d accordingly. Let \f$I\f$ the interval corresponding to the k-th space dimension. If \f$I\f$ is not bounded from above, simply return false. Otherwise, set closed, n and d as follows: closed is set to true if the the upper boundary of \f$I\f$ is closed and is set to false otherwise; n and d are assigned the integers \f$n\f$ and \f$d\f$ such that the canonical fraction \f$n/d\f$ corresponds to the least upper bound of \f$I\f$. An undefined behavior is obtained if \p k is greater than or equal to the space dimension of \p *this. */ bool get_upper_bound(dimension_type k, bool& closed, Coefficient& n, Coefficient& d) 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; 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 true if successful, false otherwise. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) bool ascii_load(std::istream& s); private: template friend class Parma_Polyhedra_Library::Box; friend bool operator==(const Box& x, const Box& y); friend std::ostream& Parma_Polyhedra_Library ::IO_Operators::operator<<<>(std::ostream& s, const Box& box); template friend bool Parma_Polyhedra_Library::l_m_distance_assign (Checked_Number& r, const Box& x, const Box& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2); //! The type of sequence used to implement the box. typedef std::vector 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::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 object. /*! \ingroup PPL_CXX_interface The assertions supported are: - empty up-to-date: the empty flag is meaningful; - empty: the box is the empty set. - universe: 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: - empty up-to-date and empty excludes universe. */ 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 Status(const typename Box::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 true if successful, false 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 all bits in \p mask are set. bool test_all(flags_t mask) const; //! Check whether at least one 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 1706. */ #undef PPL_IN_Box_CLASS //! The status flags to keep track of the internal state. Status status; /*! \brief Returns true if and only if the box is known to be empty. The return value false 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 true if and only if it is so. */ bool check_empty() const; /*! \brief Returns a reference the interval that bounds the box on the k-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 num, Coefficient_traits::const_reference den); /*! \brief WRITE ME. */ void add_interval_constraint_no_check(dimension_type var_id, Constraint::Type type, Coefficient_traits::const_reference num, Coefficient_traits::const_reference den); /*! \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 exact; we say it is inexact 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 propagations 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 true if and only if \p from_above is true and \p expr is bounded from above in \p *this, or \p from_above is false and \p expr is bounded from below in \p *this. \param expr The linear expression to test; \param from_above true 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 true 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 true 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, false 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 true 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 true 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, false 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& x) 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* name_row, const Linear_Expression& y) const; static void throw_space_dimension_overflow(const char* method, const char* reason); static void throw_constraint_incompatible(const char* method); static void throw_expression_too_complex(const char* method, const Linear_Expression& e); static void throw_generic(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 num The numerator of the constraint bound; \param den The denominator of the constraint bound The interval constraint has the form den * Variable(0) relsym num where relsym is ==, > or >= depending on the constraint_type. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template Poly_Con_Relation interval_relation(const ITV& i, const Constraint::Type constraint_type, Coefficient_traits::const_reference num, Coefficient_traits::const_reference den = 1); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Decodes the constraint \p c as an interval constraint. /*! \relates Box \return true if the constraint \p c is an \ref intervals "interval constraint"; false otherwise. \param c The constraint to be decoded. \param c_space_dim The space dimension of the constraint \p c (it is assumed to match the actual space dimension of \p c). \param c_num_vars If true 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 true 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) bool extract_interval_constraint(const Constraint& c, dimension_type c_space_dim, dimension_type& c_num_vars, dimension_type& c_only_var); bool extract_interval_congruence(const Congruence& cg, dimension_type cg_space_dim, 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::Status class implementation: inline functions. */ #include namespace Parma_Polyhedra_Library { template inline Box::Status::Status(flags_t mask) : flags(mask) { } template inline Box::Status::Status(const Status& y) : flags(y.flags) { } template template inline Box::Status::Status(const typename Box::Status& y) : flags(y.flags) { } template inline Box::Status::Status() : flags(NONE) { } template inline bool Box::Status::test_all(flags_t mask) const { return (flags & mask) == mask; } template inline bool Box::Status::test_any(flags_t mask) const { return flags & mask; } template inline void Box::Status::set(flags_t mask) { flags |= mask; } template inline void Box::Status::reset(flags_t mask) { flags &= ~mask; } template inline bool Box::Status::test_empty_up_to_date() const { return test_any(EMPTY_UP_TO_DATE); } template inline void Box::Status::reset_empty_up_to_date() { reset(EMPTY_UP_TO_DATE); } template inline void Box::Status::set_empty_up_to_date() { set(EMPTY_UP_TO_DATE); } template inline bool Box::Status::test_empty() const { return test_any(EMPTY); } template inline void Box::Status::reset_empty() { reset(EMPTY); } template inline void Box::Status::set_empty() { set(EMPTY); } template inline bool Box::Status::test_universe() const { return test_any(UNIVERSE); } template inline void Box::Status::reset_universe() { reset(UNIVERSE); } template inline void Box::Status::set_universe() { set(UNIVERSE); } template bool Box::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. const std::string empty_up_to_date = "EUP"; const std::string empty = "EM"; const std::string universe = "UN"; const char yes = '+'; const char no = '-'; const char sep = ' '; /*! \relates Parma_Polyhedra_Library::Box::Status Reads a keyword and its associated on/off flag from \p s. Returns true if the operation is successful, returns false otherwise. When successful, \p positive is set to true if the flag is on; it is set to false otherwise. */ inline bool get_field(std::istream& s, const std::string& 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 void Box::Status::ascii_dump(std::ostream& s) const { using namespace Implementation::Boxes; s << (test_empty_up_to_date() ? yes : no) << empty_up_to_date << sep << (test_empty() ? yes : no) << empty << sep << (test_universe() ? yes : no) << universe << sep; } PPL_OUTPUT_TEMPLATE_DEFINITIONS_ASCII_ONLY(ITV, Box::Status) template bool Box::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/Box.inlines.hh line 33. */ namespace Parma_Polyhedra_Library { template inline bool Box::marked_empty() const { return status.test_empty_up_to_date() && status.test_empty(); } template inline void Box::set_empty() { status.set_empty(); status.set_empty_up_to_date(); } template inline void Box::set_nonempty() { status.reset_empty(); status.set_empty_up_to_date(); } template inline void Box::set_empty_up_to_date() { status.set_empty_up_to_date(); } template inline void Box::reset_empty_up_to_date() { return status.reset_empty_up_to_date(); } template inline Box::Box(const Box& y, Complexity_Class) : seq(y.seq), status(y.status) { } template inline Box& Box::operator=(const Box& y) { seq = y.seq; status = y.status; return *this; } template inline void Box::swap(Box& y) { Box& x = *this; std::swap(x.seq, y.seq); std::swap(x.status, y.status); } template inline Box::Box(const Constraint_System& cs, Recycle_Input) { // Recycling is useless: just delegate. Box tmp(cs); this->swap(tmp); } template inline Box::Box(const Generator_System& gs, Recycle_Input) { // Recycling is useless: just delegate. Box tmp(gs); this->swap(tmp); } template inline Box::Box(const Congruence_System& cgs, Recycle_Input) { // Recycling is useless: just delegate. Box tmp(cgs); this->swap(tmp); } template inline memory_size_type Box::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } template inline dimension_type Box::space_dimension() const { return seq.size(); } template inline dimension_type Box::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 inline const ITV& Box::operator[](const dimension_type k) const { PPL_ASSERT(k < seq.size()); return seq[k]; } template inline const ITV& Box::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 inline void Box::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 inline bool Box::is_empty() const { return marked_empty() || check_empty(); } template inline bool Box::bounds_from_above(const Linear_Expression& expr) const { return bounds(expr, true); } template inline bool Box::bounds_from_below(const Linear_Expression& expr) const { return bounds(expr, false); } template inline bool Box::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 inline bool Box::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 inline bool Box::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 inline bool Box::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 inline bool Box::strictly_contains(const Box& y) const { const Box& x = *this; return x.contains(y) && !y.contains(x); } template inline void Box::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_generic("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 inline bool operator!=(const Box& x, const Box& y) { return !(x == y); } template inline bool Box::get_lower_bound(const dimension_type k, bool& closed, Coefficient& n, Coefficient& d) const { 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_TEMP0(mpq_class, lr); assign_r(lr, seq_k.lower(), ROUND_NOT_NEEDED); n = lr.get_num(); d = lr.get_den(); return true; } template inline bool Box::get_upper_bound(const dimension_type k, bool& closed, Coefficient& n, Coefficient& d) const { 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_TEMP0(mpq_class, ur); assign_r(ur, seq_k.upper(), ROUND_NOT_NEEDED); n = ur.get_num(); d = ur.get_den(); return true; } template inline void Box::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 inline void Box::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 inline void Box::add_recycled_constraints(Constraint_System& cs) { add_constraints(cs); } template inline void Box::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 inline void Box::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 inline void Box::add_recycled_congruences(Congruence_System& cgs) { add_congruences(cgs); } template inline bool Box::can_recycle_constraint_systems() { return false; } template inline bool Box::can_recycle_congruence_systems() { return false; } template inline void Box::widening_assign(const Box& y, unsigned* tp) { CC76_widening_assign(y, tp); } template inline Congruence_System Box::minimized_congruences() const { // Only equalities can be congruences and these are already minimized. return congruences(); } template inline I_Result Box::refine_interval_no_check(ITV& itv, const Constraint::Type type, Coefficient_traits::const_reference num, Coefficient_traits::const_reference den) { PPL_ASSERT(den != 0); // The interval constraint is of the form // `var + num / den rel 0', // where `rel' is either the relation `==', `>=', or `>'. // For the purpose of refining the interval, this is // (morally) turned into `var rel -num/den'. PPL_DIRTY_TEMP0(mpq_class, q); assign_r(q.get_num(), num, ROUND_NOT_NEEDED); assign_r(q.get_den(), den, ROUND_NOT_NEEDED); q.canonicalize(); // Turn `num/den' into `-num/den'. q = -q; I_Result res; switch (type) { case Constraint::EQUALITY: res = itv.add_constraint(i_constraint(EQUAL, q)); break; case Constraint::NONSTRICT_INEQUALITY: res = itv.add_constraint(i_constraint(den > 0 ? GREATER_OR_EQUAL : LESS_OR_EQUAL, q)); break; case Constraint::STRICT_INEQUALITY: res = itv.add_constraint(i_constraint(den > 0 ? GREATER_THAN : LESS_THAN, q)); break; default: // Silence an annoying GCC warning (should never reach this point). PPL_ASSERT(false); res = I_ANY; } PPL_ASSERT(itv.OK()); return res; } template inline void Box ::add_interval_constraint_no_check(const dimension_type var_id, const Constraint::Type type, Coefficient_traits::const_reference num, Coefficient_traits::const_reference den) { PPL_ASSERT(!marked_empty()); PPL_ASSERT(var_id < space_dimension()); PPL_ASSERT(den != 0); refine_interval_no_check(seq[var_id], type, num, den); // 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 inline void Box::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 inline void Box::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 inline void Box::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 inline void Box::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 inline void Box::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 inline void Box::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 inline void Box::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 inline bool rectilinear_distance_assign(Checked_Number& r, const Box& x, const Box& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign > (r, x, y, dir, tmp0, tmp1, tmp2); } /*! \relates Box */ template inline bool rectilinear_distance_assign(Checked_Number& r, const Box& x, const Box& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool rectilinear_distance_assign(Checked_Number& r, const Box& x, const Box& 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(r, x, y, dir); } /*! \relates Box */ template inline bool euclidean_distance_assign(Checked_Number& r, const Box& x, const Box& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign > (r, x, y, dir, tmp0, tmp1, tmp2); } /*! \relates Box */ template inline bool euclidean_distance_assign(Checked_Number& r, const Box& x, const Box& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool euclidean_distance_assign(Checked_Number& r, const Box& x, const Box& 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(r, x, y, dir); } /*! \relates Box */ template inline bool l_infinity_distance_assign(Checked_Number& r, const Box& x, const Box& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign > (r, x, y, dir, tmp0, tmp1, tmp2); } /*! \relates Box */ template inline bool l_infinity_distance_assign(Checked_Number& r, const Box& x, const Box& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool l_infinity_distance_assign(Checked_Number& r, const Box& x, const Box& 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(r, x, y, dir); } } // 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/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 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 class DB_Row_Impl_Handler; template 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 class Ptr_Iterator; } // namespace Implementation } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Ptr_Iterator.defs.hh line 28. */ #include namespace Parma_Polyhedra_Library { namespace Implementation { template bool operator==(const Ptr_Iterator& x, const Ptr_Iterator& y); template bool operator!=(const Ptr_Iterator& x, const Ptr_Iterator& y); template bool operator<(const Ptr_Iterator& x, const Ptr_Iterator& y); template bool operator<=(const Ptr_Iterator& x, const Ptr_Iterator& y); template bool operator>(const Ptr_Iterator& x, const Ptr_Iterator& y); template bool operator>=(const Ptr_Iterator& x, const Ptr_Iterator& y); template typename Ptr_Iterator::difference_type operator-(const Ptr_Iterator& x, const Ptr_Iterator& y); template Ptr_Iterator

operator+(typename Ptr_Iterator

::difference_type m, const Ptr_Iterator

& 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 class Parma_Polyhedra_Library::Implementation::Ptr_Iterator : public std::iterator::iterator_category, typename std::iterator_traits

::value_type, typename std::iterator_traits

::difference_type, typename std::iterator_traits

::pointer, typename std::iterator_traits

::reference> { public: typedef typename std::iterator_traits

::difference_type difference_type; typedef typename std::iterator_traits

::reference reference; typedef typename std::iterator_traits

::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 Ptr_Iterator(const Ptr_Iterator& 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 friend bool Parma_Polyhedra_Library::Implementation:: operator==(const Ptr_Iterator& x, const Ptr_Iterator& y); template friend bool Parma_Polyhedra_Library::Implementation:: operator!=(const Ptr_Iterator& x, const Ptr_Iterator& y); template friend bool Parma_Polyhedra_Library::Implementation:: operator<(const Ptr_Iterator& x, const Ptr_Iterator& y); template friend bool Parma_Polyhedra_Library::Implementation:: operator<=(const Ptr_Iterator& x, const Ptr_Iterator& y); template friend bool Parma_Polyhedra_Library::Implementation:: operator>(const Ptr_Iterator& x, const Ptr_Iterator& y); template friend bool Parma_Polyhedra_Library::Implementation:: operator>=(const Ptr_Iterator& x, const Ptr_Iterator& y); template friend typename Ptr_Iterator::difference_type Parma_Polyhedra_Library::Implementation:: operator-(const Ptr_Iterator& x, const Ptr_Iterator& y); friend Ptr_Iterator

Parma_Polyhedra_Library::Implementation:: operator+<>(typename Ptr_Iterator

::difference_type m, const Ptr_Iterator

& y); }; /* Automatically generated from PPL source file ../src/Ptr_Iterator.inlines.hh line 1. */ /* Ptr_Iterator class implementation: inline functions. */ #include /* Automatically generated from PPL source file ../src/Ptr_Iterator.inlines.hh line 29. */ namespace Parma_Polyhedra_Library { namespace Implementation { template inline const P& Ptr_Iterator

::base() const { return p; } template inline Ptr_Iterator

::Ptr_Iterator() : p(P()) { } template inline Ptr_Iterator

::Ptr_Iterator(const P& q) : p(q) { } template template inline Ptr_Iterator

::Ptr_Iterator(const Ptr_Iterator& y) : p(y.base()) { } template inline typename Ptr_Iterator

::reference Ptr_Iterator

::operator*() const { return *p; } template inline typename Ptr_Iterator

::pointer Ptr_Iterator

::operator->() const { return p; } template inline typename Ptr_Iterator

::reference Ptr_Iterator

::operator[](const difference_type m) const { return p[m]; } template inline Ptr_Iterator

& Ptr_Iterator

::operator++() { ++p; return *this; } template inline Ptr_Iterator

Ptr_Iterator

::operator++(int) { return Ptr_Iterator(p++); } template inline Ptr_Iterator

& Ptr_Iterator

::operator--() { --p; return *this; } template inline Ptr_Iterator

Ptr_Iterator

::operator--(int) { return Ptr_Iterator(p--); } template inline Ptr_Iterator

& Ptr_Iterator

::operator+=(const difference_type m) { p += m; return *this; } template inline Ptr_Iterator

& Ptr_Iterator

::operator-=(const difference_type m) { p -= m; return *this; } template inline typename Ptr_Iterator

::difference_type Ptr_Iterator

::operator-(const Ptr_Iterator& y) const { return p - y.p; } template inline Ptr_Iterator

Ptr_Iterator

::operator+(const difference_type m) const { return Ptr_Iterator(p + m); } template inline Ptr_Iterator

Ptr_Iterator

::operator-(const difference_type m) const { return Ptr_Iterator(p - m); } template inline bool operator==(const Ptr_Iterator

& x, const Ptr_Iterator& y) { return x.base() == y.base(); } template inline bool operator!=(const Ptr_Iterator

& x, const Ptr_Iterator& y) { return x.base() != y.base(); } template inline bool operator<(const Ptr_Iterator

& x, const Ptr_Iterator& y) { return x.base() < y.base(); } template inline bool operator<=(const Ptr_Iterator

& x, const Ptr_Iterator& y) { return x.base() <= y.base(); } template inline bool operator>(const Ptr_Iterator

& x, const Ptr_Iterator& y) { return x.base() > y.base(); } template inline bool operator>=(const Ptr_Iterator

& x, const Ptr_Iterator& y) { return x.base() >= y.base(); } template inline typename Ptr_Iterator

::difference_type operator-(const Ptr_Iterator

& x, const Ptr_Iterator& y) { return x.base() - y.base(); } template inline Ptr_Iterator

operator+(typename Ptr_Iterator

::difference_type m, const Ptr_Iterator

& y) { return Ptr_Iterator

(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 #include #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 true, 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 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 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 nan, not a number, 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 T().OK() gives true (see below). \code ~T() \endcode is the destructor. \code bool is_nan() const \endcode returns true if and only \p *this represents the not a number value. \code bool OK() const \endcode returns true if and only if \p *this satisfies all its invariants. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template class Parma_Polyhedra_Library::DB_Row : private DB_Row_Impl_Handler { 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 void construct_upward_approximation(const DB_Row& 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 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 iterator; //! A const random access iterator to access the row's elements. typedef Implementation::Ptr_Iterator 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 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 //! \name Classical comparison operators. //@{ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) /*! \relates DB_Row */ template bool operator==(const DB_Row& x, const DB_Row& y); /*! \relates DB_Row */ template bool operator!=(const DB_Row& x, const DB_Row& 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 class Parma_Polyhedra_Library::DB_Row_Impl_Handler::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 shrink() 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 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; //! The number of coefficients in the row. dimension_type size_; //! The vector of coefficients. T vec_[ #if !PPL_CXX_SUPPORTS_FLEXIBLE_ARRAYS 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); }; namespace std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::DB_Row */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template void swap(Parma_Polyhedra_Library::DB_Row& x, Parma_Polyhedra_Library::DB_Row& y); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::iter_swap. /*! \relates Parma_Polyhedra_Library::DB_Row */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template void iter_swap(typename std::vector > ::iterator x, typename std::vector > ::iterator y); } // namespace std /* 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 #include #include #include namespace Parma_Polyhedra_Library { template inline void* DB_Row_Impl_Handler::Impl::operator new(const size_t fixed_size, const dimension_type capacity) { #if PPL_CXX_SUPPORTS_FLEXIBLE_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 inline void DB_Row_Impl_Handler::Impl::operator delete(void* p) { ::operator delete(p); } template inline void DB_Row_Impl_Handler::Impl::operator delete(void* p, dimension_type) { ::operator delete(p); } template inline memory_size_type DB_Row_Impl_Handler::Impl ::total_memory_in_bytes(dimension_type capacity) const { return sizeof(*this) + capacity*sizeof(T) #if !PPL_CXX_SUPPORTS_FLEXIBLE_ARRAYS - 1*sizeof(T) #endif + external_memory_in_bytes(); } template inline memory_size_type DB_Row_Impl_Handler::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 inline dimension_type DB_Row_Impl_Handler::Impl::max_size() { return std::numeric_limits::max() / sizeof(T); } template inline dimension_type DB_Row_Impl_Handler::Impl::size() const { return size_; } template inline void DB_Row_Impl_Handler::Impl::set_size(const dimension_type new_sz) { size_ = new_sz; } template inline void DB_Row_Impl_Handler::Impl::bump_size() { ++size_; } template inline DB_Row_Impl_Handler::Impl::Impl() : size_(0) { } template inline DB_Row_Impl_Handler::Impl::~Impl() { shrink(0); } template inline DB_Row_Impl_Handler::DB_Row_Impl_Handler() : impl(0) { #if PPL_DB_ROW_EXTRA_DEBUG capacity_ = 0; #endif } template inline DB_Row_Impl_Handler::~DB_Row_Impl_Handler() { delete impl; } template inline T& DB_Row_Impl_Handler::Impl::operator[](const dimension_type k) { PPL_ASSERT(k < size()); return vec_[k]; } template inline const T& DB_Row_Impl_Handler::Impl::operator[](const dimension_type k) const { PPL_ASSERT(k < size()); return vec_[k]; } template inline dimension_type DB_Row::max_size() { return DB_Row_Impl_Handler::Impl::max_size(); } template inline dimension_type DB_Row::size() const { return this->impl->size(); } #if PPL_DB_ROW_EXTRA_DEBUG template inline dimension_type DB_Row::capacity() const { return this->capacity_; } #endif // PPL_DB_ROW_EXTRA_DEBUG template inline DB_Row::DB_Row() : DB_Row_Impl_Handler() { } template inline void DB_Row::allocate( #if PPL_CXX_SUPPORTS_FLEXIBLE_ARRAYS const #endif dimension_type capacity) { DB_Row& x = *this; PPL_ASSERT(capacity <= max_size()); #if !PPL_CXX_SUPPORTS_FLEXIBLE_ARRAYS if (capacity == 0) ++capacity; #endif PPL_ASSERT(x.impl == 0); x.impl = new (capacity) typename DB_Row_Impl_Handler::Impl(); #if PPL_DB_ROW_EXTRA_DEBUG PPL_ASSERT(x.capacity_ == 0); x.capacity_ = capacity; #endif } template inline void DB_Row::expand_within_capacity(const dimension_type new_size) { DB_Row& 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 inline void DB_Row::copy_construct_coefficients(const DB_Row& y) { DB_Row& 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 template inline void DB_Row::construct_upward_approximation(const DB_Row& y, const dimension_type capacity) { DB_Row& x = *this; PPL_ASSERT(y.size() <= capacity && capacity <= max_size()); allocate(capacity); PPL_ASSERT(y.impl); x.impl->construct_upward_approximation(*(y.impl)); } template inline void DB_Row::construct(const dimension_type sz, const dimension_type capacity) { PPL_ASSERT(sz <= capacity && capacity <= max_size()); allocate(capacity); expand_within_capacity(sz); } template inline void DB_Row::construct(const dimension_type sz) { construct(sz, sz); } template inline DB_Row::DB_Row(const dimension_type sz, const dimension_type capacity) : DB_Row_Impl_Handler() { construct(sz, capacity); } template inline DB_Row::DB_Row(const dimension_type sz) { construct(sz); } template inline DB_Row::DB_Row(const DB_Row& y) : DB_Row_Impl_Handler() { if (y.impl) { allocate(compute_capacity(y.size(), max_size())); copy_construct_coefficients(y); } } template inline DB_Row::DB_Row(const DB_Row& y, const dimension_type capacity) : DB_Row_Impl_Handler() { PPL_ASSERT(y.impl); PPL_ASSERT(y.size() <= capacity && capacity <= max_size()); allocate(capacity); copy_construct_coefficients(y); } template inline DB_Row::DB_Row(const DB_Row& y, const dimension_type sz, const dimension_type capacity) : DB_Row_Impl_Handler() { 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 inline DB_Row::~DB_Row() { } template inline void DB_Row::shrink(const dimension_type new_size) { DB_Row& x = *this; PPL_ASSERT(x.impl); x.impl->shrink(new_size); } template inline void DB_Row::swap(DB_Row& y) { DB_Row& x = *this; std::swap(x.impl, y.impl); #if PPL_DB_ROW_EXTRA_DEBUG std::swap(x.capacity_, y.capacity_); #endif } template inline void DB_Row::assign(DB_Row& y) { DB_Row& x = *this; x.impl = y.impl; #if PPL_DB_ROW_EXTRA_DEBUG x.capacity_ = y.capacity_; #endif } template inline DB_Row& DB_Row::operator=(const DB_Row& y) { // Copy-construct `tmp' from `y'. DB_Row tmp(y); // Swap the implementation of `*this' with the one of `tmp'. swap(tmp); // Now `tmp' goes out of scope, so the old `*this' will be destroyed. return *this; } template inline T& DB_Row::operator[](const dimension_type k) { DB_Row& x = *this; return (*x.impl)[k]; } template inline const T& DB_Row::operator[](const dimension_type k) const { const DB_Row& x = *this; return (*x.impl)[k]; } template inline typename DB_Row::iterator DB_Row::begin() { DB_Row& x = *this; return iterator(x.impl->vec_); } template inline typename DB_Row::iterator DB_Row::end() { DB_Row& x = *this; return iterator(x.impl->vec_ + x.impl->size_); } template inline typename DB_Row::const_iterator DB_Row::begin() const { const DB_Row& x = *this; return const_iterator(x.impl->vec_); } template inline typename DB_Row::const_iterator DB_Row::end() const { const DB_Row& x = *this; return const_iterator(x.impl->vec_ + x.impl->size_); } template inline memory_size_type DB_Row::external_memory_in_bytes(dimension_type capacity) const { const DB_Row& x = *this; return x.impl->total_memory_in_bytes(capacity); } template inline memory_size_type DB_Row::total_memory_in_bytes(dimension_type capacity) const { return sizeof(*this) + external_memory_in_bytes(capacity); } template inline memory_size_type DB_Row::external_memory_in_bytes() const { const DB_Row& 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 inline memory_size_type DB_Row::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } /*! \relates DB_Row */ template inline bool operator!=(const DB_Row& x, const DB_Row& y) { return !(x == y); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::DB_Row */ template inline void swap(Parma_Polyhedra_Library::DB_Row& x, Parma_Polyhedra_Library::DB_Row& y) { x.swap(y); } /*! \relates Parma_Polyhedra_Library::DB_Row */ template inline void iter_swap(typename std::vector > ::iterator x, typename std::vector > ::iterator y) { swap(*x, *y); } } // namespace std /* 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 template void DB_Row_Impl_Handler::Impl::construct_upward_approximation(const U& y) { const dimension_type y_size = y.size(); #if PPL_CXX_SUPPORTS_FLEXIBLE_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_FLEXIBLE_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_FLEXIBLE_ARRAYS } template void DB_Row_Impl_Handler:: Impl::expand_within_capacity(const dimension_type new_size) { PPL_ASSERT(size() <= new_size && new_size <= max_size()); #if !PPL_CXX_SUPPORTS_FLEXIBLE_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 void DB_Row_Impl_Handler::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_FLEXIBLE_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 void DB_Row_Impl_Handler::Impl::copy_construct_coefficients(const Impl& y) { const dimension_type y_size = y.size(); #if PPL_CXX_SUPPORTS_FLEXIBLE_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_FLEXIBLE_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_FLEXIBLE_ARRAYS } template memory_size_type DB_Row_Impl_Handler::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 bool DB_Row::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& x = *this; bool is_broken = false; #if PPL_DB_ROW_EXTRA_DEBUG # if !PPL_CXX_SUPPORTS_FLEXIBLE_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_FLEXIBLE_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 bool operator==(const DB_Row& x, const DB_Row& 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 474. */ /* Automatically generated from PPL source file ../src/DB_Matrix.defs.hh line 32. */ #include #include #include 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 std::ostream& operator<<(std::ostream& s, const DB_Matrix& 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 allows for the representation of a square matrix of T objects. Each DB_Matrix object can be viewed as a multiset of DB_Row. */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template 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 explicit DB_Matrix(const DB_Matrix& 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 >::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::value_type value_type; typedef typename std::iterator_traits::difference_type difference_type; typedef typename std::iterator_traits::pointer pointer; typedef typename std::iterator_traits::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 true if and only if \p *this and \p y are identical. */ bool operator==(const const_iterator& y) const; /*! \brief Returns true 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 friend class DB_Matrix; //! The rows of the matrix. std::vector > rows; //! Size of the initialized part of each row. dimension_type row_size; /*! \brief Capacity allocated for each row, i.e., number of long objects that each row can contain. */ dimension_type row_capacity; public: //! Swaps \p *this with \p y. void 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& operator[](dimension_type k); //! Returns a constant reference to the \p k-th row of the matrix. const DB_Row& 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 true if successful, false 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 std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::DB_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template void swap(Parma_Polyhedra_Library::DB_Matrix& x, Parma_Polyhedra_Library::DB_Matrix& y); } // namespace std namespace Parma_Polyhedra_Library { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true if and only if \p x and \p y are identical. /*! \relates DB_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template bool operator==(const DB_Matrix& x, const DB_Matrix& y); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true if and only if \p x and \p y are different. /*! \relates DB_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template bool operator!=(const DB_Matrix& x, const DB_Matrix& 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 true; returns false 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 bool rectilinear_distance_assign(Checked_Number& r, const DB_Matrix& x, const DB_Matrix& 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 true; returns false 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 bool euclidean_distance_assign(Checked_Number& r, const DB_Matrix& x, const DB_Matrix& 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 true; returns false 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 bool l_infinity_distance_assign(Checked_Number& r, const DB_Matrix& x, const DB_Matrix& 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 namespace Parma_Polyhedra_Library { template inline void DB_Matrix::swap(DB_Matrix& y) { std::swap(rows, y.rows); std::swap(row_size, y.row_size); std::swap(row_capacity, y.row_capacity); } template inline dimension_type DB_Matrix::max_num_rows() { return std::vector >().max_size(); } template inline dimension_type DB_Matrix::max_num_columns() { return DB_Row::max_size(); } template inline memory_size_type DB_Matrix::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } template inline DB_Matrix::const_iterator::const_iterator() : i(Iter()) { } template inline DB_Matrix::const_iterator::const_iterator(const Iter& b) : i(b) { } template inline DB_Matrix::const_iterator::const_iterator(const const_iterator& y) : i(y.i) { } template inline typename DB_Matrix::const_iterator& DB_Matrix::const_iterator::operator=(const const_iterator& y) { i = y.i; return *this; } template inline typename DB_Matrix::const_iterator::reference DB_Matrix::const_iterator::operator*() const { return *i; } template inline typename DB_Matrix::const_iterator::pointer DB_Matrix::const_iterator::operator->() const { return &*i; } template inline typename DB_Matrix::const_iterator& DB_Matrix::const_iterator::operator++() { ++i; return *this; } template inline typename DB_Matrix::const_iterator DB_Matrix::const_iterator::operator++(int) { return const_iterator(i++); } template inline bool DB_Matrix::const_iterator::operator==(const const_iterator& y) const { return i == y.i; } template inline bool DB_Matrix::const_iterator::operator!=(const const_iterator& y) const { return !operator==(y); } template inline typename DB_Matrix::const_iterator DB_Matrix::begin() const { return const_iterator(rows.begin()); } template inline typename DB_Matrix::const_iterator DB_Matrix::end() const { return const_iterator(rows.end()); } template inline DB_Matrix::DB_Matrix() : rows(), row_size(0), row_capacity(0) { } template inline DB_Matrix::~DB_Matrix() { } template inline DB_Row& DB_Matrix::operator[](const dimension_type k) { PPL_ASSERT(k < rows.size()); return rows[k]; } template inline const DB_Row& DB_Matrix::operator[](const dimension_type k) const { PPL_ASSERT(k < rows.size()); return rows[k]; } template inline dimension_type DB_Matrix::num_rows() const { return rows.size(); } #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \relates DB_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template inline bool operator!=(const DB_Matrix& x, const DB_Matrix& y) { return !(x == y); } template inline DB_Matrix::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 inline DB_Matrix& DB_Matrix::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 inline bool l_m_distance_assign(Checked_Number& r, const DB_Matrix& x, const DB_Matrix& 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& x_i = x[i]; const DB_Row& 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 inline bool rectilinear_distance_assign(Checked_Number& r, const DB_Matrix& x, const DB_Matrix& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign >(r, x, y, dir, tmp0, tmp1, tmp2); } #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \relates DB_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template inline bool euclidean_distance_assign(Checked_Number& r, const DB_Matrix& x, const DB_Matrix& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign >(r, x, y, dir, tmp0, tmp1, tmp2); } #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \relates DB_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template inline bool l_infinity_distance_assign(Checked_Number& r, const DB_Matrix& x, const DB_Matrix& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign >(r, x, y, dir, tmp0, tmp1, tmp2); } } // namespace Parma_Polyhedra_Library namespace std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \relates Parma_Polyhedra_Library::DB_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template inline void swap(Parma_Polyhedra_Library::DB_Matrix& x, Parma_Polyhedra_Library::DB_Matrix& y) { x.swap(y); } } // namespace std /* 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 DB_Matrix::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 template DB_Matrix::DB_Matrix(const DB_Matrix& 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 void DB_Matrix::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 > new_rows; new_rows.reserve(compute_capacity(new_n_rows, max_num_rows())); new_rows.insert(new_rows.end(), new_n_rows, DB_Row()); // 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) new_rows[i].swap(rows[i]); // Put the new vector into place. std::swap(rows, new_rows); } else { // Reallocation will NOT take place. rows.insert(rows.end(), new_n_rows - old_n_rows, DB_Row()); 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()); // 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 new_row(rows[i], new_matrix.row_size, new_matrix.row_capacity); std::swap(new_matrix.rows[i], new_row); } // Put the new vector into place. 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 new_row(rows[i], new_n_rows, new_row_capacity); std::swap(rows[i], new_row); } row_capacity = new_row_capacity; } // Rows have grown or shrunk. row_size = new_n_rows; } } template void DB_Matrix::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 > new_rows; new_rows.reserve(compute_capacity(new_n_rows, max_num_rows())); new_rows.insert(new_rows.end(), new_n_rows, DB_Row()); // 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) new_rows[i].swap(rows[i]); // Put the new vector into place. std::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()); // 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); swap(new_matrix); return; } } else if (new_n_rows < old_n_rows) { // Drop some rows. rows.erase(rows.begin() + new_n_rows, rows.end()); // 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 new_row(new_n_rows, new_row_capacity); std::swap(rows[i], new_row); } row_capacity = new_row_capacity; } } // DB_Rows have grown or shrunk. row_size = new_n_rows; } template void DB_Matrix::ascii_dump(std::ostream& s) const { const DB_Matrix& 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) template bool DB_Matrix::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 bool operator==(const DB_Matrix& x, const DB_Matrix& 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 memory_size_type DB_Matrix::external_memory_in_bytes() const { memory_size_type n = rows.capacity() * sizeof(DB_Row); for (dimension_type i = num_rows(); i-- > 0; ) n += rows[i].external_memory_in_bytes(row_capacity); return n; } template bool DB_Matrix::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 std::ostream& IO_Operators::operator<<(std::ostream& s, const DB_Matrix& 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 331. */ /* 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 /*! \brief \ingroup PPL_CXX_interface 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); // Do not uncomment the following. // The compile time error on conversions is the expected behavior. // const_bool_nodef(convertible, false); //! 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); // Do not uncomment the following. // The compile time error is the expected behavior. // static const Rounding_Dir ROUND_DEFAULT_CONSTRUCTOR = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_OPERATOR = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_FUNCTION = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_INPUT = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_OUTPUT = ROUND_UP; /*! \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 /*! \brief \ingroup PPL_CXX_interface 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); // Do not uncomment the following. // The compile time error on conversions is the expected behavior. // const_bool_nodef(convertible, false); //! 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); // Do not uncomment the following. // The compile time error is the expected behavior. // static const Rounding_Dir ROUND_DEFAULT_CONSTRUCTOR = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_OPERATOR = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_FUNCTION = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_INPUT = ROUND_UP; // static const Rounding_Dir ROUND_DEFAULT_OUTPUT = ROUND_UP; /*! \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 158. */ /* Automatically generated from PPL source file ../src/BD_Shape.defs.hh line 49. */ #include #include #include 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: false is written if \p bds is an empty polyhedron; true is written if \p bds is the universe polyhedron; a system of constraints defining \p bds is written otherwise, all constraints separated by ", ". */ template std::ostream& operator<<(std::ostream& s, const BD_Shape& bds); } // namespace IO_Operators //! Returns true 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 false is returned. */ template bool operator==(const BD_Shape& x, const BD_Shape& y); //! Returns true if and only if \p x and \p y aren't the same BDS. /*! \relates BD_Shape Note that \p x and \p y may be dimension-incompatible shapes: in this case, the value true is returned. */ template bool operator!=(const BD_Shape& x, const BD_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool rectilinear_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool rectilinear_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& 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 true; returns false 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 bool rectilinear_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool euclidean_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool euclidean_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& 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 true; returns false 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 bool euclidean_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool l_infinity_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool l_infinity_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& 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 true; returns false 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 bool l_infinity_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& y, Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Decodes the constraint \p c as a bounded difference. /*! \relates BD_Shape \return true if the constraint \p c is a \ref Bounded_Difference_Shapes "bounded difference"; false otherwise. \param c The constraint to be decoded. \param c_space_dim The space dimension of the constraint \p c (it is assumed to match the actual space dimension of \p c). \param c_num_vars If true 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 true 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 true 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 true 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) bool extract_bounded_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); #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& predecessor, std::vector& indices); } // namespace Parma_Polyhedra_Library //! A bounded difference shape. /*! \ingroup PPL_CXX_interface The class template BD_Shape allows for the efficient representation of a restricted kind of topologically closed convex polyhedra called bounded difference shapes (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 rounding-aware 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 optimally supports: - 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 x, y and 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 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 bd(cs); \endcode Since only those constraints having the syntactic form of a bounded difference 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 bd(cs); \endcode */ template 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 N; #else typedef Checked_Number 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 explicit BD_Shape(const BD_Shape& 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 explicit BD_Shape(const Box& 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 explicit BD_Shape(const Octagonal_Shape& 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 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 true 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 true 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 true 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 true 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, false 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 true 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 true 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, false 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 true 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 true 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, false 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 true 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 true 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, false 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 true if and only if there exist a unique value \p val such that \p *this saturates the equality expr = val. \param expr The linear expression for which the frequency is needed; \param freq_n If true 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 true 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 false 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 true 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 true 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 true 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 true if and only if \p *this is an empty BDS. bool is_empty() const; //! Returns true if and only if \p *this is a universe BDS. bool is_universe() const; //! Returns true if and only if \p *this is discrete. bool is_discrete() const; /*! \brief Returns true if and only if \p *this is a topologically closed subset of the vector space. */ bool is_topologically_closed() const; //! Returns true if and only if \p *this is a bounded BDS. bool is_bounded() const; /*! \brief Returns true if and only if \p *this contains at least one integer point. */ bool contains_integer_point() const; /*! \brief Returns true 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 true 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 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 true is returned, otherwise false 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 true is returned; otherwise false 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()); /*! \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 pcs Possibly null pointer to a constraint system whose variables are contained in \p vars. If *pcs 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 *pcs. \param complexity_threshold A precision parameter of the \ref Wrapping_Operator "wrapping operator": higher values result in possibly improved precision. \param wrap_individually true 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 *pcs is dimension-incompatible with \p vars, or if \p *this is dimension-incompatible \p vars or with *pcs. */ 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* pcs = 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 ANY_COMPLEXITY. */ 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 ANY_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 CC76_extrapolation "CC76-extrapolation" between \p *this and \p y. \param y A BDS that must 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 must 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 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 must 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 must 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 must 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 decreasing iteration sequence). Hence, the call x.CC76_narrowing_assign(y) 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 must 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 must 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 must 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 max_space_dimension(). */ 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 true if and only if the represented partial function has an empty co-domain (i.e., it is always undefined). The has_empty_codomain() method will always be called before the methods below. However, if has_empty_codomain() returns true, 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 true is returned. If \f$f\f$ is undefined in \f$k\f$, then false 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 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 max_space_dimension(). If \p *this has space dimension \f$n\f$, with \f$n > 0\f$, and var 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$, dest 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 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 true if successful, false 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 x == y, then x.hash_code() == y.hash_code(). */ int32_t hash_code() const; friend bool operator==(const BD_Shape& x, const BD_Shape& y); template friend bool Parma_Polyhedra_Library::rectilinear_distance_assign (Checked_Number& r, const BD_Shape& x, const BD_Shape& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2); template friend bool Parma_Polyhedra_Library::euclidean_distance_assign (Checked_Number& r, const BD_Shape& x, const BD_Shape& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2); template friend bool Parma_Polyhedra_Library::l_infinity_distance_assign (Checked_Number& r, const BD_Shape& x, const BD_Shape& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2); private: template friend class Parma_Polyhedra_Library::BD_Shape; template friend class Parma_Polyhedra_Library::Box; //! The matrix representing the system of bounded differences. DB_Matrix dbm; #define PPL_IN_BD_Shape_CLASS /* Automatically generated from PPL source file ../src/BDS_Status.idefs.hh line 1. */ /* BD_Shape::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 object. /*! \ingroup PPL_CXX_interface The assertions supported are: - zero-dim universe: the BDS is the zero-dimensional vector space \f$\Rset^0 = \{\cdot\}\f$; - empty: the BDS is the empty set; - shortest-path closed: the BDS is represented by a shortest-path closed system of bounded differences, so that all the constraints are as tight as possible; - shortest-path reduced: 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: - zero-dim universe excludes any other assertion; - empty: excludes any other assertion; - shortest-path reduced implies shortest-path closed. */ class Status { public: //! By default Status is the zero-dim universe 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 true if successful, false 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 all bits in \p mask are set. bool test_all(flags_t mask) const; //! Check whether at least one 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 1808. */ #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 true if the BDS is the zero-dimensional universe. bool marked_zero_dim_univ() const; /*! \brief Returns true if the BDS is known to be empty. The return value false does not necessarily implies that \p *this is non-empty. */ bool marked_empty() const; /*! \brief Returns true if the system of bounded differences is known to be shortest-path closed. The return value false does not necessarily implies that this->dbm is not shortest-path closed. */ bool marked_shortest_path_closed() const; /*! \brief Returns true if the system of bounded differences is known to be shortest-path reduced. The return value false does not necessarily implies that this->dbm 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 this->dbm its shortest-path closure. void shortest_path_closure_assign() const; /*! \brief Assigns to this->dbm its shortest-path closure and records into this->redundancy_dbm which of the entries in this->dbm are redundant. */ void shortest_path_reduction_assign() const; /*! \brief Returns true if and only if this->dbm is shortest-path closed and this->redundancy_dbm correctly flags the redundant entries in this->dbm. */ 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 true if and only if \p from_above is true and \p expr is bounded from above in \p *this, or \p from_above is false and \p expr is bounded from below in \p *this. \param expr The linear expression to test; \param from_above true 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 true 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 true 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, false 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 true 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 true 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, false 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 Convexity Recognition of the Union of Polyhedra 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 datatype. */ template 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 dbm[i][j] \<= k. void add_dbm_constraint(dimension_type i, dimension_type j, const N& k); //! Adds the constraint dbm[i][j] \<= num/den. void add_dbm_constraint(dimension_type i, dimension_type j, Coefficient_traits::const_reference num, Coefficient_traits::const_reference den); /*! \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 v - u \<= c, starting from \p ub_v which is an upper bound for \p v. The shortest-path closure is able to deduce the constraint v - u \<= ub_v - lb_u. 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 q == sc_expr[u]/sc_den 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). */ void deduce_v_minus_u_bounds(dimension_type v, dimension_type last_v, const Linear_Expression& sc_expr, Coefficient_traits::const_reference sc_den, const N& ub_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 u - v \<= c, starting from \p minus_lb_v which is a lower bound for \p v. The shortest-path closure is able to deduce the constraint u - v \<= ub_u - lb_v. 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 q == sc_expr[u]/sc_den 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. */ void deduce_u_minus_v_bounds(dimension_type v, dimension_type last_v, const Linear_Expression& sc_expr, Coefficient_traits::const_reference sc_den, 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& 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& 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& c); //! \name Exception Throwers //@{ void throw_dimension_incompatible(const char* method, const BD_Shape& x) 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* name_row, const Linear_Expression& y) const; static void throw_expression_too_complex(const char* method, const Linear_Expression& e); static void throw_generic(const char* method, const char* reason); //@} // Exception Throwers }; namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::BD_Shape */ template void swap(Parma_Polyhedra_Library::BD_Shape& x, Parma_Polyhedra_Library::BD_Shape& y); } // namespace std /* Automatically generated from PPL source file ../src/BDS_Status.inlines.hh line 1. */ /* BD_Shape::Status class implementation: inline functions. */ namespace Parma_Polyhedra_Library { template inline BD_Shape::Status::Status(flags_t mask) : flags(mask) { } template inline BD_Shape::Status::Status() : flags(ZERO_DIM_UNIV) { } template inline bool BD_Shape::Status::test_all(flags_t mask) const { return (flags & mask) == mask; } template inline bool BD_Shape::Status::test_any(flags_t mask) const { return flags & mask; } template inline void BD_Shape::Status::set(flags_t mask) { flags |= mask; } template inline void BD_Shape::Status::reset(flags_t mask) { flags &= ~mask; } template inline bool BD_Shape::Status::test_zero_dim_univ() const { return flags == ZERO_DIM_UNIV; } template inline void BD_Shape::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 inline void BD_Shape::Status::set_zero_dim_univ() { // Zero-dim universe is incompatible with anything else. flags = ZERO_DIM_UNIV; } template inline bool BD_Shape::Status::test_empty() const { return test_any(EMPTY); } template inline void BD_Shape::Status::reset_empty() { reset(EMPTY); } template inline void BD_Shape::Status::set_empty() { flags = EMPTY; } template inline bool BD_Shape::Status::test_shortest_path_closed() const { return test_any(SHORTEST_PATH_CLOSED); } template inline void BD_Shape::Status::reset_shortest_path_closed() { // A system is reduced only if it is also closed. reset(SHORTEST_PATH_CLOSED | SHORTEST_PATH_REDUCED); } template inline void BD_Shape::Status::set_shortest_path_closed() { set(SHORTEST_PATH_CLOSED); } template inline bool BD_Shape::Status::test_shortest_path_reduced() const { return test_any(SHORTEST_PATH_REDUCED); } template inline void BD_Shape::Status::reset_shortest_path_reduced() { reset(SHORTEST_PATH_REDUCED); } template inline void BD_Shape::Status::set_shortest_path_reduced() { PPL_ASSERT(test_shortest_path_closed()); set(SHORTEST_PATH_REDUCED); } template bool BD_Shape::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. const std::string zero_dim_univ = "ZE"; const std::string empty = "EM"; const std::string sp_closed = "SPC"; const std::string sp_reduced = "SPR"; const char yes = '+'; const char no = '-'; const char sep = ' '; /*! \relates Parma_Polyhedra_Library::BD_Shape::Status Reads a keyword and its associated on/off flag from \p s. Returns true if the operation is successful, returns false otherwise. When successful, \p positive is set to true if the flag is on; it is set to false otherwise. */ inline bool get_field(std::istream& s, const std::string& 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 void BD_Shape::Status::ascii_dump(std::ostream& s) const { using namespace Implementation::BD_Shapes; s << (test_zero_dim_univ() ? yes : no) << zero_dim_univ << sep << (test_empty() ? yes : no) << empty << sep << sep << (test_shortest_path_closed() ? yes : no) << sp_closed << sep << (test_shortest_path_reduced() ? yes : no) << sp_reduced << sep; } PPL_OUTPUT_TEMPLATE_DEFINITIONS_ASCII_ONLY(T, BD_Shape::Status) template bool BD_Shape::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/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 class OR_Matrix; } /* Automatically generated from PPL source file ../src/OR_Matrix.defs.hh line 32. */ #include #include #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 true, 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 true if and only if \p x and \p y are identical. /*! \relates OR_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template bool operator==(const OR_Matrix& x, const OR_Matrix& 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 std::ostream& operator<<(std::ostream& s, const OR_Matrix& 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:

         _ _
   0    |_|_|
   1    |_|_|_ _
   2    |_|_|_|_|
   3    |_|_|_|_|_ _
   4    |_|_|_|_|_|_|
   5    |_|_|_|_|_|_|
         . . .
         _ _ _ _ _ _       _
 2n-2   |_|_|_|_|_|_| ... |_|
 2n-1   |_|_|_|_|_|_| ... |_|
         0 1 2 3 4 5  ... 2n-1

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 class Parma_Polyhedra_Library::OR_Matrix { private: /*! \brief An object that behaves like a matrix's row with respect to the subscript operators. */ template 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 Pseudo_Row(const Pseudo_Row& 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 template friend class OR_Matrix::Pseudo_Row; template template friend class OR_Matrix::any_row_iterator; #else template friend class Pseudo_Row; template 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 row_reference_type; //! A const reference to a matrix's row. typedef Pseudo_Row const_row_reference_type; private: /*! \brief A template class to derive both OR_Matrix::iterator and OR_Matrix::const_iterator. */ template class any_row_iterator { public: typedef std::random_access_iterator_tag iterator_category; typedef Pseudo_Row value_type; typedef long difference_type; typedef const Pseudo_Row* pointer; typedef const Pseudo_Row& 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 any_row_iterator(const any_row_iterator& y); /*! \brief Assignment operator allowing the assignment of a non-const iterator to a const_iterator. */ template any_row_iterator& operator=(const any_row_iterator& 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-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 difference of \p *this and \p m. any_row_iterator operator-(difference_type m) const; //! Returns true if and only if \p *this is equal to \p y. bool operator==(const any_row_iterator& y) const; /*! \brief Returns true if and only if \p *this is different from \p y. */ bool operator!=(const any_row_iterator& y) const; //! Returns true if and only if \p *this is less than \p y. bool operator<(const any_row_iterator& y) const; /*! \brief Returns true if and only if \p *this is less than or equal to \p y. */ bool operator<=(const any_row_iterator& y) const; //! Returns true if and only if \p *this is greater than \p y. bool operator>(const any_row_iterator& y) const; /*! \brief Returns true 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 value; //! External index. dimension_type e; //! Internal index: i = (e+1)*(e+1)/2. 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 template friend class OR_Matrix::any_row_iterator; #else template friend class any_row_iterator; #endif }; // class any_row_iterator public: //! A (non const) row iterator. typedef any_row_iterator row_iterator; //! A const row iterator. typedef any_row_iterator const_row_iterator; //! A (non const) element iterator. typedef typename DB_Row::iterator element_iterator; //! A const element iterator. typedef typename DB_Row::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 space_dim The space dimension of the matrix that will be created. This constructor creates a matrix with \p 2*space_dim rows. Each element is initialized to plus infinity. */ OR_Matrix(dimension_type space_dim); //! Copy constructor. OR_Matrix(const OR_Matrix& y); //! Constructs a conservative approximation of \p y. template explicit OR_Matrix(const OR_Matrix& y); //! Destructor. ~OR_Matrix(); //! Assignment operator. OR_Matrix& operator=(const OR_Matrix& y); private: template 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 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 vec 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 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 true if successful, false 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==(const OR_Matrix& x, const OR_Matrix& y); //! Checks if all the invariants are satisfied. bool OK() const; }; namespace std { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::OR_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template void swap(Parma_Polyhedra_Library::OR_Matrix& x, Parma_Polyhedra_Library::OR_Matrix& y); } // namespace std namespace Parma_Polyhedra_Library { #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Returns true if and only if \p x and \p y are different. /*! \relates OR_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template bool operator!=(const OR_Matrix& x, const OR_Matrix& 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 true; returns false 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 bool rectilinear_distance_assign(Checked_Number& r, const OR_Matrix& x, const OR_Matrix& 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 true; returns false 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 bool euclidean_distance_assign(Checked_Number& r, const OR_Matrix& x, const OR_Matrix& 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 true; returns false 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 bool l_infinity_distance_assign(Checked_Number& r, const OR_Matrix& x, const OR_Matrix& 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 /* Automatically generated from PPL source file ../src/OR_Matrix.inlines.hh line 35. */ namespace Parma_Polyhedra_Library { template inline dimension_type OR_Matrix::row_first_element_index(const dimension_type k) { return ((k + 1)*(k + 1))/2; } template inline dimension_type OR_Matrix::row_size(const dimension_type k) { return k + 2 - k%2; } #if PPL_OR_MATRIX_EXTRA_DEBUG template template inline dimension_type OR_Matrix::Pseudo_Row::size() const { return size_; } #endif // PPL_OR_MATRIX_EXTRA_DEBUG template template inline OR_Matrix::Pseudo_Row::Pseudo_Row() : first(0) #if PPL_OR_MATRIX_EXTRA_DEBUG , size_(0) #endif { } template template inline OR_Matrix::Pseudo_Row::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 template template inline OR_Matrix::Pseudo_Row::Pseudo_Row(const Pseudo_Row& y) : first(y.first) #if PPL_OR_MATRIX_EXTRA_DEBUG , size_(y.size_) #endif { } template template inline OR_Matrix::Pseudo_Row& OR_Matrix::Pseudo_Row::operator=(const Pseudo_Row& y) { first = y.first; #if PPL_OR_MATRIX_EXTRA_DEBUG size_ = y.size_; #endif return *this; } template template inline OR_Matrix::Pseudo_Row::~Pseudo_Row() { } template template inline U& OR_Matrix::Pseudo_Row::operator[](const dimension_type k) const { #if PPL_OR_MATRIX_EXTRA_DEBUG PPL_ASSERT(k < size_); #endif return *(first + k); } template template inline OR_Matrix::any_row_iterator ::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 template inline OR_Matrix::any_row_iterator::any_row_iterator(U& base) : value(base #if PPL_OR_MATRIX_EXTRA_DEBUG , OR_Matrix::row_size(0) #endif ), e(0), i(0) { } template template template inline OR_Matrix::any_row_iterator ::any_row_iterator(const any_row_iterator& y) : value(y.value), e(y.e), i(y.i) { } template template template inline typename OR_Matrix::template any_row_iterator& OR_Matrix::any_row_iterator::operator=(const any_row_iterator& y) { value = y.value; e = y.e; i = y.i; return *this; } template template inline typename OR_Matrix::template any_row_iterator::reference OR_Matrix::any_row_iterator::operator*() const { return value; } template template inline typename OR_Matrix::template any_row_iterator::pointer OR_Matrix::any_row_iterator::operator->() const { return &value; } template template inline typename OR_Matrix::template any_row_iterator& OR_Matrix::any_row_iterator::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 template inline typename OR_Matrix::template any_row_iterator OR_Matrix::any_row_iterator::operator++(int) { any_row_iterator old = *this; ++(*this); return old; } template template inline typename OR_Matrix::template any_row_iterator& OR_Matrix::any_row_iterator::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 template inline typename OR_Matrix::template any_row_iterator OR_Matrix::any_row_iterator::operator--(int) { any_row_iterator old = *this; --(*this); return old; } template template inline typename OR_Matrix::template any_row_iterator& OR_Matrix::any_row_iterator::operator+=(const difference_type m) { difference_type increment = m + m*m/2 + m*e; if (e % 2 == 0 && m % 2 != 0) ++increment; e += m; i += increment; value.first += increment; #if PPL_OR_MATRIX_EXTRA_DEBUG value.size_ += (m - m%2); #endif return *this; } template template inline typename OR_Matrix::template any_row_iterator& OR_Matrix::any_row_iterator::operator-=(difference_type m) { return *this += -m; } template template inline typename OR_Matrix::template any_row_iterator::difference_type OR_Matrix::any_row_iterator::operator-(const any_row_iterator& y) const { return e - y.e; } template template inline typename OR_Matrix::template any_row_iterator OR_Matrix::any_row_iterator::operator+(difference_type m) const { any_row_iterator r = *this; r += m; return r; } template template inline typename OR_Matrix::template any_row_iterator OR_Matrix::any_row_iterator::operator-(const difference_type m) const { any_row_iterator r = *this; r -= m; return r; } template template inline bool OR_Matrix::any_row_iterator ::operator==(const any_row_iterator& y) const { return e == y.e; } template template inline bool OR_Matrix::any_row_iterator ::operator!=(const any_row_iterator& y) const { return e != y.e; } template template inline bool OR_Matrix::any_row_iterator::operator<(const any_row_iterator& y) const { return e < y.e; } template template inline bool OR_Matrix::any_row_iterator ::operator<=(const any_row_iterator& y) const { return e <= y.e; } template template inline bool OR_Matrix::any_row_iterator::operator>(const any_row_iterator& y) const { return e > y.e; } template template inline bool OR_Matrix::any_row_iterator ::operator>=(const any_row_iterator& y) const { return e >= y.e; } template template inline dimension_type OR_Matrix::any_row_iterator::row_size() const { return OR_Matrix::row_size(e); } template template inline dimension_type OR_Matrix::any_row_iterator::index() const { return e; } template inline typename OR_Matrix::row_iterator OR_Matrix::row_begin() { return num_rows() == 0 ? row_iterator(0) : row_iterator(vec[0]); } template inline typename OR_Matrix::row_iterator OR_Matrix::row_end() { return row_iterator(num_rows()); } template inline typename OR_Matrix::const_row_iterator OR_Matrix::row_begin() const { return num_rows() == 0 ? const_row_iterator(0) : const_row_iterator(vec[0]); } template inline typename OR_Matrix::const_row_iterator OR_Matrix::row_end() const { return const_row_iterator(num_rows()); } template inline typename OR_Matrix::element_iterator OR_Matrix::element_begin() { return vec.begin(); } template inline typename OR_Matrix::element_iterator OR_Matrix::element_end() { return vec.end(); } template inline typename OR_Matrix::const_element_iterator OR_Matrix::element_begin() const { return vec.begin(); } template inline typename OR_Matrix::const_element_iterator OR_Matrix::element_end() const { return vec.end(); } template inline void OR_Matrix::swap(OR_Matrix& y) { std::swap(vec, y.vec); std::swap(space_dim, y.space_dim); std::swap(vec_capacity, y.vec_capacity); } //! Returns the integer square root of \p x. inline unsigned long isqrt(unsigned long x) { unsigned long r = 0; for (unsigned long t = 0x40000000; t; t >>= 2) { unsigned long s = r + t; if (s <= x) { x -= s; r = s + t; } r >>= 1; } return r; } template inline dimension_type OR_Matrix::max_num_rows() { // Compute the maximum number of rows that are contained in a DB_Row // that allocates a pseudo-triangular matrix. dimension_type k = isqrt(2*DB_Row::max_size() + 1); return (k - 1) - (k - 1)%2; } template inline memory_size_type OR_Matrix::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } template inline OR_Matrix::OR_Matrix(const dimension_type dim) : vec(2*dim*(dim+1)), space_dim(dim), vec_capacity(vec.size()) { } template inline OR_Matrix::~OR_Matrix() { } template inline typename OR_Matrix::row_reference_type OR_Matrix::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 inline typename OR_Matrix::const_row_reference_type OR_Matrix::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 inline dimension_type OR_Matrix::space_dimension() const { return space_dim; } template inline dimension_type OR_Matrix::num_rows() const { return 2*space_dimension(); } template inline void OR_Matrix::clear() { OR_Matrix(0).swap(*this); } #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \relates OR_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template inline bool operator==(const OR_Matrix& x, const OR_Matrix& 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 inline bool operator!=(const OR_Matrix& x, const OR_Matrix& y) { return !(x == y); } template inline OR_Matrix::OR_Matrix(const OR_Matrix& y) : vec(y.vec), space_dim(y.space_dim), vec_capacity(compute_capacity(y.vec.size(), DB_Row::max_size())) { } template template inline OR_Matrix::OR_Matrix(const OR_Matrix& y) : vec(), space_dim(y.space_dim), vec_capacity(compute_capacity(y.vec.size(), DB_Row::max_size())) { vec.construct_upward_approximation(y.vec, vec_capacity); PPL_ASSERT(OK()); } template inline OR_Matrix& OR_Matrix::operator=(const OR_Matrix& y) { vec = y.vec; space_dim = y.space_dim; vec_capacity = compute_capacity(y.vec.size(), DB_Row::max_size()); return *this; } template inline void OR_Matrix::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 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); swap(new_matrix); } } } template inline void OR_Matrix::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 inline void OR_Matrix::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 new_matrix(new_dim); 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 inline bool l_m_distance_assign(Checked_Number& r, const OR_Matrix& x, const OR_Matrix& 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::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 inline bool rectilinear_distance_assign(Checked_Number& r, const OR_Matrix& x, const OR_Matrix& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign >(r, x, y, dir, tmp0, tmp1, tmp2); } #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \relates OR_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template inline bool euclidean_distance_assign(Checked_Number& r, const OR_Matrix& x, const OR_Matrix& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign >(r, x, y, dir, tmp0, tmp1, tmp2); } #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \relates OR_Matrix */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template inline bool l_infinity_distance_assign(Checked_Number& r, const OR_Matrix& x, const OR_Matrix& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2) { return l_m_distance_assign >(r, x, y, dir, tmp0, tmp1, tmp2); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::OR_Matrix */ template inline void swap(Parma_Polyhedra_Library::OR_Matrix& x, Parma_Polyhedra_Library::OR_Matrix& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/OR_Matrix.templates.hh line 1. */ /* OR_Matrix class implementation: non-inline template functions. */ #include namespace Parma_Polyhedra_Library { template memory_size_type OR_Matrix::external_memory_in_bytes() const{ return vec.external_memory_in_bytes(); } template bool OR_Matrix::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 void OR_Matrix::ascii_dump(std::ostream& s) const { const OR_Matrix& 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) template bool OR_Matrix::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 std::ostream& IO_Operators::operator<<(std::ostream& s, const OR_Matrix& m) { for (typename OR_Matrix::const_row_iterator m_iter = m.row_begin(), m_end = m.row_end(); m_iter != m_end; ++m_iter) { typename OR_Matrix::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 607. */ /* Automatically generated from PPL source file ../src/Octagonal_Shape.defs.hh line 48. */ #include #include #include #include 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: false is written if \p oct is an empty polyhedron; true is written if \p oct is a universe polyhedron; a system of constraints defining \p oct is written otherwise, all constraints separated by ", ". */ template std::ostream& operator<<(std::ostream& s, const Octagonal_Shape& oct); } // namespace IO_Operators /*! \brief Returns true 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 false is returned. */ template bool operator==(const Octagonal_Shape& x, const Octagonal_Shape& y); /*! \brief Returns true 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 true is returned. */ template bool operator!=(const Octagonal_Shape& x, const Octagonal_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool rectilinear_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool rectilinear_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& 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 true; returns false 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 bool rectilinear_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool euclidean_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool euclidean_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& 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 true; returns false 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 bool euclidean_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool l_infinity_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& 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 true; returns false otherwise. The direction of the approximation is specified by \p dir. All computations are performed using variables of type Checked_Number. */ template bool l_infinity_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& 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 true; returns false 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 bool l_infinity_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& y, Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS //! Decodes the constraint \p c as an octagonal difference. /*! \relates Octagonal_Shape \return true if the constraint \p c is an octagonal difference; false otherwise. \param c The constraint to be decoded. \param c_space_dim The space dimension of the constraint \p c (it is assumed to match the actual space dimension of \p c). \param c_num_vars If true 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 true 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 true 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 true 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 true 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) 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 allows for the efficient representation of a restricted kind of topologically closed convex polyhedra called octagonal shapes (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 octagonal constraints. 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 rounding-aware 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 optimally supports: - 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 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 oct(cs); \endcode */ template 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 N; #else typedef Checked_Number 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& x, Complexity_Class complexity = ANY_COMPLEXITY); //! Builds a conservative, upward approximation of \p y. /*! The complexity argument is ignored. */ template explicit Octagonal_Shape(const Octagonal_Shape& 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 explicit Octagonal_Shape(const Box& 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 explicit Octagonal_Shape(const BD_Shape& 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 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 true 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 true 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 true 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 true if and only if \p *this is an empty OS. bool is_empty() const; //! Returns true if and only if \p *this is a universe OS. bool is_universe() const; //! Returns true if and only if \p *this is discrete. bool is_discrete() const; /*! \brief Returns true if and only if \p *this is a bounded OS. */ bool is_bounded() const; /*! \brief Returns true if and only if \p *this is a topologically closed subset of the vector space. */ bool is_topologically_closed() const; /*! \brief Returns true if and only if \p *this contains (at least) an integer point. */ bool contains_integer_point() const; /*! \brief Returns true 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 true 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 true 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 true 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 true 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, false 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 true 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 true 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, false 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 true 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 true 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, false 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 true 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 true 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, false 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 true if and only if there exist a unique value \p val such that \p *this saturates the equality expr = val. \param expr The linear expression for which the frequency is needed; \param freq_n If true 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 true 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 false 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 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 true is returned, otherwise false 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 true is returned; otherwise false 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 datatype. \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()); /*! \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 pcs Possibly null pointer to a constraint system whose variables are contained in \p vars. If *pcs 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 *pcs. \param complexity_threshold A precision parameter of the \ref Wrapping_Operator "wrapping operator": higher values result in possibly improved precision. \param wrap_individually true 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 *pcs is dimension-incompatible with \p vars, or if \p *this is dimension-incompatible \p vars or with *pcs. */ 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* pcs = 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 ANY_COMPLEXITY. */ 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 ANY_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 CC76_extrapolation "CC76-extrapolation" between \p *this and \p y. \param y An OS that must 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 must 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 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 must 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 must 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 must 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 must 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 max_space_dimension(). */ 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 true if and only if the represented partial function has an empty codomain (i.e., it is always undefined). The has_empty_codomain() method will always be called before the methods below. However, if has_empty_codomain() returns true, 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 true is returned. If \f$f\f$ is undefined in \f$k\f$, then false 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 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 max_space_dimension(). If \p *this has space dimension \f$n\f$, with \f$n > 0\f$, and var 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$, dest 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 void refine_fp_interval_abstract_store( Box< Interval >& 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 true if successful, false 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 x == y, then x.hash_code() == y.hash_code(). */ int32_t hash_code() const; friend bool operator==(const Octagonal_Shape& x, const Octagonal_Shape& y); template friend bool Parma_Polyhedra_Library::rectilinear_distance_assign (Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2); template friend bool Parma_Polyhedra_Library::euclidean_distance_assign (Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2); template friend bool Parma_Polyhedra_Library::l_infinity_distance_assign (Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& y, const Rounding_Dir dir, Temp& tmp0, Temp& tmp1, Temp& tmp2); private: template friend class Parma_Polyhedra_Library::Octagonal_Shape; template friend class Parma_Polyhedra_Library::Box; //! The matrix that represents the octagonal shape. OR_Matrix 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::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 object. /*! The assertions supported are: - zero-dim universe: the polyhedron is the zero-dimensional vector space \f$\Rset^0 = \{\cdot\}\f$; - empty: the polyhedron is the empty set; - strongly closed: 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: - zero-dim universe excludes any other assertion; - empty: excludes any other assertion. */ class Status { public: //! By default Status is the zero-dim universe 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 true if successful, false 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 all bits in \p mask are set. bool test_all(flags_t mask) const; //! Check whether at least one 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 1793. */ #undef PPL_IN_Octagonal_Shape_CLASS //! The status flags to keep track of the internal state. Status status; //! Returns true if the OS is the zero-dimensional universe. bool marked_zero_dim_univ() const; //! Returns true if the OS is known to be empty. /*! The return value false does not necessarily implies that \p *this is non-empty. */ bool marked_empty() const; /*! \brief Returns true if \c this->matrix is known to be strongly closed. The return value false 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 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 matrix[i][j] <= k. void add_octagonal_constraint(dimension_type i, dimension_type j, const N& k); //! Adds the constraint matrix[i][j] <= num/den. void add_octagonal_constraint(dimension_type i, dimension_type j, Coefficient_traits::const_reference num, Coefficient_traits::const_reference den); /*! \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 v - u \<= k and v + u \<= k, starting from \p ub_v, which is an upper bound for \c v computed according to \p sc_expr and \p sc_den. Strong-closure will be able to deduce the constraints v - u \<= ub_v - lb_u and v + u \<= ub_v + ub_u. 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 q == sc_expr[u]/sc_den of \c u in \p sc_expr is greater than zero, we can improve the bound for v - u. 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). Conversely, if \c q is less than zero, we can improve the bound for v + u. In particular: - if q \<= -1, then v + u \<= ub_v + lb_u; - if -1 \< q \< 0, then v + u \<= ub_v + ((-q)*lb_u + (1+q)*ub_u). */ void deduce_v_pm_u_bounds(dimension_type v_id, dimension_type last_id, const Linear_Expression& sc_expr, Coefficient_traits::const_reference sc_den, 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 -v + u \<= k and -v - u \<= k, 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_den. Strong-closure will be able to deduce the constraints -v - u \<= -lb_v - lb_u and -v + u \<= -lb_v + ub_u. 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 q == sc_expr[u]/sc_den of \c u in \p sc_expr is greater than zero, we can improve the bound for -v + u. In particular: - if q \>= 1, then -v + u \<= -lb_v + lb_u; - if 0 \< q \< 1, then -v + u \<= -lb_v + (q*lb_u + (1-q)*ub_u). Conversely, if \c q is less than zero, we can improve the bound for -v - u. In particular: - if q \<= -1, then -v - u \<= -lb_v - ub_u; - if -1 \< q \< 0, then -v - u \<= -lb_v - ((-q)*ub_u + (1+q)*lb_u). */ void deduce_minus_v_pm_u_bounds(dimension_type v, dimension_type last_v, const Linear_Expression& sc_expr, Coefficient_traits::const_reference sc_den, 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& 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& successor, std::vector& 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& leaders) const; /*! \brief Stores into \p non_redundant information about the matrix entries that are non-redundant (i.e., will occur in 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& non_redundant) const; //! Removes the redundant constraints from \c this->matrix. void strong_reduction_assign() const; /*! \brief Returns true if and only if \c this->matrix is strongly reduced. */ bool is_strongly_reduced() const; /*! \brief Returns true 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 const 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 datatype. */ 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 true if and only if \p from_above is true and \p expr is bounded from above in \p *this, or \p from_above is false and \p expr is bounded from below in \p *this. \param expr The linear expression to test; \param from_above true 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 true 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 true 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, false 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 true 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 true 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, false 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& c); //! \name Exception Throwers //@{ void throw_dimension_incompatible(const char* method, const Octagonal_Shape& x) 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* name_row, const Linear_Expression& y) const; void throw_constraint_incompatible(const char* method) const; void throw_expression_too_complex(const char* method, const Linear_Expression& e) const; void throw_generic(const char* method, const char* reason) const; //@} // Exception Throwers static T default_stop_points[]; }; namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Octagonal_Shape */ template void swap(Parma_Polyhedra_Library::Octagonal_Shape& x, Parma_Polyhedra_Library::Octagonal_Shape& y); } // namespace std /* Automatically generated from PPL source file ../src/Og_Status.inlines.hh line 1. */ /* Octagonal_Shape::Status class implementation: inline functions. */ namespace Parma_Polyhedra_Library { template inline Octagonal_Shape::Status::Status(flags_t mask) : flags(mask) { } template inline Octagonal_Shape::Status::Status() : flags(ZERO_DIM_UNIV) { } template inline bool Octagonal_Shape::Status::test_all(flags_t mask) const { return (flags & mask) == mask; } template inline bool Octagonal_Shape::Status::test_any(flags_t mask) const { return flags & mask; } template inline void Octagonal_Shape::Status::set(flags_t mask) { flags |= mask; } template inline void Octagonal_Shape::Status::reset(flags_t mask) { flags &= ~mask; } template inline bool Octagonal_Shape::Status::test_zero_dim_univ() const { return flags == ZERO_DIM_UNIV; } template inline void Octagonal_Shape::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 inline void Octagonal_Shape::Status::set_zero_dim_univ() { // Zero-dim universe is incompatible with anything else. flags = ZERO_DIM_UNIV; } template inline bool Octagonal_Shape::Status::test_empty() const { return test_any(EMPTY); } template inline void Octagonal_Shape::Status::reset_empty() { reset(EMPTY); } template inline void Octagonal_Shape::Status::set_empty() { flags = EMPTY; } template inline bool Octagonal_Shape::Status::test_strongly_closed() const { return test_any(STRONGLY_CLOSED); } template inline void Octagonal_Shape::Status::reset_strongly_closed() { reset(STRONGLY_CLOSED); } template inline void Octagonal_Shape::Status::set_strongly_closed() { set(STRONGLY_CLOSED); } template inline bool Octagonal_Shape::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. const std::string zero_dim_univ = "ZE"; const std::string empty = "EM"; const std::string strong_closed = "SC"; const char yes = '+'; const char no = '-'; const char sep = ' '; /*! \relates Parma_Polyhedra_Library::Octagonal_Shape::Status Reads a keyword and its associated on/off flag from \p s. Returns true if the operation is successful, returns false otherwise. When successful, \p positive is set to true if the flag is on; it is set to false otherwise. */ inline bool get_field(std::istream& s, const std::string& 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 inline void Octagonal_Shape::Status::ascii_dump(std::ostream& s) const { using namespace Implementation::Octagonal_Shapes; s << (test_zero_dim_univ() ? yes : no) << zero_dim_univ << sep << (test_empty() ? yes : no) << empty << sep << sep << (test_strongly_closed() ? yes : no) << strong_closed << sep; } template inline bool Octagonal_Shape::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_Translations; template 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(); const dimension_type cs_space_dim = cs.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 { Variables_Set::const_iterator vars_end = vars.end(); for (Constraint_System::const_iterator j = cs.begin(), cs_end = cs.end(); j != cs_end; ++j) { const Constraint& c = *j; for (dimension_type d = cs_space_dim; d-- > 0; ) { if (c.coefficient(Variable(d)) != 0 && vars.find(d) != vars_end) goto skip; } // If we are here it means `c' does not depend on variables // in `vars'. p.refine_with_constraint(c); skip: continue; } } p.refine_with_constraint(min_value <= x); p.refine_with_constraint(x <= max_value); hull.upper_bound_assign(p); } pointset.swap(hull); } } template 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* pcs, Coefficient& tmp) { if (first == end) { PSET p(src); if (pcs != 0) p.refine_with_constraints(*pcs); for (Variables_Set::const_iterator i = vars.begin(), vars_end = vars.end(); i != vars.end(); ++i) { const Variable x = Variable(*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, pcs, tmp); } else wrap_assign_col(dest, src, vars, first+1, end, w, min_value, max_value, pcs, tmp); } } } template 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* pcs, const unsigned complexity_threshold, const bool wrap_individually, const char* class_name) { // We must have pcs->space_dimension() <= vars.space_dimension() // and vars.space_dimension() <= pointset.space_dimension(). // Dimension-compatibility check of `*pcs', if any. const dimension_type vars_space_dim = vars.space_dimension(); if (pcs != 0) { if (pcs->space_dimension() > vars_space_dim) { std::ostringstream s; s << "PPL::" << class_name << "::wrap_assign(..., pcs, ...):" << std::endl << "vars.space_dimension() == " << vars_space_dim << ", pcs->space_dimension() == " << pcs->space_dimension() << "."; throw std::invalid_argument(s.str()); } #ifndef NDEBUG // Check that all variables upon which `*pcs' depends are in `vars'. // An assertion is violated otherwise. const Constraint_System cs = *pcs; 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 *pcs, if any. if (vars.empty()) { if (pcs != 0) pointset.refine_with_constraints(*pcs); 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 `pcs' 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(ln); PPL_DIRTY_TEMP_COEFFICIENT(ld); PPL_DIRTY_TEMP_COEFFICIENT(un); PPL_DIRTY_TEMP_COEFFICIENT(ud); for (Variables_Set::const_iterator i = vars.begin(), vars_end = vars.end(); i != vars_end; ++i) { const Variable x = Variable(*i); bool extremum; if (!pointset.minimize(x, ln, ld, 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, un, ud, extremum)) goto set_full_range; div_assign_r(ln, ln, ld, ROUND_DOWN); div_assign_r(un, un, ud, ROUND_DOWN); ln -= min_value; un -= min_value; div_2exp_assign_r(ln, ln, w, ROUND_DOWN); div_2exp_assign_r(un, un, w, ROUND_DOWN); Coefficient& first_quadrant = ln; Coefficient& last_quadrant = un; // 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 = ud; quadrants = last_quadrant - first_quadrant + 1; unsigned extension; Result res = assign_r(extension, quadrants, ROUND_IGNORE); if (result_overflow(res) || 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) || 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(), tend = translations.end(); j != tend; ++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 && pcs == 0) { Coefficient& quadrant = first_quadrant; // Temporary variable holding the shifts to be applied in order // to implement the translations. Coefficient& shift = ld; 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.swap(hull); } else if (wrap_individually || !collective_wrap_too_complex) { PPL_ASSERT(!wrap_individually || pcs != 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(pcs != 0); wrap_assign_ind(pointset, dimensions_to_be_translated, translations.begin(), translations.end(), w, min_value, max_value, *pcs, ln, ld); } else { PSET hull(space_dim, EMPTY); wrap_assign_col(hull, pointset, dimensions_to_be_translated, translations.begin(), translations.end(), w, min_value, max_value, pcs, ln); pointset.swap(hull); } } if (pcs != 0) pointset.refine_with_constraints(*pcs); 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 37. */ #include 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 inline dimension_type Octagonal_Shape::max_space_dimension() { return OR_Matrix::max_num_rows()/2; } template inline bool Octagonal_Shape::marked_zero_dim_univ() const { return status.test_zero_dim_univ(); } template inline bool Octagonal_Shape::marked_strongly_closed() const { return status.test_strongly_closed(); } template inline bool Octagonal_Shape::marked_empty() const { return status.test_empty(); } template inline void Octagonal_Shape::set_zero_dim_univ() { status.set_zero_dim_univ(); } template inline void Octagonal_Shape::set_empty() { status.set_empty(); } template inline void Octagonal_Shape::set_strongly_closed() { status.set_strongly_closed(); } template inline void Octagonal_Shape::reset_strongly_closed() { status.reset_strongly_closed(); } template inline Octagonal_Shape::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 inline Octagonal_Shape::Octagonal_Shape(const Octagonal_Shape& y, Complexity_Class) : matrix(y.matrix), space_dim(y.space_dim), status(y.status) { } template template inline Octagonal_Shape::Octagonal_Shape(const Octagonal_Shape& 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 inline Octagonal_Shape::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 inline Octagonal_Shape::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 template inline Octagonal_Shape::Octagonal_Shape(const Box& box, Complexity_Class) : matrix(box.space_dimension()), space_dim(box.space_dimension()), status() { // Check for emptyness 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 inline Octagonal_Shape::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 template inline Octagonal_Shape::Octagonal_Shape(const BD_Shape& bd, Complexity_Class) : matrix(bd.space_dimension()), space_dim(bd.space_dimension()), status() { // Check for emptyness 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 inline Congruence_System Octagonal_Shape::congruences() const { return minimized_congruences(); } template inline Octagonal_Shape& Octagonal_Shape::operator=(const Octagonal_Shape& y) { matrix = y.matrix; space_dim = y.space_dim; status = y.status; return *this; } template inline Octagonal_Shape::~Octagonal_Shape() { } template inline void Octagonal_Shape::swap(Octagonal_Shape& y) { std::swap(matrix, y.matrix); std::swap(space_dim, y.space_dim); std::swap(status, y.status); } template inline dimension_type Octagonal_Shape::space_dimension() const { return space_dim; } template inline bool Octagonal_Shape::is_discrete() const { return affine_dimension() == 0; } template inline bool Octagonal_Shape::is_empty() const { strong_closure_assign(); return marked_empty(); } template inline bool Octagonal_Shape::bounds_from_above(const Linear_Expression& expr) const { return bounds(expr, true); } template inline bool Octagonal_Shape::bounds_from_below(const Linear_Expression& expr) const { return bounds(expr, false); } template inline bool Octagonal_Shape::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 inline bool Octagonal_Shape::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 inline bool Octagonal_Shape::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 inline bool Octagonal_Shape::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 inline bool Octagonal_Shape::is_topologically_closed() const { return true; } template inline void Octagonal_Shape::topological_closure_assign() { } /*! \relates Octagonal_Shape */ template inline bool operator==(const Octagonal_Shape& x, const Octagonal_Shape& 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 inline bool operator!=(const Octagonal_Shape& x, const Octagonal_Shape& y) { return !(x == y); } template inline const typename Octagonal_Shape::coefficient_type& Octagonal_Shape::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 inline typename Octagonal_Shape::coefficient_type& Octagonal_Shape::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 inline Constraint_System Octagonal_Shape::minimized_constraints() const { strong_reduction_assign(); return constraints(); } template inline void Octagonal_Shape::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::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 inline void Octagonal_Shape ::add_octagonal_constraint(const dimension_type i, const dimension_type j, Coefficient_traits::const_reference num, Coefficient_traits::const_reference den) { #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::row_iterator m_i = matrix.row_begin() + i; PPL_ASSERT(j < m_i.row_size()); PPL_ASSERT(den != 0); #endif PPL_DIRTY_TEMP(N, k); div_round_up(k, num, den); add_octagonal_constraint(i, j, k); } template inline void Octagonal_Shape::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 inline void Octagonal_Shape::add_recycled_constraints(Constraint_System& cs) { add_constraints(cs); } template inline void Octagonal_Shape::add_recycled_congruences(Congruence_System& cgs) { add_congruences(cgs); } template inline void Octagonal_Shape::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 inline void Octagonal_Shape::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 inline void Octagonal_Shape::refine_with_constraints(const Constraint_System& cs) { // Dimension-compatibility check. if (cs.space_dimension() > space_dimension()) throw_generic("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 inline void Octagonal_Shape::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 void Octagonal_Shape::refine_with_congruences(const Congruence_System& cgs) { // Dimension-compatibility check. if (cgs.space_dimension() > space_dimension()) throw_generic("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 inline void Octagonal_Shape::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 inline bool Octagonal_Shape::can_recycle_constraint_systems() { return false; } template inline bool Octagonal_Shape::can_recycle_congruence_systems() { return false; } template inline void Octagonal_Shape ::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 void Octagonal_Shape::wrap_assign(const Variables_Set& vars, Bounded_Integer_Type_Width w, Bounded_Integer_Type_Representation r, Bounded_Integer_Type_Overflow o, const Constraint_System* pcs, unsigned complexity_threshold, bool wrap_individually) { Implementation::wrap_assign(*this, vars, w, r, o, pcs, complexity_threshold, wrap_individually, "Octagonal_Shape"); } template inline void Octagonal_Shape::widening_assign(const Octagonal_Shape& y, unsigned* tp) { BHMZ05_widening_assign(y, tp); } template inline void Octagonal_Shape::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 inline void Octagonal_Shape::time_elapse_assign(const Octagonal_Shape& y) { // Dimension-compatibility check. if (space_dimension() != y.space_dimension()) throw_dimension_incompatible("time_elapse_assign(y)", y); // See the polyhedra documentation. C_Polyhedron px(constraints()); C_Polyhedron py(y.constraints()); px.time_elapse_assign(py); Octagonal_Shape x(px); swap(x); PPL_ASSERT(OK()); } template inline bool Octagonal_Shape::strictly_contains(const Octagonal_Shape& y) const { const Octagonal_Shape& x = *this; return x.contains(y) && !y.contains(x); } /*! \relates Octagonal_Shape */ template inline bool rectilinear_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& 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 inline bool rectilinear_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool rectilinear_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& y, const Rounding_Dir dir) { return rectilinear_distance_assign(r, x, y, dir); } /*! \relates Octagonal_Shape */ template inline bool euclidean_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& 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 inline bool euclidean_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool euclidean_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& y, const Rounding_Dir dir) { return euclidean_distance_assign(r, x, y, dir); } /*! \relates Octagonal_Shape */ template inline bool l_infinity_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& 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 inline bool l_infinity_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool l_infinity_distance_assign(Checked_Number& r, const Octagonal_Shape& x, const Octagonal_Shape& y, const Rounding_Dir dir) { return l_infinity_distance_assign(r, x, y, dir); } template inline memory_size_type Octagonal_Shape::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } template inline int32_t Octagonal_Shape::hash_code() const { return space_dimension() & 0x7fffffff; } template inline void Octagonal_Shape::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(); } } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Octagonal_Shape */ template inline void swap(Parma_Polyhedra_Library::Octagonal_Shape& x, Parma_Polyhedra_Library::Octagonal_Shape& y) { x.swap(y); } } // namespace std /* 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 33. */ #include #include #include #include #include #include #include namespace Parma_Polyhedra_Library { template Octagonal_Shape::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()) lp.add_constraint(Linear_Expression(c) >= 0); else lp.add_constraint(c); } // Check for unsatisfiability. if (!lp.is_satisfiable()) { *this = Octagonal_Shape(num_dimensions, EMPTY); return; } // Start with a universe OS that will be refined by the simplex. *this = Octagonal_Shape(num_dimensions, UNIVERSE); // Get all the upper bounds. Generator g(point()); PPL_DIRTY_TEMP_COEFFICIENT(num); PPL_DIRTY_TEMP_COEFFICIENT(den); 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, num, den); num *= 2; div_round_up(matrix[2*i+1][2*i], num, den); } // 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, num, den); div_round_up(matrix[2*i+1][2*j], num, den); } } // 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, num, den); div_round_up((i < j ? matrix[2*j][2*i] : matrix[2*i+1][2*j+1]), num, den); } } // 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, num, den); div_round_up((i < j ? matrix[2*j][2*i] : matrix[2*i+1][2*j+1]), num, den); } } // 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, num, den); div_round_up(matrix[2*i][2*j+1], num, den); } } // 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, num, den); num *= 2; div_round_up(matrix[2*i][2*i+1], num, den); } } 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 Octagonal_Shape::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::row_reference_type Row_Reference; typename OR_Matrix::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(); 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(); 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_generic("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: 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: 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 void Octagonal_Shape::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_generic("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 (!extract_octagonal_difference(c, c_space_dim, num_vars, i, j, coeff, term)) throw_generic("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::row_iterator i_iter = matrix.row_begin() + i; typename OR_Matrix::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::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 void Octagonal_Shape::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_generic("add_congruence(cg)", "cg is a non-trivial, proper congruence"); } PPL_ASSERT(cg.is_equality()); Constraint c(cg); add_constraint(c); } template void Octagonal_Shape::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 (!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::row_iterator i_iter = matrix.row_begin() + i; typename OR_Matrix::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::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 dimension_type Octagonal_Shape::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 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 Congruence_System Octagonal_Shape::minimized_congruences() const { // Strong closure is necessary to detect emptiness // and all (possibly implicit) equalities. strong_closure_assign(); const dimension_type space_dim = space_dimension(); Congruence_System cgs; if (space_dim == 0) { if (marked_empty()) cgs = Congruence_System::zero_dim_empty(); } else if (marked_empty()) cgs.insert((0*Variable(space_dim-1) %= 1) / 0); else { // KLUDGE: in the future `cgs' will be constructed of the right dimension. // For the time being, we force the dimension with the following line. cgs.insert(0*Variable(space_dim-1) == 0); // 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 leaders; compute_leaders(leaders); PPL_DIRTY_TEMP_COEFFICIENT(num); PPL_DIRTY_TEMP_COEFFICIENT(den); 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, num, den); den *= 2; cgs.insert(den*x == num); } 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, num, den); if (lead_i % 2 == 0) cgs.insert(den*x - den*y == num); else cgs.insert(den*x + den*y + num == 0); } continue; } } return cgs; } template void Octagonal_Shape::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::const_element_iterator y_it = y.matrix.element_begin(); for (typename OR_Matrix::row_iterator i = matrix.row_begin()+old_num_rows, matrix_row_end = matrix.row_end(); i != matrix_row_end; ++i) { typename OR_Matrix::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 doesn't preserve the closure. if (marked_strongly_closed()) reset_strongly_closed(); PPL_ASSERT(OK()); } template bool Octagonal_Shape::contains(const Octagonal_Shape& y) const { // Dimension-compatibility check. if (space_dim != y.space_dim) throw_dimension_incompatible("contains(y)", y); // The zero-dimensional universe octagon contains any other // dimension-compatible octagon. // The zero-dimensional empty octagon only contains another // zero-dimensional empty octagon. if (space_dim == 0) { if (!marked_empty()) return true; else return y.marked_empty(); } // `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; // `*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::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 bool Octagonal_Shape::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 correspomding constraint in the second Octagonal_Shape or vice versa. const dimension_type n_rows = matrix.num_rows(); typedef typename OR_Matrix::const_row_iterator Row_Iterator; typedef typename OR_Matrix::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 bool Octagonal_Shape::is_universe() const { // An empty octagon isn't, of course, 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::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 bool Octagonal_Shape::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::const_row_iterator i = matrix.row_begin(), matrix_row_end = matrix.row_end(); i != matrix_row_end; ++i) { typename OR_Matrix::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 bool Octagonal_Shape::contains_integer_point() const { // Force strong closure. if (is_empty()) return false; const dimension_type space_dim = space_dimension(); if (space_dim == 0) return true; // A strongly closed and consistent Octagonal_Shape defined by // integer constraints can only be empty due to tight coeherence. if (std::numeric_limits::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 oct_z(space_dim); oct_z.reset_strongly_closed(); typedef Octagonal_Shape::N Z; bool all_integers = true; typename OR_Matrix::const_element_iterator x_i = matrix.element_begin(); for (typename OR_Matrix::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 bool Octagonal_Shape::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 = 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(num); PPL_DIRTY_TEMP_COEFFICIENT(den); 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::const_row_iterator Row_Iterator; typedef typename OR_Matrix::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_den); val_den = 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, num, den); den *= 2; le -= coeff*v; le *= den; le -= num*coeff; val_den *= den; 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, num, den); le -= coeff*v; le += coeff*vj; le *= den; le -= num*coeff; val_den *= den; 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, num, den); le -= coeff*v; le -= coeff*vj; le *= den; le -= num*coeff; val_den *= den; 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_den, val_n, val_d); return true; } template bool Octagonal_Shape::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::const_row_iterator m_iter = matrix.row_begin() + n_v; typename OR_Matrix::const_row_reference_type r_v = *m_iter; typename OR_Matrix::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::const_row_iterator m_end = matrix.row_end(); m_iter != m_end; ++m_iter) { typename OR_Matrix::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 bool Octagonal_Shape::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::const_row_iterator iter = matrix.row_begin() + i; typename OR_Matrix::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 bool Octagonal_Shape::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::const_row_iterator iter = matrix.row_begin(), matrix_row_end = matrix.row_end(); iter != matrix_row_end; ++iter) { typename OR_Matrix::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 bool Octagonal_Shape::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 (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::const_row_iterator i_iter = matrix.row_begin() + i; typename OR_Matrix::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 bool Octagonal_Shape::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 (!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::const_row_iterator i_iter = matrix.row_begin() + i; typename OR_Matrix::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, expr.coefficient(Variable(i/2))); 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 bool Octagonal_Shape::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 Poly_Con_Relation Octagonal_Shape::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 = Linear_Expression(cg); PPL_DIRTY_TEMP_COEFFICIENT(min_num); PPL_DIRTY_TEMP_COEFFICIENT(min_den); bool min_included; bool bounded_below = minimize(le, min_num, min_den, 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_num); PPL_DIRTY_TEMP_COEFFICIENT(max_den); bool max_included; bool bounded_above = maximize(le, max_num, max_den, 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_num / min_den; const Coefficient& modulus = cg.modulus(); signed_distance = min_value % modulus; min_value -= signed_distance; if (min_value * min_den < min_num) 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_num / max_den; signed_distance = max_value % modulus; max_value += signed_distance; if (max_value * max_den > max_num) 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 Poly_Con_Relation Octagonal_Shape::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 (!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; for (dimension_type k = c_space_dim; k-- > 0; ) { Variable vk(k); le += c.coefficient(vk) * vk; } PPL_DIRTY_TEMP(Coefficient, max_num); PPL_DIRTY_TEMP(Coefficient, max_den); bool max_included; PPL_DIRTY_TEMP(Coefficient, min_num); PPL_DIRTY_TEMP(Coefficient, min_den); bool min_included; bool bounded_above = maximize(le, max_num, max_den, max_included); bool bounded_below = minimize(le, min_num, min_den, min_included); if (!bounded_above) { if (!bounded_below) return Poly_Con_Relation::strictly_intersects(); min_num += c.inhomogeneous_term() * min_den; switch (sgn(min_num)) { 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_num += c.inhomogeneous_term() * max_den; switch (sgn(max_num)) { 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_num += c.inhomogeneous_term() * max_den; min_num += c.inhomogeneous_term() * min_den; switch (sgn(max_num)) { case 1: switch (sgn(min_num)) { 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(); } case 0: if (min_num == 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::const_row_iterator i_iter = matrix.row_begin() + i; typename OR_Matrix::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::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_TEMP0(mpq_class, q_x); PPL_DIRTY_TEMP0(mpq_class, q_y); PPL_DIRTY_TEMP0(mpq_class, d); PPL_DIRTY_TEMP0(mpq_class, d1); PPL_DIRTY_TEMP0(mpq_class, c_den); PPL_DIRTY_TEMP0(mpq_class, q_den); assign_r(c_den, 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_den, ROUND_NOT_NEEDED); div_assign_r(d1, d1, c_den, 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_den, denom, ROUND_NOT_NEEDED); assign_r(q_y, numer, ROUND_NOT_NEEDED); div_assign_r(q_y, q_y, q_den, 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_den, denom, ROUND_NOT_NEEDED); assign_r(q_x, numer, ROUND_NOT_NEEDED); div_assign_r(q_x, q_x, q_den, ROUND_NOT_NEEDED); if (!is_plus_infinity(m_ci_cj)) { numer_denom(m_ci_cj, numer, denom); assign_r(q_den, denom, ROUND_NOT_NEEDED); assign_r(q_y, numer, ROUND_NOT_NEEDED); div_assign_r(q_y, q_y, q_den, 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 Poly_Gen_Relation Octagonal_Shape::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::const_row_iterator Row_Iterator; typedef typename OR_Matrix::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(num); PPL_DIRTY_TEMP_COEFFICIENT(den); 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's necessary that the scalar product // is not zero. The scalar product has the form: // 'den * g_coeff_x - num * g.divisor()'. numer_denom(m_ii_i, num, den); den *= 2; product = den * 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(num); add_mul_assign(product, num, 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, num, den); den *= -2; product = den * 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(num); add_mul_assign(product, num, 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: // '-den * g.coeff_x - num * 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, num, den); den *= 2; product = den * 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(num); add_mul_assign(product, num , 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: // 'den * g_coeff_x - num * 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 ax - ay = b. // The scalar product has the form // 'den * coeff_x - den * coeff_y - num * g.divisor()'. // To satisfy the constraint it's necessary that the scalar product // is not zero. numer_denom(m_i_j, num, den); product = den * g_coeff_x; neg_assign(den); add_mul_assign(product, den, 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(num); add_mul_assign(product, num, g.divisor()); } if (product != 0) return Poly_Gen_Relation::nothing(); } else { if (!is_plus_infinity(m_i_j)) { // The constraint has form ax - ay <= b. // The scalar product has the form // 'den * coeff_x - den * coeff_y - num * 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, num, den); product = den * g_coeff_x; neg_assign(den); add_mul_assign(product, den, 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(num); add_mul_assign(product, num, 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 -ax + ay <= b. // The scalar product has the form // '-den * coeff_x + den * coeff_y - num * 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, num, den); product = den * g_coeff_y; neg_assign(den); add_mul_assign(product, den, 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(num); add_mul_assign(product, num, 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 ax + ay = b. // The scalar product has the form // 'den * coeff_x + den * coeff_y - num * g.divisor()'. // To satisfy the constraint it's necessary that the scalar product // is not zero. numer_denom(m_ii_j, num, den); product = den * g_coeff_x; add_mul_assign(product, den, 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(num); add_mul_assign(product, num, g.divisor()); } if (product != 0) return Poly_Gen_Relation::nothing(); } else { if (!is_plus_infinity(m_i_jj)) { // The constraint has form -ax - ay <= b. // The scalar product has the form // '-den * coeff_x - den * coeff_y - num * 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, num, den); neg_assign(den); product = den * g_coeff_x; add_mul_assign(product, den, 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(num); add_mul_assign(product, num, 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 ax + ay <= b. // The scalar product has the form // 'den * coeff_x + den * coeff_y - num * 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, num, den); product = den * g_coeff_x; add_mul_assign(product, den, 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(num); add_mul_assign(product, num, 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 void Octagonal_Shape::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&>(*this); typedef typename OR_Matrix::row_iterator Row_Iterator; typedef typename OR_Matrix::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::element_iterator iter_ij; std::vector vec_k(n_rows); std::vector 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: // 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. 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 void Octagonal_Shape::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::row_iterator i_iter = matrix.row_begin(), i_end = matrix.row_end(); i_iter != i_end; ++i_iter) { typename OR_Matrix::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 bool Octagonal_Shape::tight_coherence_would_make_empty() const { PPL_ASSERT(std::numeric_limits::is_integer); PPL_ASSERT(marked_strongly_closed()); const dimension_type space_dim = space_dimension(); 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 void Octagonal_Shape::tight_closure_assign() { PPL_COMPILE_TIME_CHECK(std::numeric_limits::is_integer, "Octagonal_Shape::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); const dimension_type space_dim = space_dimension(); 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 void Octagonal_Shape ::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&>(*this); typedef typename OR_Matrix::row_iterator Row_Iterator; typedef typename OR_Matrix::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 void Octagonal_Shape ::compute_successors(std::vector& 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::const_row_iterator i_iter = matrix.row_begin()+i; typename OR_Matrix::const_row_reference_type m_i = *i_iter; typename OR_Matrix::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 void Octagonal_Shape ::compute_leaders(std::vector& 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::const_row_iterator i_iter = matrix.row_begin(), matrix_row_end = matrix.row_end(); i_iter != matrix_row_end; ++i_iter) { typename OR_Matrix::const_row_reference_type m_i = *i_iter; dimension_type i = i_iter.index(); typename OR_Matrix::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 void Octagonal_Shape ::compute_leaders(std::vector& successor, std::vector& 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 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 isn't a leader. dealt_with[next_i] = true; } } template void Octagonal_Shape::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 non_red; non_redundant_matrix_entries(non_red); // Throw away redundant constraints. Octagonal_Shape& x = const_cast&>(*this); #ifndef NDEBUG const Octagonal_Shape x_copy_before(x); #endif typename OR_Matrix::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::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 void Octagonal_Shape ::non_redundant_matrix_entries(std::vector& nr_rows) const { // Private method: the caller has to ensure the following. PPL_ASSERT(space_dim > 0 && !marked_empty() && marked_strongly_closed()); PPL_ASSERT(nr_rows.empty()); // Initialize `non_redundant' as if it was an OR_Matrix of booleans // (initially set to false). nr_rows.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 no_sing_leaders; dimension_type sing_leader = 0; bool exist_sing_class = false; std::vector 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::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) { nr_rows[next_j].set(j); j = next_j; next_j = successor[j]; } const dimension_type cj = coherent_index(j); nr_rows[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. nr_rows[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) { nr_rows[sing_leader].set(sing_leader+1); if (successor[sing_leader+1] != sing_leader+1) { dimension_type j = sing_leader; dimension_type next_jj = successor[j+1]; while (next_jj != j+1) { nr_rows[next_jj].set(j); j = next_jj; next_jj = successor[j+1]; } nr_rows[j+1].set(j); } else nr_rows[sing_leader+1].set(sing_leader); } } template void Octagonal_Shape::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::const_element_iterator j = y.matrix.element_begin(); for (typename OR_Matrix::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 void Octagonal_Shape::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 complementaries 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 = Linear_Expression(c); 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 bool Octagonal_Shape::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 res(dim, UNIVERSE); x.swap(res); return false; } typedef typename OR_Matrix::row_iterator Row_Iter; typedef typename OR_Matrix::const_row_iterator Row_CIter; typedef typename OR_Matrix::element_iterator Elem_Iter; typedef typename OR_Matrix::const_element_iterator Elem_CIter; // 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 wrt 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 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.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 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 x_nonred; x.non_redundant_matrix_entries(x_nonred); // ... count the non-redundant constraints. dimension_type x_num_nonred = 0; for (size_t i = x_nonred.size(); i-- > 0 ; ) x_num_nonred += x_nonred[i].count_ones(); PPL_ASSERT(x_num_nonred > 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 yy = y; // The constraints added to `yy' will be recorded in `res' ... Octagonal_Shape res(dim, UNIVERSE); // ... and we will count them too. dimension_type res_num_nonred = 0; // Compute leader information for `x'. std::vector 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_nonred; // 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_nonred; // 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_nonred < x_num_nonred) { res.reset_strongly_closed(); x.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_nonred; // 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_nonred; // 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_nonred < x_num_nonred) { res.reset_strongly_closed(); x.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_nonred_i = x_nonred[i]; for (dimension_type j = 0; j < 2*dim; ++j) { if (j != x_leaders[j]) continue; if (i >= j) { if (!x_nonred_i[j]) continue; } else if (!x_nonred[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_nonred; // 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_nonred < x_num_nonred) { res.reset_strongly_closed(); x.swap(res); } return bool_result; } } } } // This point should be unreachable. throw std::runtime_error("PPL internal error"); } template void Octagonal_Shape::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 void Octagonal_Shape::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::row_iterator i = matrix.row_begin() + n, matrix_row_end = matrix.row_end(); i != matrix_row_end; i += 2) { typename OR_Matrix::row_reference_type x_i = *i; typename OR_Matrix::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 void Octagonal_Shape::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 every variable and we check if it is to be removed. // If it is to be removed, we pass to the successive one, elsewhere // we move its elements in the right position. Variables_Set::const_iterator vsi = vars.begin(); dimension_type ftr = *vsi; dimension_type ftr_size = 2*ftr*(ftr+1); typename OR_Matrix::element_iterator iter = matrix.element_begin()+ftr_size; dimension_type i = ftr + 1; while (i < space_dim) { if (vars.count(i) != 0) ++i; else { typename OR_Matrix::row_iterator row_iter = matrix.row_begin()+2*i; typename OR_Matrix::row_reference_type row_ref = *row_iter; typename OR_Matrix::row_reference_type row_ref1 = *(++row_iter); // If variable(j) is to remove, we pass another variable, // else we shift its cells to up right. // Attention: first we shift the cells corrispondent to the first // row of variable(j), then we shift the cells corrispondent to the // second row. We recall that every variable is represented // in the `matrix' by two rows and two rows. 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]); } ++i; } } // Update the space dimension. matrix.shrink(new_space_dim); space_dim = new_space_dim; PPL_ASSERT(OK()); } template template void Octagonal_Shape::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 x(new_space_dim); typedef typename OR_Matrix::row_iterator Row_Iterator; typedef typename OR_Matrix::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 xj_iter = m_begin + double_new_j; Row_Reference x_j = *xj_iter; Row_Reference x_jj = *(xj_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]); } } } } } std::swap(matrix, x); space_dim = new_space_dim; PPL_ASSERT(OK()); } template void Octagonal_Shape::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::const_element_iterator j = y.matrix.element_begin(); for (typename OR_Matrix::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 template void Octagonal_Shape::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); #ifndef NDEBUG { // We assume that `y' is contained in or equal to `*this'. const Octagonal_Shape x_copy = *this; const Octagonal_Shape y_copy = y; PPL_ASSERT(x_copy.contains(y_copy)); } #endif // 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::const_element_iterator j = y.matrix.element_begin(); for (typename OR_Matrix::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 void Octagonal_Shape ::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 (!extract_octagonal_difference(c, cs_space_dim, num_vars, i, j, coeff, term)) continue; typedef typename OR_Matrix::const_row_iterator Row_iterator; typedef typename OR_Matrix::const_row_reference_type Row_reference; typedef typename OR_Matrix::row_iterator Row_Iterator; typedef typename OR_Matrix::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& 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 void Octagonal_Shape ::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; #ifndef NDEBUG { // We assume that `y' is contained in or equal to `*this'. const Octagonal_Shape x_copy = *this; const Octagonal_Shape y_copy = y; PPL_ASSERT(x_copy.contains(y_copy)); } #endif // 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 void Octagonal_Shape::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); #ifndef NDEBUG { // We assume that `y' is contained in or equal to `*this'. const Octagonal_Shape x_copy = *this; const Octagonal_Shape y_copy = y; PPL_ASSERT(x_copy.contains(y_copy)); } #endif // 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::const_element_iterator j = y.matrix.element_begin(); for (typename OR_Matrix::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 void Octagonal_Shape ::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; #ifndef NDEBUG { // We assume that `y' is contained in or equal to `*this'. const Octagonal_Shape x_copy = *this; const Octagonal_Shape y_copy = y; PPL_ASSERT(x_copy.contains(y_copy)); } #endif // 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 void Octagonal_Shape::CC76_narrowing_assign(const Octagonal_Shape& y) { // Dimension-compatibility check. if (space_dim != y.space_dim) throw_dimension_incompatible("CC76_narrowing_assign(y)", y); #ifndef NDEBUG { // We assume that `*this' is contained in or equal to `y'. const Octagonal_Shape x_copy = *this; const Octagonal_Shape y_copy = y; PPL_ASSERT(y_copy.contains(x_copy)); } #endif // 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::const_element_iterator j = y.matrix.element_begin(); for (typename OR_Matrix::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 void Octagonal_Shape ::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_den, const N& ub_v) { // Private method: the caller has to ensure the following. PPL_ASSERT(sc_den > 0); PPL_ASSERT(!is_plus_infinity(ub_v)); PPL_DIRTY_TEMP0(mpq_class, mpq_sc_den); assign_r(mpq_sc_den, sc_den, ROUND_NOT_NEEDED); // No need to consider indices greater than `last_id'. const dimension_type n_v = 2*v_id; typename OR_Matrix::row_reference_type m_cv = matrix[n_v+1]; // Speculatively allocate temporaries out of the loop. PPL_DIRTY_TEMP(N, half); PPL_DIRTY_TEMP0(mpq_class, minus_lb_u); PPL_DIRTY_TEMP0(mpq_class, q); PPL_DIRTY_TEMP0(mpq_class, minus_q); PPL_DIRTY_TEMP0(mpq_class, ub_u); PPL_DIRTY_TEMP0(mpq_class, lb_u); PPL_DIRTY_TEMP(N, up_approx); PPL_DIRTY_TEMP_COEFFICIENT(minus_expr_u); for (dimension_type u_id = last_id+1; u_id-- > 0; ) { // Skip the case when `u_id == v_id'. if (u_id == v_id) continue; const Coefficient& expr_u = sc_expr.coefficient(Variable(u_id)); // Skip the case when `expr_u == 0'. if (expr_u == 0) continue; 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_den) { // 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::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_den, 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_den) { // 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::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_den, 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 void Octagonal_Shape ::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_den, const N& minus_lb_v) { // Private method: the caller has to ensure the following. PPL_ASSERT(sc_den > 0); PPL_ASSERT(!is_plus_infinity(minus_lb_v)); PPL_DIRTY_TEMP0(mpq_class, mpq_sc_den); assign_r(mpq_sc_den, sc_den, ROUND_NOT_NEEDED); // No need to consider indices greater than `last_id'. const dimension_type n_v = 2*v_id; typename OR_Matrix::row_reference_type m_v = matrix[n_v]; // Speculatively allocate temporaries out of the loop. PPL_DIRTY_TEMP(N, half); PPL_DIRTY_TEMP0(mpq_class, ub_u); PPL_DIRTY_TEMP0(mpq_class, q); PPL_DIRTY_TEMP0(mpq_class, minus_lb_u); PPL_DIRTY_TEMP(N, up_approx); PPL_DIRTY_TEMP_COEFFICIENT(minus_expr_u); for (dimension_type u_id = last_id+1; u_id-- > 0; ) { // Skip the case when `u_id == v_id'. if (u_id == v_id) continue; const Coefficient& expr_u = sc_expr.coefficient(Variable(u_id)); // Skip the case when `expr_u == 0'. if (expr_u == 0) continue; 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_den) { // 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::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_den, 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_den) { // 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::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_den, 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 void Octagonal_Shape ::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::row_iterator m_iter = matrix.row_begin() + n_v; typename OR_Matrix::row_reference_type r_v = *m_iter; typename OR_Matrix::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::row_iterator m_end = matrix.row_end(); m_iter != m_end; ++m_iter) { typename OR_Matrix::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 void Octagonal_Shape ::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::row_iterator m_iter = matrix.row_begin() + n_v; typename OR_Matrix::row_reference_type r_v = *m_iter; typename OR_Matrix::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::row_iterator m_end = matrix.row_end(); m_iter != m_end; ++m_iter) { typename OR_Matrix::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 void Octagonal_Shape::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 void Octagonal_Shape::unconstrain(const Variables_Set& vars) { // The cylindrification wrt 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 void Octagonal_Shape::refine(const Variable var, const Relation_Symbol relsym, const Linear_Expression& expr, Coefficient_traits::const_reference denominator) { PPL_ASSERT(denominator != 0); const dimension_type expr_space_dim = expr.space_dimension(); PPL_ASSERT(space_dim >= expr_space_dim); 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 = 0; // Get information about the number of non-zero coefficients in `expr'. for (dimension_type i = expr_space_dim; i-- > 0; ) if (expr.coefficient(Variable(i)) != 0) { if (t++ == 1) break; else w_id = i; } // 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, the `expr' is of the general form. typedef typename OR_Matrix::row_iterator Row_Iterator; typedef typename OR_Matrix::row_reference_type Row_Reference; typedef typename OR_Matrix::const_row_iterator Row_iterator; typedef typename OR_Matrix::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_den); neg_assign(minus_den, 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_den) 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_den); 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_den); break; default: // We already dealt with the other cases. throw std::runtime_error("PPL internal error"); } } 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_den); } else { add_octagonal_constraint(n_var+1, n_w+1, b, denominator); add_octagonal_constraint(n_var, n_w, b, minus_den); } 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_den); } else { add_octagonal_constraint(n_var+1, n_w, b, denominator); add_octagonal_constraint(n_var, n_w+1, b, minus_den); } 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_den) { // 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_den); // 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_den) { // 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. throw std::runtime_error("PPL internal error"); } } 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_den = is_sc ? denominator : minus_den; const Coefficient& minus_sc_den = is_sc ? minus_den : 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_iter + (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_den != 1) { // Before computing quotients, the denominator should be // approximated towards zero. Since `sc_den' is known to be // positive, this amounts to rounding downwards, which is // achieved as usual by rounding upwards `minus_sc_den' // and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(sum, sum, down_sc_den, 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_den, sum); } else // Here `pinf_count == 1'. if (pinf_index != var_id) { const Coefficient& ppi = sc_expr.coefficient(Variable(pinf_index)); if (ppi == sc_den) // 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_den) { // 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_den != 1) { // Before computing quotients, the denominator should be // approximated towards zero. Since `sc_den' is known to be // positive, this amounts to rounding downwards, which is // achieved as usual by rounding upwards `minus_sc_den' // and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(neg_sum, neg_sum, down_sc_den, 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_den, 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_den) // 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_den) { // 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_end = m_iter + (2*w_id) + 2; m_iter != m_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_den != 1) { // Before computing the quotient, the denominator should be // approximated towards zero. Since `sc_den' is known to be // positive, this amounts to rounding downwards, which is achieved // by rounding upwards `minus_sc-den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(sum, sum, down_sc_den, 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_den, 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_den) { // 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_end = m_iter + (2*w_id) + 2; m_iter != m_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_den != 1) { // Before computing the quotient, the denominator should be // approximated towards zero. Since `sc_den' is known to be positive, // this amounts to rounding downwards, which is achieved by rounding // upwards `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(sum, sum, down_sc_den, 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_den, 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_den) { // 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. throw std::runtime_error("PPL internal error"); } } } template void Octagonal_Shape::affine_image(const Variable var, const Linear_Expression& expr, Coefficient_traits::const_reference denominator) { // The denominator cannot be zero. if (denominator == 0) throw_generic("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 = 0; // Get information about the number of non-zero coefficients in `expr'. // The `expr' must not be in two or plus variables. for (dimension_type i = expr_space_dim; i-- > 0; ) if (expr.coefficient(Variable(i)) != 0) { if (t++ == 1) break; else w_id = i; } typedef typename OR_Matrix::row_iterator Row_Iterator; typedef typename OR_Matrix::row_reference_type Row_Reference; typedef typename OR_Matrix::const_row_iterator Row_iterator; typedef typename OR_Matrix::const_row_reference_type Row_reference; const dimension_type n_var = 2*var_id; const Coefficient& b = expr.inhomogeneous_term(); PPL_DIRTY_TEMP_COEFFICIENT(minus_den); neg_assign_r(minus_den, 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_den); 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_den) { // 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_den); if (sign_symmetry) std::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) std::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) std::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) std::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_den); } else { add_octagonal_constraint(n_var+1, n_w+1, b, denominator); add_octagonal_constraint(n_var, n_w, b, minus_den); } } 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_den); } else { add_octagonal_constraint(n_var+1, n_w, b, denominator); add_octagonal_constraint(n_var, n_w+1, b, minus_den); } } 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_den = is_sc ? denominator : minus_den; const Coefficient& minus_sc_den = is_sc ? minus_den : 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_iter + (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_den != 1) { // Before computing quotients, the denominator should be approximated // towards zero. Since `sc_den' is known to be positive, this amounts to // rounding downwards, which is achieved as usual by rounding upwards // `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(pos_sum, pos_sum, down_sc_den, 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_den, 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_den) // 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_den) { // 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_den != 1) { // Before computing quotients, the denominator should be approximated // towards zero. Since `sc_den' is known to be positive, this amounts to // rounding downwards, which is achieved as usual by rounding upwards // `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(neg_sum, neg_sum, down_sc_den, 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_den, 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_den) // 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_den) { // 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 void Octagonal_Shape::affine_preimage(const Variable var, const Linear_Expression& expr, Coefficient_traits::const_reference denominator) { // The denominator cannot be zero. if (denominator == 0) throw_generic("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 = 0; // Get information about the number of the non-zero coefficients of `expr'. for (dimension_type i = expr_space_dim; i-- > 0; ) if (expr.coefficient(Variable(i)) != 0) { if (t++ == 1) break; else w_id = i; } // `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 void Octagonal_Shape ::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_generic("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_generic("generalized_affine_image(v, r, e, d)", "r is a strict relation symbol and " "*this is an Octagonal_Shape"); 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 = 0; // Get information about the number of non-zero coefficients in `expr'. // The `expr' must not be in two or plus variables. for (dimension_type i = expr_space_dim; i-- > 0; ) if (expr.coefficient(Variable(i)) != 0) { if (t++ == 1) break; else w_id = i; } typedef typename OR_Matrix::row_iterator Row_Iterator; typedef typename OR_Matrix::row_reference_type Row_Reference; typedef typename OR_Matrix::const_row_iterator Row_iterator; typedef typename OR_Matrix::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_den); neg_assign_r(minus_den, 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_den); break; default: // We already dealt with the other cases. throw std::runtime_error("PPL internal error"); } 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_den) { // 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_den); 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_den); else add_octagonal_constraint(n_var, n_w, b, minus_den); } 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_den); else add_octagonal_constraint(n_var, n_w+1, b, minus_den); } } break; } default: // We already dealt with the other cases. throw std::runtime_error("PPL internal error"); } 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_den = is_sc ? denominator : minus_den; const Coefficient& minus_sc_den = is_sc ? minus_den : 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_iter + (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_den != 1) { // Before computing the quotient, the denominator should be // approximated towards zero. Since `sc_den' is known to be // positive, this amounts to rounding downwards, which is // achieved as usual by rounding upwards // `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(sum, sum, down_sc_den, 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_den, 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_den) { // 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_iter + (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_den != 1) { // Before computing the quotient, the denominator should be // approximated towards zero. Since `sc_den' is known to be // positive, this amounts to rounding downwards, which is // achieved as usual by rounding upwards // `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(sum, sum, down_sc_den, 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_den, 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_den) { // 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. throw std::runtime_error("PPL internal error"); } incremental_strong_closure_assign(var); PPL_ASSERT(OK()); } template void Octagonal_Shape::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_generic("generalized_affine_image(e1, r, e2)", "r is a strict relation symbol and " "*this is an Octagonal_Shape"); 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 = 0; // Compute the number of the non-zero components of `lhs'. for (dimension_type i = lhs_space_dim; i-- > 0; ) if (lhs.coefficient(Variable(i)) != 0) { if (t_lhs++ == 1) break; else j_lhs = i; } 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. throw std::runtime_error("PPL internal error"); } } 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& den = lhs.coefficient(v); Relation_Symbol new_relsym = relsym; if (den < 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, den); } else { // Here `lhs' is of the general form, having at least two variables. // Compute the set of variables occurring in `lhs'. bool lhs_vars_intersects_rhs_vars = false; std::vector lhs_vars; for (dimension_type i = lhs_space_dim; i-- > 0; ) if (lhs.coefficient(Variable(i)) != 0) { lhs_vars.push_back(Variable(i)); if (rhs.coefficient(Variable(i)) != 0) lhs_vars_intersects_rhs_vars = true; } if (!lhs_vars_intersects_rhs_vars) { // `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. throw std::runtime_error("PPL internal error"); } } 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 = Variable(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. throw std::runtime_error("PPL internal error"); } // Remove the temporarily added dimension. remove_higher_space_dimensions(space_dim-1); #endif // Currently unnecessarily complex computation. } } PPL_ASSERT(OK()); } template void Octagonal_Shape::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_generic("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)", "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)", "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 = 0; // Get information about the number of non-zero coefficients in `lb_expr'. // The `expr' must not be in two or plus variables. for (dimension_type i = lb_space_dim; i-- > 0; ) if (lb_expr.coefficient(Variable(i)) != 0) { if (t++ == 1) break; else w_id = i; } typedef typename OR_Matrix::row_iterator Row_Iterator; typedef typename OR_Matrix::row_reference_type Row_Reference; typedef typename OR_Matrix::const_row_iterator Row_iterator; typedef typename OR_Matrix::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_den); neg_assign_r(minus_den, 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_den); 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_den) { // 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 = Variable(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_den); else add_octagonal_constraint(n_var, n_w, b, minus_den); else { // Add the new constraint `var + w >= b/denominator'. if (var_id < w_id) add_octagonal_constraint(n_w, n_var+1, b, minus_den); else add_octagonal_constraint(n_var, n_w+1, b, minus_den); } 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_den = is_sc ? denominator : minus_den; const Coefficient& minus_sc_den = is_sc ? minus_den : 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_iter + (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_den != 1) { // Before computing quotients, the denominator should be approximated // towards zero. Since `sc_den' is known to be positive, this amounts to // rounding downwards, which is achieved as usual by rounding upwards // `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(neg_sum, neg_sum, down_sc_den, 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_den, 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_den) // 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_den) { // 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 void Octagonal_Shape ::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_generic("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_generic("generalized_affine_preimage(v, r, e, d)", "r is a strict relation symbol and " "*this is an Octagonal_Shape"); 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_den); neg_assign(inverse_den, expr_v); const Relation_Symbol inverse_relsym = (sgn(denominator) == sgn(inverse_den)) ? relsym : reversed_relsym; generalized_affine_image(var, inverse_relsym, inverse, inverse_den); 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 void Octagonal_Shape ::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_generic("generalized_affine_preimage(e1, r, e2)", "r is a strict relation symbol and " "*this is an Octagonal_Shape"); 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 = 0; // Compute the number of the non-zero components of `lhs'. for (dimension_type i = lhs_space_dim; i-- > 0; ) if (lhs.coefficient(Variable(i)) != 0) { if (t_lhs++ == 1) break; else j_lhs = i; } 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& den = lhs.coefficient(v); Relation_Symbol new_relsym = relsym; if (den < 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, den); } else { // Here `lhs' is of the general form, having at least two variables. // Compute the set of variables occurring in `lhs'. bool lhs_vars_intersects_rhs_vars = false; std::vector lhs_vars; for (dimension_type i = lhs_space_dim; i-- > 0; ) if (lhs.coefficient(Variable(i)) != 0) { lhs_vars.push_back(Variable(i)); if (rhs.coefficient(Variable(i)) != 0) lhs_vars_intersects_rhs_vars = true; } if (!lhs_vars_intersects_rhs_vars) { // `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. throw std::runtime_error("PPL internal error"); } // 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 = Variable(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. throw std::runtime_error("PPL internal error"); } // Remove the temporarily added dimension. remove_higher_space_dimensions(space_dim-1); } } PPL_ASSERT(OK()); } template void Octagonal_Shape::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_generic("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)", "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)", "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 = Variable(space_dim); add_space_dimensions_and_embed(1); const Linear_Expression lb_inverse = lb_expr - (expr_v + denominator)*var; PPL_DIRTY_TEMP_COEFFICIENT(inverse_den); neg_assign(inverse_den, expr_v); affine_image(new_var, lb_inverse, inverse_den); strong_closure_assign(); PPL_ASSERT(!marked_empty()); generalized_affine_preimage(var, LESS_OR_EQUAL, ub_expr, denominator); if (sgn(denominator) == sgn(inverse_den)) 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 Constraint_System Octagonal_Shape::constraints() const { Constraint_System cs; if (space_dim == 0) { if (marked_empty()) cs = Constraint_System::zero_dim_empty(); } else if (marked_empty()) cs.insert(0*Variable(space_dim-1) <= -1); else { // KLUDGE: in the future `cs' will be constructed of the right dimension. // For the time being, we force the dimension with the following line. cs.insert(0*Variable(space_dim-1) <= 0); typedef typename OR_Matrix::const_row_iterator Row_Iterator; typedef typename OR_Matrix::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 ax - ay = 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 ax + ay = 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 void Octagonal_Shape::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_generic("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::row_iterator Row_Iterator; typedef typename OR_Matrix::row_reference_type Row_Reference; typedef typename OR_Matrix::const_row_iterator Row_iterator; typedef typename OR_Matrix::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 void Octagonal_Shape::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_generic("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::row_iterator Row_Iterator; typedef typename OR_Matrix::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 bool Octagonal_Shape::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 ub(x); ub.upper_bound_assign(y); // Compute redundancy information for x and y. // TODO: provide a nicer data structure for redundancy. std::vector x_non_red; x.non_redundant_matrix_entries(x_non_red); std::vector 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::const_row_iterator Row_Iterator; typedef typename OR_Matrix::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::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::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::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. swap(ub); PPL_ASSERT(OK()); return true; } template bool Octagonal_Shape ::integer_upper_bound_assign_if_exact(const Octagonal_Shape& y) { PPL_COMPILE_TIME_CHECK(std::numeric_limits::is_integer, "Octagonal_Shape::" "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 tx(x); tx.tight_closure_assign(); Octagonal_Shape ty(y); ty.tight_closure_assign(); Octagonal_Shape 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 tx_non_red; tx.non_redundant_matrix_entries(tx_non_red); std::vector 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::const_row_iterator Row_Iterator; typedef typename OR_Matrix::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::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::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::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. swap(ub); PPL_ASSERT(OK()); return true; } template void Octagonal_Shape::drop_some_non_integer_points(Complexity_Class) { if (std::numeric_limits::is_integer) return; const dimension_type space_dim = space_dimension(); strong_closure_assign(); if (space_dim == 0 || marked_empty()) return; for (typename OR_Matrix::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 void Octagonal_Shape ::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::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::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 constaints: 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()); } /*! \relates Parma_Polyhedra_Library::Octagonal_Shape */ template std::ostream& IO_Operators::operator<<(std::ostream& s, const Octagonal_Shape& x) { // Handle special cases first. if (x.marked_empty()) { s << "false"; return s; } if (x.is_universe()) { s << "true"; return s; } typedef typename Octagonal_Shape::coefficient_type N; typedef typename OR_Matrix::const_row_iterator Row_Iterator; typedef typename OR_Matrix::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 = x.matrix.row_begin(); Row_Iterator m_end = x.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 = Variable(i/2); const N& x_i_ii = (*i_iter)[i+1]; ++i_iter; const N& x_ii_i = (*i_iter)[i]; ++i_iter; // Check whether or not it is an equality constraint. if (is_additive_inverse(x_i_ii, x_ii_i)) { // It is an equality. PPL_ASSERT(!is_plus_infinity(x_i_ii) && !is_plus_infinity(x_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, x_ii_i, 1, ROUND_UP | ROUND_STRICT_RELATION) == V_EQ) s << v_i << " = " << half; else s << "2*" << v_i << " = " << x_ii_i; } else { // We will print unary non-strict inequalities, if any. if (!is_plus_infinity(x_i_ii)) { if (first) first = false; else s << ", "; neg_assign_r(negation, x_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(x_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, x_ii_i, 1, ROUND_UP | ROUND_STRICT_RELATION) == V_EQ) s << v_i << " <= " << half; else s << "2*" << v_i << " <= " << x_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 = Variable(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 = Variable(j/2); // Print binary differences. const N& x_ii_jj = r_ii[j+1]; const N& x_i_j = r_i[j]; // Check whether or not it is an equality constraint. if (is_additive_inverse(x_ii_jj, x_i_j)) { // It is an equality. PPL_ASSERT(!is_plus_infinity(x_i_j) && !is_plus_infinity(x_ii_jj)); if (first) first = false; else s << ", "; if (sgn(x_i_j) >= 0) s << v_j << " - " << v_i << " = " << x_i_j; else s << v_i << " - " << v_j << " = " << x_ii_jj; } else { // We will print non-strict inequalities, if any. if (!is_plus_infinity(x_i_j)) { if (first) first = false; else s << ", "; if (sgn(x_i_j) >= 0) s << v_j << " - " << v_i << " <= " << x_i_j; else { neg_assign_r(negation, x_i_j, ROUND_DOWN); s << v_i << " - " << v_j << " >= " << negation; } } if (!is_plus_infinity(x_ii_jj)) { if (first) first = false; else s << ", "; if (sgn(x_ii_jj) >= 0) s << v_i << " - " << v_j << " <= " << x_ii_jj; else { neg_assign_r(negation, x_ii_jj, ROUND_DOWN); s << v_j << " - " << v_i << " >= " << negation; } } } // Print binary sums. const N& x_i_jj = r_i[j+1]; const N& x_ii_j = r_ii[j]; // Check whether or not it is an equality constraint. if (is_additive_inverse(x_i_jj, x_ii_j)) { // It is an equality. PPL_ASSERT(!is_plus_infinity(x_i_jj) && !is_plus_infinity(x_ii_j)); if (first) first = false; else s << ", "; s << v_j << " + " << v_i << " = " << x_ii_j; } else { // We will print non-strict inequalities, if any. if (!is_plus_infinity(x_i_jj)) { if (first) first = false; else s << ", "; neg_assign_r(negation, x_i_jj, ROUND_DOWN); s << v_j << " + " << v_i << " >= " << negation; } if (!is_plus_infinity(x_ii_j)) { if (first) first = false; else s << ", "; s << v_j << " + " << v_i << " <= " << x_ii_j; } } } } return s; } template void Octagonal_Shape::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) template bool Octagonal_Shape::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 memory_size_type Octagonal_Shape::external_memory_in_bytes() const { return matrix.external_memory_in_bytes(); } template bool Octagonal_Shape::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::const_row_iterator i = matrix.row_begin(), matrix_row_end = matrix.row_end(); i != matrix_row_end; ++i) { typename OR_Matrix::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::const_row_iterator i = matrix.row_begin(), m_end = matrix.row_end(); i != m_end; ++i) { typename OR_Matrix::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::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 void Octagonal_Shape ::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 void Octagonal_Shape ::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 void Octagonal_Shape::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 void Octagonal_Shape::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 void Octagonal_Shape::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 void Octagonal_Shape::throw_constraint_incompatible(const char* method) const { std::ostringstream s; s << "PPL::Octagonal_Shape::" << method << ":\n" << "the constraint is incompatible."; throw std::invalid_argument(s.str()); } template void Octagonal_Shape ::throw_expression_too_complex(const char* method, const Linear_Expression& e) const { using namespace IO_Operators; std::ostringstream s; s << "PPL::Octagonal_Shape::" << method << ":\n" << e << " is too complex."; throw std::invalid_argument(s.str()); } template void Octagonal_Shape ::throw_dimension_incompatible(const char* method, const char* name_row, const Linear_Expression& y) const { std::ostringstream s; s << "PPL::Octagonal_Shape::" << method << ":\n" << "this->space_dimension() == " << space_dimension() << ", " << name_row << "->space_dimension() == " << y.space_dimension() << "."; throw std::invalid_argument(s.str()); } template void Octagonal_Shape::throw_generic(const char* method, const char* reason) const { 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 2178. */ /* Automatically generated from PPL source file ../src/BD_Shape.inlines.hh line 38. */ #include #include #include namespace Parma_Polyhedra_Library { template inline dimension_type BD_Shape::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::max_num_rows() - 1, DB_Matrix::max_num_columns() - 1); } template inline bool BD_Shape::marked_zero_dim_univ() const { return status.test_zero_dim_univ(); } template inline bool BD_Shape::marked_empty() const { return status.test_empty(); } template inline bool BD_Shape::marked_shortest_path_closed() const { return status.test_shortest_path_closed(); } template inline bool BD_Shape::marked_shortest_path_reduced() const { return status.test_shortest_path_reduced(); } template inline void BD_Shape::set_zero_dim_univ() { status.set_zero_dim_univ(); } template inline void BD_Shape::set_empty() { status.set_empty(); } template inline void BD_Shape::set_shortest_path_closed() { status.set_shortest_path_closed(); } template inline void BD_Shape::set_shortest_path_reduced() { status.set_shortest_path_reduced(); } template inline void BD_Shape::reset_shortest_path_closed() { status.reset_shortest_path_closed(); } template inline void BD_Shape::reset_shortest_path_reduced() { status.reset_shortest_path_reduced(); } template inline BD_Shape::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 inline BD_Shape::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 template inline BD_Shape::BD_Shape(const BD_Shape& 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 inline Congruence_System BD_Shape::congruences() const { return minimized_congruences(); } template inline void BD_Shape::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 inline void BD_Shape::add_recycled_constraints(Constraint_System& cs) { add_constraints(cs); } template inline void BD_Shape::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 inline void BD_Shape::add_recycled_congruences(Congruence_System& cgs) { add_congruences(cgs); } template inline void BD_Shape::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 inline void BD_Shape::refine_with_constraints(const Constraint_System& cs) { // Dimension-compatibility check. if (cs.space_dimension() > space_dimension()) throw_generic("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 inline void BD_Shape::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 void BD_Shape::refine_with_congruences(const Congruence_System& cgs) { // Dimension-compatibility check. if (cgs.space_dimension() > space_dimension()) throw_generic("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 inline void BD_Shape::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 inline bool BD_Shape::can_recycle_constraint_systems() { return false; } template inline bool BD_Shape::can_recycle_congruence_systems() { return false; } template inline BD_Shape::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 template inline BD_Shape::BD_Shape(const Box& box, Complexity_Class) : dbm(box.space_dimension() + 1), status(), redundancy_dbm() { // Check for emptyness 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 inline BD_Shape::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 template inline BD_Shape::BD_Shape(const Octagonal_Shape& os, Complexity_Class) : dbm(os.space_dimension() + 1), status(), redundancy_dbm() { // Check for emptyness 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 inline BD_Shape& BD_Shape::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 inline BD_Shape::~BD_Shape() { } template inline void BD_Shape::swap(BD_Shape& y) { std::swap(dbm, y.dbm); std::swap(status, y.status); std::swap(redundancy_dbm, y.redundancy_dbm); } template inline dimension_type BD_Shape::space_dimension() const { return dbm.num_rows() - 1; } template inline bool BD_Shape::is_empty() const { shortest_path_closure_assign(); return marked_empty(); } template inline bool BD_Shape::bounds_from_above(const Linear_Expression& expr) const { return bounds(expr, true); } template inline bool BD_Shape::bounds_from_below(const Linear_Expression& expr) const { return bounds(expr, false); } template inline bool BD_Shape::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 inline bool BD_Shape::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 inline bool BD_Shape::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 inline bool BD_Shape::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 inline bool BD_Shape::is_topologically_closed() const { return true; } template inline bool BD_Shape::is_discrete() const { return affine_dimension() == 0; } template inline void BD_Shape::topological_closure_assign() { } /*! \relates BD_Shape */ template inline bool operator==(const BD_Shape& x, const BD_Shape& 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 inline bool operator!=(const BD_Shape& x, const BD_Shape& y) { return !(x == y); } /*! \relates BD_Shape */ template inline bool rectilinear_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& 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 inline bool rectilinear_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool rectilinear_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& y, const Rounding_Dir dir) { return rectilinear_distance_assign(r, x, y, dir); } /*! \relates BD_Shape */ template inline bool euclidean_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& 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 inline bool euclidean_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool euclidean_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& y, const Rounding_Dir dir) { return euclidean_distance_assign(r, x, y, dir); } /*! \relates BD_Shape */ template inline bool l_infinity_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& 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 inline bool l_infinity_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& y, const Rounding_Dir dir) { typedef Checked_Number 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 inline bool l_infinity_distance_assign(Checked_Number& r, const BD_Shape& x, const BD_Shape& y, const Rounding_Dir dir) { return l_infinity_distance_assign(r, x, y, dir); } template inline void BD_Shape::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 inline void BD_Shape::add_dbm_constraint(const dimension_type i, const dimension_type j, Coefficient_traits::const_reference num, Coefficient_traits::const_reference den) { // Private method: the caller has to ensure the following. PPL_ASSERT(i <= space_dimension() && j <= space_dimension() && i != j); PPL_ASSERT(den != 0); PPL_DIRTY_TEMP(N, k); div_round_up(k, num, den); add_dbm_constraint(i, j, k); } template inline void BD_Shape::time_elapse_assign(const BD_Shape& y) { // Dimension-compatibility check. if (space_dimension() != y.space_dimension()) throw_dimension_incompatible("time_elapse_assign(y)", y); // See the polyhedra documentation. C_Polyhedron px(constraints()); C_Polyhedron py(y.constraints()); px.time_elapse_assign(py); BD_Shape x(px); swap(x); PPL_ASSERT(OK()); } template inline bool BD_Shape::strictly_contains(const BD_Shape& y) const { const BD_Shape& x = *this; return x.contains(y) && !y.contains(x); } template inline bool BD_Shape::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(y); #endif } template inline bool BD_Shape::integer_upper_bound_assign_if_exact(const BD_Shape& y) { PPL_COMPILE_TIME_CHECK(std::numeric_limits::is_integer, "BD_Shape::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(y); } template inline void BD_Shape::remove_higher_space_dimensions(const dimension_type new_dim) { // Dimension-compatibility check: the variable having // maximum index is the one occurring last in the set. if (new_dim > space_dimension()) throw_dimension_incompatible("remove_higher_space_dimensions(nd)", new_dim); // 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_dim == space_dimension()) { PPL_ASSERT(OK()); return; } // Shortest-path closure is necessary as in remove_space_dimensions(). shortest_path_closure_assign(); dbm.resize_no_copy(new_dim + 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_dim == 0 && !marked_empty()) set_zero_dim_univ(); PPL_ASSERT(OK()); } template void BD_Shape::wrap_assign(const Variables_Set& vars, Bounded_Integer_Type_Width w, Bounded_Integer_Type_Representation r, Bounded_Integer_Type_Overflow o, const Constraint_System* pcs, unsigned complexity_threshold, bool wrap_individually) { Implementation::wrap_assign(*this, vars, w, r, o, pcs, complexity_threshold, wrap_individually, "BD_Shape"); } template inline void BD_Shape::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 inline void BD_Shape::H79_widening_assign(const BD_Shape& y, unsigned* tp) { // See the documentation for polyhedra. C_Polyhedron px(constraints()); C_Polyhedron py(y.constraints()); px.H79_widening_assign(py, tp); BD_Shape x(px); swap(x); PPL_ASSERT(OK()); } template inline void BD_Shape::widening_assign(const BD_Shape& y, unsigned* tp) { H79_widening_assign(y, tp); } template inline void BD_Shape::limited_H79_extrapolation_assign(const BD_Shape& y, const Constraint_System& cs, unsigned* tp) { // See the documentation for polyhedra. C_Polyhedron px(constraints()); C_Polyhedron py(y.constraints()); px.limited_H79_extrapolation_assign(py, cs, tp); BD_Shape x(px); swap(x); PPL_ASSERT(OK()); } template inline memory_size_type BD_Shape::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } template inline int32_t BD_Shape::hash_code() const { return space_dimension() & 0x7fffffff; } template inline void BD_Shape::drop_some_non_integer_points_helper(N& elem) { if (!is_integer(elem)) { Result r = floor_assign_r(elem, elem, ROUND_DOWN); used(r); PPL_ASSERT(r == V_EQ); reset_shortest_path_closed(); } } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::BD_Shape */ template inline void swap(Parma_Polyhedra_Library::BD_Shape& x, Parma_Polyhedra_Library::BD_Shape& y) { x.swap(y); } } // namespace std /* 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 38. */ #include #include #include #include #include #include namespace Parma_Polyhedra_Library { template BD_Shape::BD_Shape(const Congruence_System& cgs) : dbm(cgs.space_dimension() + 1), status(), redundancy_dbm() { add_congruences(cgs); } template BD_Shape::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& 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(); for (dimension_type i = space_dim; i > 0; --i) { const Coefficient& g_i = g.coefficient(Variable(i-1)); DB_Row& dbm_i = dbm[i]; for (dimension_type j = space_dim; j > 0; --j) if (i != j) div_round_up(dbm_i[j], g.coefficient(Variable(j-1)) - g_i, d); div_round_up(dbm_i[0], -g_i, d); } for (dimension_type j = space_dim; j > 0; --j) div_round_up(dbm_0[j], g.coefficient(Variable(j-1)), 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(); for (dimension_type i = space_dim; i > 0; --i) { const Coefficient& g_i = g.coefficient(Variable(i-1)); DB_Row& dbm_i = dbm[i]; // The loop correctly handles the case when i == j. for (dimension_type j = space_dim; j > 0; --j) { div_round_up(tmp, g.coefficient(Variable(j-1)) - 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) { div_round_up(tmp, g.coefficient(Variable(j-1)), 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_generic("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: for (dimension_type i = space_dim; i > 0; --i) { const Coefficient& g_i = g.coefficient(Variable(i-1)); DB_Row& 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.coefficient(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 (dimension_type j = space_dim; j > 0; --j) if (g.coefficient(Variable(j-1)) != 0) assign_r(dbm_0[j], PLUS_INFINITY, ROUND_NOT_NEEDED); break; case Generator::RAY: for (dimension_type i = space_dim; i > 0; --i) { const Coefficient& g_i = g.coefficient(Variable(i-1)); DB_Row& 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.coefficient(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 (dimension_type j = space_dim; j > 0; --j) if (g.coefficient(Variable(j-1)) > 0) assign_r(dbm_0[j], PLUS_INFINITY, ROUND_NOT_NEEDED); break; default: // Points and closure points already dealt with. break; } } set_shortest_path_closed(); PPL_ASSERT(OK()); } template BD_Shape::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(num_dimensions, EMPTY); return; } if (num_dimensions == 0) { *this = BD_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 = BD_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 = BD_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 = BD_Shape(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()) lp.add_constraint(Linear_Expression(c) >= 0); else lp.add_constraint(c); } // Check for unsatisfiability. if (!lp.is_satisfiable()) { *this = BD_Shape(num_dimensions, EMPTY); return; } // Start with a universe BDS that will be refined by the simplex. *this = BD_Shape(num_dimensions, UNIVERSE); // Get all the upper bounds. Generator g(point()); PPL_DIRTY_TEMP_COEFFICIENT(num); PPL_DIRTY_TEMP_COEFFICIENT(den); 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, num, den); div_round_up(dbm[0][i], num, den); } // 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, num, den); div_round_up(dbm[j][i], num, den); } } // 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, num, den); div_round_up(dbm[i][0], num, den); } } set_shortest_path_closed(); PPL_ASSERT(OK()); return; } // Extract easy-to-find bounds from constraints. PPL_ASSERT(complexity == POLYNOMIAL_COMPLEXITY); *this = BD_Shape(num_dimensions, UNIVERSE); refine_with_constraints(ph.constraints()); } template dimension_type BD_Shape::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 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 Congruence_System BD_Shape::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; if (space_dim == 0) { if (marked_empty()) cgs = Congruence_System::zero_dim_empty(); } else if (marked_empty()) cgs.insert((0*Variable(space_dim-1) %= 1) / 0); else { // KLUDGE: in the future `cgs' will be constructed of the right dimension. // For the time being, we force the dimension with the following line. cgs.insert(0*Variable(space_dim-1) == 0); PPL_DIRTY_TEMP_COEFFICIENT(num); PPL_DIRTY_TEMP_COEFFICIENT(den); // Compute leader information. std::vector leaders; compute_leaders(leaders); // Go through the non-leaders to generate equality constraints. const DB_Row& 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], num, den); cgs.insert(den*Variable(i-1) == num); } else { // A binary equality has to be generated. PPL_ASSERT(!is_plus_infinity(dbm[i][leader])); numer_denom(dbm[i][leader], num, den); cgs.insert(den*Variable(leader-1) - den*Variable(i-1) == num); } } } } return cgs; } template void BD_Shape::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); // 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_generic("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 (!extract_bounded_difference(c, c_space_dim, num_vars, i, j, coeff)) throw_generic("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); 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 void BD_Shape::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_generic("add_congruence(cg)", "cg is a non-trivial, proper congruence"); } PPL_ASSERT(cg.is_equality()); Constraint c(cg); add_constraint(c); } template void BD_Shape::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_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 (!extract_bounded_difference(c, c_space_dim, 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 void BD_Shape::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& 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 bool BD_Shape::contains(const BD_Shape& y) const { const BD_Shape& 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); // The zero-dimensional universe shape contains any other // dimension-compatible shape. // The zero-dimensional empty shape only contains another // zero-dimensional empty shape. if (x_space_dim == 0) { if (!marked_empty()) return true; else return y.marked_empty(); } /* The `y' bounded difference shape need be closed. In fact if, for example, in `*this' we have the constraints: x1 - x2 <= 1; x1 <= 3; x2 <= 2; in `y' the constraints are: x1 - x2 <= 0; x2 <= 1; without closure it returns "false", instead if we close `y' we have the implicit constraint x1 <= 1; and so we obtain the right result "true". */ y.shortest_path_closure_assign(); // An empty shape is contained in any other dimension-compatible shapes. if (y.marked_empty()) return true; // `*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& x_dbm_i = x.dbm[i]; const DB_Row& 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 bool BD_Shape::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& 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 bool BD_Shape::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& 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 bool BD_Shape::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& 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 bool BD_Shape::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::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 bds_z(space_dim); typedef BD_Shape::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_i = bds_z.dbm[i]; const DB_Row& 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 bool BD_Shape::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 = 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(num); PPL_DIRTY_TEMP_COEFFICIENT(den); 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. bool constant_v = false; PPL_DIRTY_TEMP_COEFFICIENT(val_den); val_den = 1; for (dimension_type i = dbm.num_rows(); i-- > 1; ) { constant_v = false; const Variable v(i-1); coeff = le.coefficient(v); if (coeff == 0) { constant_v = true; continue; } const DB_Row& 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, num, den); le -= coeff*v; le *= den; le -= num*coeff; val_den *= den; constant_v = true; continue; } // Check the bounded differences with the other dimensions that // have non-zero coefficient in `le'. else { PPL_ASSERT(!constant_v); for (dimension_type j = i; j-- > 1; ) { const Variable vj(j-1); if (le.coefficient(vj) == 0) // The coefficient in `le' is 0, so do nothing. continue; assign_r(tmp, dbm_i[j], ROUND_NOT_NEEDED); if (is_additive_inverse(dbm[j][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, num, den); le -= coeff*v - coeff*vj; le *= den; le -= num*coeff; val_den *= den; 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_den, val_n, val_d); return true; } template bool BD_Shape::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& 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 void BD_Shape ::compute_predecessors(std::vector& 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 pred_size = dbm.num_rows(); // Initially, each variable is leader of its own zero-equivalence class. predecessor.reserve(pred_size); for (dimension_type i = 0; i < pred_size; ++i) predecessor.push_back(i); // Now compute actual predecessors. for (dimension_type i = pred_size; i-- > 1; ) if (i == predecessor[i]) { const DB_Row& 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 void BD_Shape::compute_leaders(std::vector& 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 l_i = leaders[i]; PPL_ASSERT(l_i <= i); if (l_i != i) { const dimension_type ll_i = leaders[l_i]; PPL_ASSERT(ll_i == leaders[ll_i]); leaders[i] = ll_i; } } } template bool BD_Shape::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 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& 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& x_k = x_copy.dbm[k]; for (dimension_type i = 0; i <= space_dim; ++i) if (leader[i] == i) { const DB_Row& 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 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 no-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 ld_i = leader[i]; // Case a: leader. if (ld_i == i) { for (dimension_type j = 0; j <= space_dim; ++j) { dimension_type ld_j = leader[j]; // Only the connectedness with equivalent variables // is considered. if (j != ld_j) if (!redundancy_dbm[i][j]) { if (t == 1) // Two no-leaders couldn't connected with the same leader. return false; else if (ld_j != i) // The variables isn't in the same equivalence class. return false; else { ++t; var_conn[i] = j; } } } } // Case b: no-leader. else { for (dimension_type j = 0; j <= space_dim; ++j) { if (!redundancy_dbm[i][j]) { dimension_type ld_j = leader[j]; if (ld_i != ld_j) // The variables isn't in the same equivalence class. return false; else { if (t == 1) // Two variables couldn't connected with the same leader. return false; else { ++t; var_conn[i] = j; } } // A no-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 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) { bool jc_i = just_checked[i]; // We do not re-check the already considered single cycles. if (!jc_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 bool BD_Shape::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; const dimension_type c_space_dim = c.space_dimension(); 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 (extract_bounded_difference(c, c_space_dim, 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 bool BD_Shape::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; const dimension_type c_space_dim = c.space_dimension(); 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 (!extract_bounded_difference(c, c_space_dim, 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); const Coefficient& coeff_i = expr.coefficient(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 bool BD_Shape::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 Poly_Con_Relation BD_Shape::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); PPL_DIRTY_TEMP_COEFFICIENT(min_num); PPL_DIRTY_TEMP_COEFFICIENT(min_den); bool min_included; bool bounded_below = minimize(le, min_num, min_den, 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_num); PPL_DIRTY_TEMP_COEFFICIENT(max_den); bool max_included; bool bounded_above = maximize(le, max_num, max_den, 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_num / min_den; const Coefficient& modulus = cg.modulus(); signed_distance = min_value % modulus; min_value -= signed_distance; if (min_value * min_den < min_num) 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_num / max_den; signed_distance = max_value % modulus; max_value += signed_distance; if (max_value * max_den > max_num) 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 Poly_Con_Relation BD_Shape::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 (!extract_bounded_difference(c, c_space_dim, 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; for (dimension_type k = c_space_dim; k-- > 0; ) { Variable vk(k); le += c.coefficient(vk) * vk; } PPL_DIRTY_TEMP(Coefficient, max_num); PPL_DIRTY_TEMP(Coefficient, max_den); bool max_included; PPL_DIRTY_TEMP(Coefficient, min_num); PPL_DIRTY_TEMP(Coefficient, min_den); bool min_included; bool bounded_above = maximize(le, max_num, max_den, max_included); bool bounded_below = minimize(le, min_num, min_den, min_included); if (!bounded_above) { if (!bounded_below) return Poly_Con_Relation::strictly_intersects(); min_num += c.inhomogeneous_term() * min_den; switch (sgn(min_num)) { 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_num += c.inhomogeneous_term() * max_den; switch (sgn(max_num)) { 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_num += c.inhomogeneous_term() * max_den; min_num += c.inhomogeneous_term() * min_den; switch (sgn(max_num)) { case 1: switch (sgn(min_num)) { 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(); } case 0: if (min_num == 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 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_den, denom, ROUND_NOT_NEEDED); assign_r(q_y, numer, ROUND_NOT_NEEDED); div_assign_r(q_y, q_y, q_den, 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_den, denom, ROUND_NOT_NEEDED); assign_r(q_x, numer, ROUND_NOT_NEEDED); div_assign_r(q_x, q_x, q_den, ROUND_NOT_NEEDED); if (!is_plus_infinity(y)) { numer_denom(y, numer, denom); assign_r(q_den, denom, ROUND_NOT_NEEDED); assign_r(q_y, numer, ROUND_NOT_NEEDED); div_assign_r(q_y, q_y, q_den, 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 Poly_Gen_Relation BD_Shape::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(num); PPL_DIRTY_TEMP_COEFFICIENT(den); PPL_DIRTY_TEMP_COEFFICIENT(product); // We find in `*this' all the constraints. 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& 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: den*x - den*y = num. // Compute the scalar product. numer_denom(dbm_ij, num, den); product = 0; add_mul_assign(product, den, g_coeff_y); add_mul_assign(product, -den, g_coeff_x); if (!is_line_or_ray) add_mul_assign(product, num, 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: den*x - den*y <= num. // Compute the scalar product. numer_denom(dbm_ij, num, den); product = 0; add_mul_assign(product, den, g_coeff_y); add_mul_assign(product, -den, g_coeff_x); if (!is_line_or_ray) add_mul_assign(product, num, 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: den*y - den*x <= b. // Compute the scalar product. numer_denom(dbm_ji, num, den); product = 0; add_mul_assign(product, den, g_coeff_x); add_mul_assign(product, -den, g_coeff_y); if (!is_line_or_ray) add_mul_assign(product, num, 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 void BD_Shape::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&>(*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& x_dbm_k = x.dbm[k]; for (dimension_type i = num_dimensions + 1; i-- > 0; ) { DB_Row& 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 void BD_Shape::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(*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& x_v = x.dbm[v]; // Step 1: Improve all constraints on variable `var'. for (dimension_type k = num_dimensions + 1; k-- > 0; ) { DB_Row& 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& 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& 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& 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 void BD_Shape::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 predecessor; compute_predecessors(predecessor); std::vector 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& 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 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 pred_j = predecessor[j]; if (j == pred_j) { // We finally found the leader of `i'. PPL_ASSERT(redundancy[i][j]); redundancy[i].clear(j); // Here we dealt with `j' (i.e., `pred_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[pred_j][j]); redundancy[pred_j].clear(j); dealt_with[pred_j] = true; j = pred_j; } } // Even though shortest-path reduction is not going to change the BDS, // it might change its internal representation. BD_Shape& x = const_cast&>(*this); std::swap(x.redundancy_dbm, redundancy); x.set_shortest_path_reduced(); PPL_ASSERT(is_shortest_path_reduced()); } template void BD_Shape::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& dbm_i = dbm[i]; const DB_Row& 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 bool BD_Shape::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 eps(x_space_dim); Linear_Expression zero_expr = 0*eps; Linear_Expression db_expr; PPL_DIRTY_TEMP_COEFFICIENT(num); PPL_DIRTY_TEMP_COEFFICIENT(den); // 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 (eps). 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& x_dbm_i = x.dbm[i]; const DB_Row& 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, num, den); // 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 (den != 1) db_expr *= den; db_expr += num; if (x_dbm_ij >= y_dbm_i[j]) env_cs.insert(db_expr >= 0); else { db_expr += eps; 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, num, den); // 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 (den != 1) db_expr *= den; db_expr += num; 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 += eps; 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 (eps) has to be maximised. MIP_Problem env_lp(x_space_dim + 1, env_cs, eps, 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? throw std::runtime_error("PPL internal error"); case UNBOUNDED_MIP_PROBLEM: return false; case OPTIMIZED_MIP_PROBLEM: lp_ij.optimal_value(num, den); if (num > 0) return false; break; } } } // The upper bound of x and y is indeed exact. upper_bound_assign(y); PPL_ASSERT(OK()); return true; } template template bool BD_Shape::BHZ09_upper_bound_assign_if_exact(const BD_Shape& y) { PPL_COMPILE_TIME_CHECK(!integer_upper_bound || std::numeric_limits::is_integer, "BD_Shape::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 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& x_i = x.dbm[i]; const Bit_Row& x_red_i = x.redundancy_dbm[i]; const DB_Row& y_i = y.dbm[i]; const DB_Row& 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& x_k = x.dbm[k]; const DB_Row& y_k = y.dbm[k]; const Bit_Row& y_red_k = y.redundancy_dbm[k]; const DB_Row& 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. swap(ub); PPL_ASSERT(OK()); return true; } template void BD_Shape::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 complementaries 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 = Linear_Expression(c); 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 bool BD_Shape::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 res(dim, UNIVERSE); x.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& 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 wrt 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 intefer type, then // we also need to check that we are actually able to construct // a constraint inconsistent wrt this one. goto found; } // Then search binary constraints. for (i = 1; i <= dim; ++i) { const DB_Row& 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 intefer type, then // we also need to check that we are actually able to construct // a constraint inconsistent wrt 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 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.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 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_nonredundant = (dim+1)*(dim+1); for (dimension_type i = dim + 1; i-- > 0; ) x_num_nonredundant -= x.redundancy_dbm[i].count_ones(); PPL_ASSERT(x_num_nonredundant > 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 yy = y; // The constraints added to `yy' will be recorded in `res' ... BD_Shape res(dim, UNIVERSE); // ... and we will count them too. dimension_type res_num_nonredundant = 0; // Compute leader information for `x'. std::vector x_leaders; x.compute_leaders(x_leaders); // First go through the unary equality constraints. const DB_Row& x_dbm_0 = x.dbm[0]; DB_Row& yy_dbm_0 = yy.dbm[0]; DB_Row& 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_nonredundant; // 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_nonredundant; // 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_nonredundant < x_num_nonredundant) { res.reset_shortest_path_closed(); x.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_nonredundant; // 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_nonredundant; // 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_nonredundant < x_num_nonredundant) { res.reset_shortest_path_closed(); x.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& x_dbm_i = x.dbm[i]; const Bit_Row& x_redundancy_dbm_i = x.redundancy_dbm[i]; DB_Row& yy_dbm_i = yy.dbm[i]; DB_Row& 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_nonredundant; // 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_nonredundant < x_num_nonredundant) { res.reset_shortest_path_closed(); x.swap(res); } return bool_result; } } } } // This point should be unreachable. throw std::runtime_error("PPL internal error"); } template void BD_Shape::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 void BD_Shape::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& 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& 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 void BD_Shape::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) { std::swap(dbm[dst], dbm[src]); for (dimension_type i = old_space_dim + 1; i-- > 0; ) { DB_Row& 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) { std::swap(dbm[dst], dbm[src]); for (dimension_type i = old_space_dim + 1; i-- > 0; ) { DB_Row& 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 template void BD_Shape::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 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& dbm_0 = dbm[0]; DB_Row& 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& dbm_i = dbm[i]; ++new_i; DB_Row& 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]); } } } } std::swap(dbm, x); PPL_ASSERT(OK()); } template void BD_Shape::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& dbm_i = dbm[i]; const DB_Row& 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 template void BD_Shape::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); #ifndef NDEBUG { // We assume that `y' is contained in or equal to `*this'. const BD_Shape x_copy = *this; const BD_Shape y_copy = y; PPL_ASSERT(x_copy.contains(y_copy)); } #endif // 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 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& dbm_i = dbm[i]; const DB_Row& 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 void BD_Shape::get_limiting_shape(const Constraint_System& cs, BD_Shape& limiting_shape) 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_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 (extract_bounded_difference(c, cs_space_dim, 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& ls_dbm = limiting_shape.dbm; 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 (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()) { 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) 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 void BD_Shape::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_generic("limited_CC76_extrapolation_assign(y, cs)", "cs is space_dimension incompatible"); // Strict inequalities not allowed. if (cs.has_strict_inequalities()) throw_generic("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; #ifndef NDEBUG { // We assume that `y' is contained in or equal to `*this'. const BD_Shape x_copy = *this; const BD_Shape y_copy = y; PPL_ASSERT(x_copy.contains(y_copy)); } #endif // 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 limiting_shape(space_dim, UNIVERSE); get_limiting_shape(cs, limiting_shape); CC76_extrapolation_assign(y, tp); intersection_assign(limiting_shape); } template void BD_Shape::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); #ifndef NDEBUG { // We assume that `y' is contained in or equal to `*this'. const BD_Shape x_copy = *this; const BD_Shape y_copy = y; PPL_ASSERT(x_copy.contains(y_copy)); } #endif // 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 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& dbm_i = dbm[i]; const DB_Row& 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 void BD_Shape::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_generic("limited_BHMZ05_extrapolation_assign(y, cs)", "cs is space-dimension incompatible"); // Strict inequalities are not allowed. if (cs.has_strict_inequalities()) throw_generic("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; #ifndef NDEBUG { // We assume that `y' is contained in or equal to `*this'. const BD_Shape x_copy = *this; const BD_Shape y_copy = y; PPL_ASSERT(x_copy.contains(y_copy)); } #endif // 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 limiting_shape(space_dim, UNIVERSE); get_limiting_shape(cs, limiting_shape); BHMZ05_widening_assign(y, tp); intersection_assign(limiting_shape); } template void BD_Shape::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); #ifndef NDEBUG { // We assume that `*this' is contained in or equal to `y'. const BD_Shape x_copy = *this; const BD_Shape y_copy = y; PPL_ASSERT(y_copy.contains(x_copy)); } #endif // 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& dbm_i = dbm[i]; const DB_Row& 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 void BD_Shape ::deduce_v_minus_u_bounds(const dimension_type v, const dimension_type last_v, const Linear_Expression& sc_expr, Coefficient_traits::const_reference sc_den, const N& ub_v) { PPL_ASSERT(sc_den > 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/den' 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_TEMP0(mpq_class, mpq_sc_den); assign_r(mpq_sc_den, sc_den, ROUND_NOT_NEEDED); const DB_Row& dbm_0 = dbm[0]; // Speculative allocation of temporaries to be used in the following loop. PPL_DIRTY_TEMP0(mpq_class, minus_lb_u); PPL_DIRTY_TEMP0(mpq_class, q); PPL_DIRTY_TEMP0(mpq_class, ub_u); PPL_DIRTY_TEMP(N, up_approx); // No need to consider indices greater than `last_v'. for (dimension_type u = last_v; u > 0; --u) if (u != v) { const Coefficient& expr_u = sc_expr.coefficient(Variable(u-1)); if (expr_u > 0) { if (expr_u >= sc_den) // Deducing `v - u <= ub_v - ub_u'. sub_assign_r(dbm[u][v], ub_v, dbm_0[u], ROUND_UP); else { DB_Row& dbm_u = dbm[u]; 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_den' be the // rational coefficient of `u' in `sc_expr/sc_den', // 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_den, ROUND_NOT_NEEDED); assign_r(ub_u, dbm_0[u], 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 void BD_Shape ::deduce_u_minus_v_bounds(const dimension_type v, const dimension_type last_v, const Linear_Expression& sc_expr, Coefficient_traits::const_reference sc_den, const N& minus_lb_v) { PPL_ASSERT(sc_den > 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/den' 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_TEMP0(mpq_class, mpq_sc_den); assign_r(mpq_sc_den, sc_den, ROUND_NOT_NEEDED); DB_Row& dbm_0 = dbm[0]; DB_Row& dbm_v = dbm[v]; // Speculative allocation of temporaries to be used in the following loop. PPL_DIRTY_TEMP0(mpq_class, ub_u); PPL_DIRTY_TEMP0(mpq_class, q); PPL_DIRTY_TEMP0(mpq_class, minus_lb_u); PPL_DIRTY_TEMP(N, up_approx); // No need to consider indices greater than `last_v'. for (dimension_type u = last_v; u > 0; --u) if (u != v) { const Coefficient& expr_u = sc_expr.coefficient(Variable(u-1)); if (expr_u > 0) { if (expr_u >= sc_den) // Deducing `u - v <= lb_u - lb_v', // i.e., `u - v <= (-lb_v) - (-lb_u)'. sub_assign_r(dbm_v[u], minus_lb_v, dbm[u][0], ROUND_UP); else { const N& dbm_0u = dbm_0[u]; 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_den' be the // rational coefficient of `u' in `sc_expr/sc_den', // 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_den, ROUND_NOT_NEEDED); assign_r(minus_lb_u, dbm[u][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], up_approx, minus_lb_v, ROUND_UP); } } } } } template void BD_Shape::forget_all_dbm_constraints(const dimension_type v) { PPL_ASSERT(0 < v && v <= dbm.num_rows()); DB_Row& 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 void BD_Shape::forget_binary_dbm_constraints(const dimension_type v) { PPL_ASSERT(0 < v && v <= dbm.num_rows()); DB_Row& 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 void BD_Shape::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 void BD_Shape::unconstrain(const Variables_Set& vars) { // The cylindrification wrt 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 void BD_Shape::refine(const Variable var, const Relation_Symbol relsym, const Linear_Expression& expr, Coefficient_traits::const_reference denominator) { PPL_ASSERT(denominator != 0); const dimension_type expr_space_dim = expr.space_dimension(); PPL_ASSERT(space_dimension() >= expr_space_dim); 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 = 0; // Get information about the number of non-zero coefficients in `expr'. for (dimension_type i = expr_space_dim; i-- > 0; ) if (expr.coefficient(Variable(i)) != 0) { if (t++ == 1) break; else w = i+1; } // 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.coefficient(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& dbm_0 = dbm[0]; PPL_DIRTY_TEMP_COEFFICIENT(minus_den); neg_assign(minus_den, 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_den); 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_den); break; default: // We already dealt with the other cases. throw std::runtime_error("PPL internal error"); } return; } if (t == 1) { // Case 2: expr == a*w + b, w != v, a == denominator. PPL_ASSERT(expr.coefficient(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_den); 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_den); add_dbm_constraint(v, w, d); break; default: // We already dealt with the other cases. throw std::runtime_error("PPL internal error"); } 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_den = is_sc ? denominator : minus_den; const Coefficient& minus_sc_den = is_sc ? minus_den : 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 (dimension_type i = w; i > 0; --i) { const Coefficient& sc_i = sc_expr.coefficient(Variable(i-1)); const int sign_i = sgn(sc_i); if (sign_i == 0) continue; 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]; if (!is_plus_infinity(approx_i)) add_mul_assign_r(sum, coeff_i, approx_i, ROUND_UP); else { ++pinf_count; pinf_index = i; } } // Approximating `-sc_expr'. if (neg_pinf_count <= 1) { const N& approx_minus_i = dbm[i][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; } } } else if (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][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; } } // Approximating `-sc_expr'. if (neg_pinf_count <= 1) { const N& approx_i = dbm_0[i]; 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; } } } } // 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_den' is known to be positive, this amounts to // rounding downwards, which is achieved as usual by rounding upwards // `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); // Exploit the upper approximation, if possible. if (pinf_count <= 1) { // Compute quotient (if needed). if (down_sc_den != 1) div_assign_r(sum, sum, down_sc_den, 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_den, sum); } else // Here `pinf_count == 1'. if (pinf_index != v && sc_expr.coefficient(Variable(pinf_index-1)) == sc_den) // 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_den != 1) div_assign_r(neg_sum, neg_sum, down_sc_den, 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& 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_den, neg_sum); } else // Here `neg_pinf_count == 1'. if (neg_pinf_index != v && sc_expr.coefficient(Variable(neg_pinf_index-1)) == sc_den) // 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 (dimension_type i = w; i > 0; --i) { const Coefficient& sc_i = sc_expr.coefficient(Variable(i-1)); const int sign_i = sgn(sc_i); if (sign_i == 0) continue; // Choose carefully: we are approximating `sc_expr'. const N& approx_i = (sign_i > 0) ? dbm_0[i] : dbm[i][0]; if (is_plus_infinity(approx_i)) { if (++pinf_count > 1) break; pinf_index = i; 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_den != 1) { // Before computing the quotient, the denominator should be // approximated towards zero. Since `sc_den' is known to be // positive, this amounts to rounding downwards, which is achieved // by rounding upwards `minus_sc-den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(sum, sum, down_sc_den, 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_den, sum); } else if (pinf_count == 1) if (expr.coefficient(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 (dimension_type i = w; i > 0; --i) { const Coefficient& sc_i = sc_expr.coefficient(Variable(i-1)); const int sign_i = sgn(sc_i); if (sign_i == 0) continue; // Choose carefully: we are approximating `-sc_expr'. const N& approx_i = (sign_i > 0) ? dbm[i][0] : dbm_0[i]; if (is_plus_infinity(approx_i)) { if (++pinf_count > 1) break; pinf_index = i; 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_den != 1) { // Before computing the quotient, the denominator should be // approximated towards zero. Since `sc_den' is known to be positive, // this amounts to rounding downwards, which is achieved by rounding // upwards `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(sum, sum, down_sc_den, 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_den, sum); } else if (pinf_count == 1) if (pinf_index != v && expr.coefficient(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. throw std::runtime_error("PPL internal error"); } PPL_ASSERT(OK()); } template void BD_Shape::affine_image(const Variable var, const Linear_Expression& expr, Coefficient_traits::const_reference denominator) { // The denominator cannot be zero. if (denominator == 0) throw_generic("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 = 0; // Get information about the number of non-zero coefficients in `expr'. for (dimension_type i = expr_space_dim; i-- > 0; ) if (expr.coefficient(Variable(i)) != 0) { if (t++ == 1) break; else w = i+1; } // 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_den); neg_assign(minus_den, 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_den); PPL_ASSERT(OK()); return; } if (t == 1) { // Value of the one and only non-zero coefficient in `expr'. const Coefficient& a = expr.coefficient(Variable(w-1)); if (a == denominator || a == minus_den) { // 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_den); DB_Row& 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'. std::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_den); 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_den); } 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_den); 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_den = is_sc ? denominator : minus_den; const Coefficient& minus_sc_den = is_sc ? minus_den : 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& 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 (dimension_type i = w; i > 0; --i) { const Coefficient& sc_i = sc_expr.coefficient(Variable(i-1)); 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]; 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; } } // Approximating `-sc_expr'. if (neg_pinf_count <= 1) { const N& up_approx_minus_i = dbm[i][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; } } } else if (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][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; } } // Approximating `-sc_expr'. if (neg_pinf_count <= 1) { const N& up_approx_i = dbm_0[i]; 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; } } } } // 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_den != 1) { // Before computing quotients, the denominator should be approximated // towards zero. Since `sc_den' is known to be positive, this amounts to // rounding downwards, which is achieved as usual by rounding upwards // `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(pos_sum, pos_sum, down_sc_den, 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_den, pos_sum); } else // Here `pos_pinf_count == 1'. if (pos_pinf_index != v && sc_expr.coefficient(Variable(pos_pinf_index-1)) == sc_den) // 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_den != 1) { // Before computing quotients, the denominator should be approximated // towards zero. Since `sc_den' is known to be positive, this amounts to // rounding downwards, which is achieved as usual by rounding upwards // `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(neg_sum, neg_sum, down_sc_den, 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& 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_den, neg_sum); } else // Here `neg_pinf_count == 1'. if (neg_pinf_index != v && sc_expr.coefficient(Variable(neg_pinf_index-1)) == sc_den) // 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 void BD_Shape::affine_preimage(const Variable var, const Linear_Expression& expr, Coefficient_traits::const_reference denominator) { // The denominator cannot be zero. if (denominator == 0) throw_generic("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 = 0; // Get information about the number of non-zero coefficients in `expr'. for (dimension_type i = expr_space_dim; i-- > 0; ) if (expr.coefficient(Variable(i)) != 0) { if (t++ == 1) break; else j = i; } // 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.coefficient(Variable(j)); if (a == denominator || a == -denominator) { // Case 2: expr = a*w + b, with a = +/- denominator. if (j == var.id()) // 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 void BD_Shape ::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_generic("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)", "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)", "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 = 0; // Get information about the number of non-zero coefficients in `expr'. for (dimension_type i = ub_space_dim; i-- > 0; ) if (ub_expr.coefficient(Variable(i)) != 0) { if (t++ == 1) break; else w = i+1; } // 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_den); neg_assign(minus_den, 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.coefficient(Variable(w-1)); if (a == denominator || a == minus_den) { // 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 = Variable(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_den = is_sc ? denominator : minus_den; const Coefficient& minus_sc_den = is_sc ? minus_den : 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& 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 (dimension_type i = w; i > 0; --i) { const Coefficient& sc_i = sc_expr.coefficient(Variable(i-1)); 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]; 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; } } } else if (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][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; } } } } // 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_den != 1) { // Before computing quotients, the denominator should be approximated // towards zero. Since `sc_den' is known to be positive, this amounts to // rounding downwards, which is achieved as usual by rounding upwards // `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(pos_sum, pos_sum, down_sc_den, 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_den, pos_sum); } else // Here `pos_pinf_count == 1'. if (pos_pinf_index != v && sc_expr.coefficient(Variable(pos_pinf_index-1)) == sc_den) // Add the constraint `v - pos_pinf_index <= pos_sum'. dbm[pos_pinf_index][v] = pos_sum; } PPL_ASSERT(OK()); } template void BD_Shape ::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_generic("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)", "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)", "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 = Variable(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_den); neg_assign(lb_inverse_den, lb_expr_v); affine_image(new_var, lb_inverse, lb_inverse_den); shortest_path_closure_assign(); PPL_ASSERT(!marked_empty()); generalized_affine_preimage(var, LESS_OR_EQUAL, ub_expr, denominator); if (sgn(denominator) == sgn(lb_inverse_den)) add_constraint(var >= new_var); else add_constraint(var <= new_var); // Remove the temporarily added dimension. remove_higher_space_dimensions(space_dim); } template void BD_Shape::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_generic("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_generic("generalized_affine_image(v, r, e, d)", "r is a strict relation symbol and " "*this is a BD_Shape"); // The relation symbol cannot be a disequality. if (relsym == NOT_EQUAL) throw_generic("generalized_affine_image(v, r, e, d)", "r is the disequality relation symbol and " "*this is a BD_Shape"); 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 = 0; // Get information about the number of non-zero coefficients in `expr'. for (dimension_type i = expr_space_dim; i-- > 0; ) if (expr.coefficient(Variable(i)) != 0) { if (t++ == 1) break; else w = i+1; } // 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& dbm_0 = dbm[0]; DB_Row& dbm_v = dbm[v]; PPL_DIRTY_TEMP_COEFFICIENT(minus_den); neg_assign(minus_den, 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_den); break; default: // We already dealt with the other cases. throw std::runtime_error("PPL internal error"); } PPL_ASSERT(OK()); return; } if (t == 1) { // Value of the one and only non-zero coefficient in `expr'. const Coefficient& a = expr.coefficient(Variable(w-1)); if (a == denominator || a == minus_den) { // 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_den); 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. throw std::runtime_error("PPL internal error"); } 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_den = is_sc ? denominator : minus_den; const Coefficient& minus_sc_den = is_sc ? minus_den : 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'. for (dimension_type i = w; i > 0; --i) { const Coefficient& sc_i = sc_expr.coefficient(Variable(i-1)); const int sign_i = sgn(sc_i); if (sign_i == 0) continue; // Choose carefully: we are approximating `sc_expr'. const N& approx_i = (sign_i > 0) ? dbm_0[i] : dbm[i][0]; if (is_plus_infinity(approx_i)) { if (++pinf_count > 1) break; pinf_index = i; 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_den != 1) { // Before computing the quotient, the denominator should be approximated // towards zero. Since `sc_den' is known to be positive, this amounts to // rounding downwards, which is achieved as usual by rounding upwards // `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(sum, sum, down_sc_den, 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_den, sum); } else if (pinf_count == 1) if (pinf_index != v && expr.coefficient(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 (dimension_type i = expr_space_dim + 1; i > 0; --i) { const Coefficient& sc_i = sc_expr.coefficient(Variable(i-1)); const int sign_i = sgn(sc_i); if (sign_i == 0) continue; // Choose carefully: we are approximating `-sc_expr'. const N& approx_i = (sign_i > 0) ? dbm[i][0] : dbm_0[i]; if (is_plus_infinity(approx_i)) { if (++pinf_count > 1) break; pinf_index = i; 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_den != 1) { // Before computing the quotient, the denominator should be approximated // towards zero. Since `sc_den' is known to be positive, this amounts to // rounding downwards, which is achieved as usual by rounding upwards // `minus_sc_den' and negating again the result. PPL_DIRTY_TEMP(N, down_sc_den); assign_r(down_sc_den, minus_sc_den, ROUND_UP); neg_assign_r(down_sc_den, down_sc_den, ROUND_UP); div_assign_r(sum, sum, down_sc_den, 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_den, sum); } else if (pinf_count == 1) if (pinf_index != v && expr.coefficient(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. throw std::runtime_error("PPL internal error"); } PPL_ASSERT(OK()); } template void BD_Shape::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_generic("generalized_affine_image(e1, r, e2)", "r is a strict relation symbol and " "*this is a BD_Shape"); // The relation symbol cannot be a disequality. if (relsym == NOT_EQUAL) throw_generic("generalized_affine_image(e1, r, e2)", "r is the disequality relation symbol and " "*this is a BD_Shape"); // 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 = 0; // Compute the number of the non-zero components of `lhs'. for (dimension_type i = lhs_space_dim; i-- > 0; ) if (lhs.coefficient(Variable(i)) != 0) { if (t_lhs++ == 1) break; else j_lhs = i; } 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. throw std::runtime_error("PPL internal error"); } } 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& den = lhs.coefficient(v); Relation_Symbol new_relsym = relsym; if (den < 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, den); } else { // Here `lhs' is of the general form, having at least two variables. // Compute the set of variables occurring in `lhs'. bool lhs_vars_intersects_rhs_vars = false; std::vector lhs_vars; for (dimension_type i = lhs_space_dim; i-- > 0; ) if (lhs.coefficient(Variable(i)) != 0) { lhs_vars.push_back(Variable(i)); if (rhs.coefficient(Variable(i)) != 0) lhs_vars_intersects_rhs_vars = true; } if (!lhs_vars_intersects_rhs_vars) { // `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. throw std::runtime_error("PPL internal error"); } } 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 = Variable(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. throw std::runtime_error("PPL internal error"); } // Remove the temporarily added dimension. remove_higher_space_dimensions(space_dim-1); #endif // Currently unnecessarily complex computation. } } PPL_ASSERT(OK()); } template void BD_Shape::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_generic("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_generic("generalized_affine_preimage(v, r, e, d)", "r is a strict relation symbol and " "*this is a BD_Shape"); // The relation symbol cannot be a disequality. if (relsym == NOT_EQUAL) throw_generic("generalized_affine_preimage(v, r, e, d)", "r is the disequality relation symbol and " "*this is a BD_Shape"); 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_den); neg_assign(inverse_den, expr_v); const Relation_Symbol inverse_relsym = (sgn(denominator) == sgn(inverse_den)) ? relsym : reversed_relsym; generalized_affine_image(var, inverse_relsym, inverse, inverse_den); 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 void BD_Shape::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_generic("generalized_affine_preimage(e1, r, e2)", "r is a strict relation symbol and " "*this is a BD_Shape"); // The relation symbol cannot be a disequality. if (relsym == NOT_EQUAL) throw_generic("generalized_affine_preimage(e1, r, e2)", "r is the disequality relation symbol and " "*this is a BD_Shape"); // 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 = 0; // Compute the number of the non-zero components of `lhs'. for (dimension_type i = lhs_space_dim; i-- > 0; ) if (lhs.coefficient(Variable(i)) != 0) { if (t_lhs++ == 1) break; else j_lhs = i; } 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& den = lhs.coefficient(v); Relation_Symbol new_relsym = relsym; if (den < 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, den); } else { // Here `lhs' is of the general form, having at least two variables. // Compute the set of variables occurring in `lhs'. bool lhs_vars_intersects_rhs_vars = false; std::vector lhs_vars; for (dimension_type i = lhs_space_dim; i-- > 0; ) if (lhs.coefficient(Variable(i)) != 0) { lhs_vars.push_back(Variable(i)); if (rhs.coefficient(Variable(i)) != 0) lhs_vars_intersects_rhs_vars = true; } if (!lhs_vars_intersects_rhs_vars) { // `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. throw std::runtime_error("PPL internal error"); } // 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 = Variable(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. throw std::runtime_error("PPL internal error"); } // Remove the temporarily added dimension. remove_higher_space_dimensions(bds_space_dim); } } PPL_ASSERT(OK()); } template Constraint_System BD_Shape::constraints() const { Constraint_System cs; const dimension_type space_dim = space_dimension(); if (space_dim == 0) { if (marked_empty()) cs = Constraint_System::zero_dim_empty(); } else if (marked_empty()) cs.insert(0*Variable(space_dim-1) <= -1); else if (marked_shortest_path_reduced()) // Disregard redundant constraints. cs = minimized_constraints(); else { // KLUDGE: in the future `cs' will be constructed of the right dimension. // For the time being, we force the dimension with the following line. cs.insert(0*Variable(space_dim-1) <= 0); PPL_DIRTY_TEMP_COEFFICIENT(a); PPL_DIRTY_TEMP_COEFFICIENT(b); // Go through all the unary constraints in `dbm'. const DB_Row& 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& 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 Constraint_System BD_Shape::minimized_constraints() const { shortest_path_reduction_assign(); Constraint_System cs; const dimension_type space_dim = space_dimension(); if (space_dim == 0) { if (marked_empty()) cs = Constraint_System::zero_dim_empty(); } else if (marked_empty()) cs.insert(0*Variable(space_dim-1) <= -1); else { // KLUDGE: in the future `cs' will be constructed of the right dimension. // For the time being, we force the dimension with the following line. cs.insert(0*Variable(space_dim-1) <= 0); PPL_DIRTY_TEMP_COEFFICIENT(num); PPL_DIRTY_TEMP_COEFFICIENT(den); // Compute leader information. std::vector leaders; compute_leaders(leaders); std::vector 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& 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], num, den); cs.insert(den*Variable(i-1) == num); } else { // A binary equality has to be generated. PPL_ASSERT(!is_plus_infinity(dbm[i][leader])); numer_denom(dbm[i][leader], num, den); cs.insert(den*Variable(leader-1) - den*Variable(i-1) == num); } } } // 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], num, den); cs.insert(den*Variable(i-1) <= num); } if (!redundancy_dbm[i][0]) { numer_denom(dbm[i][0], num, den); cs.insert(-den*Variable(i-1) <= num); } } // 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& 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], num, den); cs.insert(den*Variable(j-1) - den*Variable(i-1) <= num); } if (!redundancy_dbm[j][i]) { numer_denom(dbm[j][i], num, den); cs.insert(den*Variable(i-1) - den*Variable(j-1) <= num); } } } } return cs; } template void BD_Shape::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_generic("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& dbm_v = dbm[v_id]; for (dimension_type i = old_dim + 1; i-- > 0; ) { DB_Row& 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 void BD_Shape::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_generic("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& dbm_v = dbm[v_id]; for (Variables_Set::const_iterator i = vars.begin(), vs_end = vars.end(); i != vs_end; ++i) { const dimension_type tbf_id = *i + 1; const DB_Row& dbm_tbf = dbm[tbf_id]; for (dimension_type j = space_dim + 1; j-- > 0; ) { max_assign(dbm[j][v_id], dbm[j][tbf_id]); max_assign(dbm_v[j], dbm_tbf[j]); } } } remove_space_dimensions(vars); } template void BD_Shape::drop_some_non_integer_points(Complexity_Class) { if (std::numeric_limits::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& 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 void BD_Shape::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::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& 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& 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 std::ostream& IO_Operators::operator<<(std::ostream& s, const BD_Shape& c) { typedef typename BD_Shape::coefficient_type N; if (c.is_universe()) s << "true"; else { // We control empty bounded difference shape. dimension_type n = c.space_dimension(); if (c.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 = c.dbm[i][j]; const N& c_j_i = c.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 void BD_Shape::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) template bool BD_Shape::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 memory_size_type BD_Shape::external_memory_in_bytes() const { return dbm.external_memory_in_bytes() + redundancy_dbm.external_memory_in_bytes(); } template bool BD_Shape::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::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 void BD_Shape::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 void BD_Shape::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 void BD_Shape::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 void BD_Shape::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 void BD_Shape::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 void BD_Shape::throw_expression_too_complex(const char* method, const Linear_Expression& e) { using namespace IO_Operators; std::ostringstream s; s << "PPL::BD_Shape::" << method << ":" << std::endl << e << " is too complex."; throw std::invalid_argument(s.str()); } template void BD_Shape::throw_dimension_incompatible(const char* method, const char* name_row, const Linear_Expression& y) const { std::ostringstream s; s << "PPL::BD_Shape::" << method << ":" << std::endl << "this->space_dimension() == " << space_dimension() << ", " << name_row << "->space_dimension() == " << y.space_dimension() << "."; throw std::invalid_argument(s.str()); } template void BD_Shape::throw_generic(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 2173. */ /* Automatically generated from PPL source file ../src/Box.templates.hh line 40. */ #include #include #include namespace Parma_Polyhedra_Library { template inline Box::Box(dimension_type num_dimensions, Degenerate_Element kind) : seq(num_dimensions <= max_space_dimension() ? num_dimensions : (throw_space_dimension_overflow("Box(n, k)", "n exceeds the maximum " "allowed space dimension"), num_dimensions)), 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 inline Box::Box(const Constraint_System& cs) : seq(cs.space_dimension() <= max_space_dimension() ? cs.space_dimension() : (throw_space_dimension_overflow("Box(cs)", "cs exceeds the maximum " "allowed space dimension"), cs.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 inline Box::Box(const Congruence_System& cgs) : seq(cgs.space_dimension() <= max_space_dimension() ? cgs.space_dimension() : (throw_space_dimension_overflow("Box(cgs)", "cgs exceeds the maximum " "allowed space dimension"), cgs.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 template inline Box::Box(const Box& 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 Box::Box(const Generator_System& gs) : seq(gs.space_dimension() <= max_space_dimension() ? gs.space_dimension() : (throw_space_dimension_overflow("Box(gs)", "gs exceeds the maximum " "allowed space dimension"), 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 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. 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; 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::Box(gs):\n" "the non-empty generator system gs " "contains no points."); // Going through all the lines, rays and closure points. ITV q_interval; 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 (dimension_type i = space_dim; i-- > 0; ) if (g.coefficient(Variable(i)) != 0) seq[i].assign(UNIVERSE); break; case Generator::RAY: for (dimension_type i = space_dim; i-- > 0; ) switch (sgn(g.coefficient(Variable(i)))) { case 1: seq[i].upper_extend(); break; case -1: seq[i].lower_extend(); break; default: break; } break; case Generator::CLOSURE_POINT: { const Coefficient& d = g.divisor(); 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 template Box::Box(const BD_Shape& bds, Complexity_Class) : seq(bds.space_dimension() <= max_space_dimension() ? bds.space_dimension() : (throw_space_dimension_overflow("Box(bds)", "bds exceeds the maximum " "allowed space dimension"), bds.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::coefficient_type Coeff; PPL_DIRTY_TEMP(Coeff, tmp); const DB_Row& dbm_0 = bds.dbm[0]; for (dimension_type i = space_dim; i-- > 0; ) { I_Constraint lower; I_Constraint 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 template Box::Box(const Octagonal_Shape& oct, Complexity_Class) : seq(oct.space_dimension() <= max_space_dimension() ? oct.space_dimension() : (throw_space_dimension_overflow("Box(oct)", "oct exceeds the maximum " "allowed space dimension"), oct.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_TEMP0(mpq_class, lbound); PPL_DIRTY_TEMP0(mpq_class, ubound); for (dimension_type i = space_dim; i-- > 0; ) { typedef typename Octagonal_Shape::coefficient_type Coeff; I_Constraint lower; I_Constraint 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(ubound, twice_ub, ROUND_NOT_NEEDED); div_2exp_assign_r(ubound, ubound, 1, ROUND_NOT_NEEDED); upper.set(LESS_OR_EQUAL, ubound); } // Set the lower bound. const Coeff& twice_lb = oct.matrix[ii][cii]; if (!is_plus_infinity(twice_lb)) { assign_r(lbound, twice_lb, ROUND_NOT_NEEDED); neg_assign_r(lbound, lbound, ROUND_NOT_NEEDED); div_2exp_assign_r(lbound, lbound, 1, ROUND_NOT_NEEDED); lower.set(GREATER_OR_EQUAL, lbound); } seq_i.build(lower, upper); } } template Box::Box(const Polyhedron& ph, Complexity_Class complexity) : seq(ph.space_dimension() <= max_space_dimension() ? ph.space_dimension() : (throw_space_dimension_overflow("Box(ph)", "ph exceeds the maximum " "allowed space dimension"), ph.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()); 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. 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()) lp.add_constraint(Linear_Expression(c) >= 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_TEMP0(mpq_class, lbound); PPL_DIRTY_TEMP0(mpq_class, ubound); PPL_DIRTY_TEMP(Coefficient, bound_num); PPL_DIRTY_TEMP(Coefficient, bound_den); for (dimension_type i = space_dim; i-- > 0; ) { I_Constraint lower; I_Constraint 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_num, bound_den); assign_r(ubound.get_num(), bound_num, ROUND_NOT_NEEDED); assign_r(ubound.get_den(), bound_den, ROUND_NOT_NEEDED); PPL_ASSERT(is_canonical(ubound)); upper.set(LESS_OR_EQUAL, ubound); } // 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_num, bound_den); assign_r(lbound.get_num(), bound_num, ROUND_NOT_NEEDED); assign_r(lbound.get_den(), bound_den, ROUND_NOT_NEEDED); PPL_ASSERT(is_canonical(lbound)); lower.set(GREATER_OR_EQUAL, lbound); } seq_i.build(lower, upper); } } else { PPL_ASSERT(complexity == ANY_COMPLEXITY); if (ph.is_empty()) set_empty(); else { Box tmp(ph.generators()); swap(tmp); } } } template Box::Box(const Grid& gr, Complexity_Class) : seq(gr.space_dimension() <= max_space_dimension() ? gr.space_dimension() : (throw_space_dimension_overflow("Box(gr)", "gr exceeds the maximum " "allowed space dimension"), gr.space_dimension())), status() { // FIXME: here we are not taking advantage of intervals with restrictions! 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_TEMP0(mpq_class, bound); PPL_DIRTY_TEMP(Coefficient, bound_num); PPL_DIRTY_TEMP(Coefficient, bound_den); for (dimension_type i = space_dim; i-- > 0; ) { ITV& seq_i = seq[i]; Variable var(i); bool max; if (gr.maximize(var, bound_num, bound_den, max)) { assign_r(bound.get_num(), bound_num, ROUND_NOT_NEEDED); assign_r(bound.get_den(), bound_den, ROUND_NOT_NEEDED); bound.canonicalize(); seq_i.build(i_constraint(EQUAL, bound)); } else seq_i.assign(UNIVERSE); } } template template Box::Box(const Partially_Reduced_Product& dp, Complexity_Class complexity) : seq(), status() { if (dp.space_dimension() > max_space_dimension()) throw_space_dimension_overflow("Box(dp)", "dp exceeds the maximum " "allowed space dimension"); Box tmp1(dp.domain1(), complexity); Box tmp2(dp.domain2(), complexity); tmp1.intersection_assign(tmp2); swap(tmp1); } template inline void Box::add_space_dimensions_and_embed(const dimension_type m) { // Adding no dimensions is a no-op. if (m == 0) return; // 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 inline void Box::add_space_dimensions_and_project(const dimension_type m) { // Adding no dimensions is a no-op. if (m == 0) return; // Add `m' new zero elements to the sequence. seq.insert(seq.end(), m, ITV(0)); PPL_ASSERT(OK()); } template bool operator==(const Box& x, const Box& 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 bool Box::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; for (dimension_type i = expr_space_dim; i-- > 0; ) switch (sgn(expr.coefficient(Variable(i))) * from_above_sign) { case 1: if (seq[i].upper_is_boundary_infinity()) return false; break; case 0: // Nothing to do. break; case -1: if (seq[i].lower_is_boundary_infinity()) return false; break; } return true; } template Poly_Con_Relation interval_relation(const ITV& i, const Constraint::Type constraint_type, Coefficient_traits::const_reference num, Coefficient_traits::const_reference den) { if (i.is_universe()) return Poly_Con_Relation::strictly_intersects(); PPL_DIRTY_TEMP0(mpq_class, bound); assign_r(bound.get_num(), num, ROUND_NOT_NEEDED); assign_r(bound.get_den(), den, ROUND_NOT_NEEDED); bound.canonicalize(); neg_assign_r(bound, bound, ROUND_NOT_NEEDED); const bool is_lower_bound = (den > 0); PPL_DIRTY_TEMP0(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. throw std::runtime_error("PPL internal error"); } template Poly_Con_Relation Box::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_TEMP0(Rational_Interval, r); PPL_DIRTY_TEMP0(Rational_Interval, t); PPL_DIRTY_TEMP0(mpq_class, m); r = 0; for (dimension_type i = cg.space_dimension(); i-- > 0; ) { const Coefficient& cg_i = cg.coefficient(Variable(i)); if (sgn(cg_i) != 0) { assign_r(m, cg_i, ROUND_NOT_NEEDED); // FIXME: an add_mul_assign() method would come handy here. t.build(seq[i].lower_constraint(), seq[i].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 Poly_Con_Relation Box::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 (extract_interval_constraint(c, c_space_dim, 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_TEMP0(Rational_Interval, r); PPL_DIRTY_TEMP0(Rational_Interval, t); PPL_DIRTY_TEMP0(mpq_class, m); r = 0; for (dimension_type i = c.space_dimension(); i-- > 0; ) { const Coefficient& c_i = c.coefficient(Variable(i)); if (sgn(c_i) != 0) { assign_r(m, c_i, ROUND_NOT_NEEDED); // FIXME: an add_mul_assign() method would come handy here. t.build(seq[i].lower_constraint(), seq[i].upper_constraint()); t *= m; r += t; } } return interval_relation(r, c.type(), c.inhomogeneous_term()); } // Quiet a compiler warning: this program point is unreachable. throw std::runtime_error("PPL internal error"); } template Poly_Gen_Relation Box::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()) { for (dimension_type i = g_space_dim; i-- > 0; ) if (g.coefficient(Variable(i)) != 0 && !seq[i].is_universe()) return Poly_Gen_Relation::nothing(); return Poly_Gen_Relation::subsumes(); } else { PPL_ASSERT(g.is_ray()); for (dimension_type i = g_space_dim; i-- > 0; ) switch (sgn(g.coefficient(Variable(i)))) { case 1: if (!seq[i].upper_is_boundary_infinity()) return Poly_Gen_Relation::nothing(); break; case 0: break; case -1: if (!seq[i].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_TEMP0(mpq_class, g_coord); PPL_DIRTY_TEMP0(mpq_class, bound); 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 bool Box::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_TEMP0(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_TEMP0(mpq_class, bound_i); PPL_DIRTY_TEMP0(mpq_class, expr_i); for (dimension_type i = expr_space_dim; i-- > 0; ) { const ITV& seq_i = seq[i]; assign_r(expr_i, expr.coefficient(Variable(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: // Nothing to do. 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 bool Box::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_TEMP0(mpq_class, g_coord); PPL_DIRTY_TEMP(Coefficient, num); PPL_DIRTY_TEMP(Coefficient, den); PPL_DIRTY_TEMP(Coefficient, lcm); PPL_DIRTY_TEMP(Coefficient, factor); 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_TEMP0(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(den, g_coord.get_den(), ROUND_NOT_NEEDED); lcm_assign(lcm, g_divisor, den); exact_div_assign(factor, lcm, g_divisor); g_expr *= factor; exact_div_assign(factor, lcm, den); assign_r(num, g_coord.get_num(), ROUND_NOT_NEEDED); num *= factor; g_expr += num * Variable(i); g_divisor = lcm; } g = Generator::point(g_expr, g_divisor); return true; } template bool Box::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 bool Box::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 inline bool Box::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.marked_empty()) return true; if (x.marked_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 bool Box::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 dimension_type Box::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 bool Box::check_empty() const { PPL_ASSERT(!marked_empty()); Box& x = const_cast&>(*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 bool Box::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 bool Box::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 bool Box::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 bool Box::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 bool Box::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 bool Box::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 = 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(coeff); PPL_DIRTY_TEMP_COEFFICIENT(num); PPL_DIRTY_TEMP_COEFFICIENT(den); PPL_DIRTY_TEMP0(mpq_class, tmp); Linear_Expression le = expr; PPL_DIRTY_TEMP_COEFFICIENT(val_den); val_den = 1; for (dimension_type i = space_dim; i-- > 0; ) { const Variable v(i); coeff = le.coefficient(v); if (coeff == 0) { continue; } const ITV& seq_i = seq[i]; // 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); num = tmp.get_num(); den = tmp.get_den(); le -= coeff*v; le *= den; le += num*coeff; val_den *= den; 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(le.inhomogeneous_term(), val_den, val_n, val_d); return true; } template bool Box::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 void Box::unconstrain(const Variables_Set& vars) { // The cylindrification wrt 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 void Box::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 void Box::wrap_assign(const Variables_Set& vars, Bounded_Integer_Type_Width w, Bounded_Integer_Type_Representation r, Bounded_Integer_Type_Overflow o, const Constraint_System* pcs, unsigned complexity_threshold, bool wrap_individually) { #if 0 // Generic implementation commented out. Implementation::wrap_assign(*this, vars, w, r, o, pcs, complexity_threshold, wrap_individually, "Box"); #else // Specialized implementation. used(wrap_individually); used(complexity_threshold); Box& x = *this; // Dimension-compatibility check for `*pcs', if any. const dimension_type vars_space_dim = vars.space_dimension(); if (pcs != 0 && pcs->space_dimension() > vars_space_dim) { std::ostringstream s; s << "PPL::Box::wrap_assign(vars, w, r, o, pcs, ...):" << std::endl << "vars.space_dimension() == " << vars_space_dim << ", pcs->space_dimension() == " << pcs->space_dimension() << "."; throw std::invalid_argument(s.str()); } // Wrapping no variable only requires refining with *pcs, if any. if (vars.empty()) { if (pcs != 0) refine_with_constraints(*pcs); 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::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 lower = i_constraint(GREATER_OR_EQUAL, min_value); I_Constraint 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 (pcs == 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(pcs != 0); const Constraint_System& cs = *pcs; const dimension_type cs_space_dim = cs.space_dimension(); // A map associating interval constraints to variable indexes. typedef std::map > 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 (extract_interval_constraint(c, cs_space_dim, 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 void Box::drop_some_non_integer_points(Complexity_Class) { if (std::numeric_limits::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 void Box::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::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 void Box::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 void Box::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.marked_empty()) return; if (x.marked_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 void Box::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; // 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(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 void Box::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 bool Box::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. dimension_type i = 0; for ( ; 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 void Box::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 inline void Box::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) seq[dst++].swap(seq[src++]); ++src; } // Moving the remaining intervals. while (src < old_space_dim) seq[dst++].swap(seq[src++]); PPL_ASSERT(dst == new_space_dim); seq.resize(new_space_dim); PPL_ASSERT(OK()); } template void Box::remove_higher_space_dimensions(const dimension_type new_dim) { // Dimension-compatibility check: the variable having // maximum index is the one occurring last in the set. const dimension_type old_dim = space_dimension(); if (new_dim > old_dim) throw_dimension_incompatible("remove_higher_space_dimensions(nd)", new_dim); // 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_dim == old_dim) { PPL_ASSERT(OK()); return; } seq.erase(seq.begin() + new_dim, seq.end()); PPL_ASSERT(OK()); } template template void Box::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 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)) seq[i].swap(tmp.seq[new_i]); } swap(tmp); PPL_ASSERT(OK()); } template void Box::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_generic("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 void Box::add_constraint_no_check(const Constraint& c) { const dimension_type c_space_dim = c.space_dimension(); PPL_ASSERT(c_space_dim <= 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 (!extract_interval_constraint(c, c_space_dim, c_num_vars, c_only_var)) throw_generic("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_generic("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 void Box::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 void Box::add_congruence_no_check(const Congruence& cg) { const dimension_type cg_space_dim = cg.space_dimension(); PPL_ASSERT(cg_space_dim <= 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 // FIXME: what about intervals with restrictions? throw_generic("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 (!extract_interval_congruence(cg, cg_space_dim, cg_num_vars, cg_only_var)) throw_generic("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 void Box::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 void Box::refine_no_check(const Constraint& c) { const dimension_type c_space_dim = c.space_dimension(); 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 (!extract_interval_constraint(c, c_space_dim, 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 void Box::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 void Box::refine_no_check(const Congruence& cg) { PPL_ASSERT(!marked_empty()); PPL_ASSERT(cg.space_dimension() <= space_dimension()); if (cg.is_proper_congruence()) { // FIXME: also deal with the case of interval with restrictions. // 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 void Box::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 { 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_ASSERT(false); return true; } } } // namespace template void Box::propagate_constraint_no_check(const Constraint& c) { PPL_ASSERT(c.space_dimension() <= space_dimension()); typedef typename Select_Temp_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). dimension_type last_k = c_space_dim; for (dimension_type k = c_space_dim; k-- > 0; ) { if (c.coefficient(Variable(k)) != 0) { last_k = k; break; } } if (last_k == c_space_dim) { // 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); Result r; Temp_Boundary_Type t_bound; Temp_Boundary_Type t_a; Temp_Boundary_Type t_x; Ternary open; for (dimension_type k = last_k+1; k-- > 0; ) { const Coefficient& a_k = c.coefficient(Variable(k)); int sgn_a_k = sgn(a_k); if (sgn_a_k == 0) continue; if (sgn_a_k > 0) { open = (c_type == Constraint::STRICT_INEQUALITY) ? T_YES : T_NO; if (open == T_NO) maybe_reset_fpu_inexact(); 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 (dimension_type i = last_k+1; i-- > 0; ) { if (i == k) continue; const Coefficient& a_i = c.coefficient(Variable(i)); int sgn_a_i = sgn(a_i); if (sgn_a_i == 0) continue; ITV& x_i = seq[i]; 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() == 1) open = T_YES; seq[k].add_constraint(i_constraint(open == T_YES ? GREATER_THAN : GREATER_OR_EQUAL, t_bound)); reset_empty_up_to_date(); maybe_refine_upper_1: if (c_type != Constraint::EQUALITY) continue; open = T_NO; maybe_reset_fpu_inexact(); 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 (dimension_type i = c_space_dim; i-- > 0; ) { if (i == k) continue; const Coefficient& a_i = c.coefficient(Variable(i)); int sgn_a_i = sgn(a_i); if (sgn_a_i == 0) continue; ITV& x_i = seq[i]; 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() == 1) open = T_YES; seq[k].add_constraint(i_constraint(open == T_YES ? LESS_THAN : LESS_OR_EQUAL, 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(); 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 (dimension_type i = c_space_dim; i-- > 0; ) { if (i == k) continue; const Coefficient& a_i = c.coefficient(Variable(i)); int sgn_a_i = sgn(a_i); if (sgn_a_i == 0) continue; ITV& x_i = seq[i]; 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() == 1) open = T_YES; seq[k].add_constraint(i_constraint(open == T_YES ? LESS_THAN : LESS_OR_EQUAL, t_bound)); reset_empty_up_to_date(); maybe_refine_upper_2: if (c_type != Constraint::EQUALITY) continue; open = T_NO; maybe_reset_fpu_inexact(); 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 (dimension_type i = c_space_dim; i-- > 0; ) { if (i == k) continue; const Coefficient& a_i = c.coefficient(Variable(i)); int sgn_a_i = sgn(a_i); if (sgn_a_i == 0) continue; ITV& x_i = seq[i]; 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() == 1) open = T_YES; seq[k].add_constraint(i_constraint(open == T_YES ? GREATER_THAN : GREATER_OR_EQUAL, t_bound)); reset_empty_up_to_date(); } next_k: ; } } #else // Alternative implementations for propagate_constraint_no_check. template void Box::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 (dimension_type i = c_space_dim; i-- > 0; ) { k[i] = c.coefficient(Variable(i)); ITV& p_i = p[i]; p_i = seq[i]; p_i.mul_assign(p_i, k[i]); } const Coefficient& inhomogeneous_term = c.inhomogeneous_term(); for (dimension_type i = c_space_dim; i-- > 0; ) { int sgn_coefficient_i = sgn(c.coefficient(Variable(i))); if (sgn_coefficient_i == 0) continue; ITV q(inhomogeneous_term); for (dimension_type j = c_space_dim; j-- > 0; ) { if (i == j) continue; q.add_assign(q, p[j]); } q.div_assign(q, k[i]); 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].add_constraint(i_constraint(rel, q)); // FIXME: could/should we exploit the return value of add_constraint // in case it is available? // FIMXE: 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].is_empty()) { set_empty(); break; } } PPL_ASSERT(OK()); } #endif // Alternative implementations for propagate_constraint_no_check. template void Box ::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 cs_size = std::distance(cs_begin, cs_end); const dimension_type propagation_weight = cs_size * 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 void Box::affine_image(const Variable var, const Linear_Expression& expr, Coefficient_traits::const_reference denominator) { // The denominator cannot be zero. if (denominator == 0) throw_generic("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, temp0, temp1; expr_value.assign(expr.inhomogeneous_term()); for (dimension_type i = expr_space_dim; i-- > 0; ) { const Coefficient& coeff = expr.coefficient(Variable(i)); if (coeff != 0) { temp0.assign(coeff); temp1.assign(seq[i]); 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 void Box::affine_preimage(const Variable var, const Linear_Expression& expr, Coefficient_traits::const_reference denominator) { // The denominator cannot be zero. if (denominator == 0) throw_generic("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, temp0, temp1; expr_value.assign(expr.inhomogeneous_term()); for (dimension_type i = expr_space_dim; i-- > 0; ) { const Coefficient& coeff = expr.coefficient(Variable(i)); if (coeff != 0) { temp0.assign(coeff); temp1.assign(seq[i]); 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 void Box ::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_generic("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_num); PPL_DIRTY_TEMP(Coefficient, max_den); bool max_included; PPL_DIRTY_TEMP(Coefficient, min_num); PPL_DIRTY_TEMP(Coefficient, min_den); bool min_included; ITV& seq_v = seq[var.id()]; if (maximize(ub_expr, max_num, max_den, max_included)) { if (minimize(lb_expr, min_num, min_den, 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_den *= denominator; PPL_DIRTY_TEMP0(mpq_class, q1); PPL_DIRTY_TEMP0(mpq_class, q2); assign_r(q1.get_num(), min_num, ROUND_NOT_NEEDED); assign_r(q1.get_den(), min_den, 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_den *= denominator; assign_r(q2.get_num(), max_num, ROUND_NOT_NEEDED); assign_r(q2.get_den(), max_den, ROUND_NOT_NEEDED); q2.canonicalize(); if (denominator > 0) seq_v.build(i_constraint(min_included ? GREATER_OR_EQUAL : GREATER_THAN, q1), i_constraint(max_included ? LESS_OR_EQUAL : LESS_THAN, q2)); else seq_v.build(i_constraint(max_included ? GREATER_OR_EQUAL : GREATER_THAN, q2), i_constraint(min_included ? LESS_OR_EQUAL : LESS_THAN, 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_TEMP0(mpq_class, q); max_den *= denominator; assign_r(q.get_num(), max_num, ROUND_NOT_NEEDED); assign_r(q.get_den(), max_den, ROUND_NOT_NEEDED); q.canonicalize(); if (denominator > 0) seq_v.build(i_constraint(max_included ? LESS_OR_EQUAL : LESS_THAN, q)); else seq_v.build(i_constraint(max_included ? GREATER_OR_EQUAL : GREATER_THAN, q)); } } else if (minimize(lb_expr, min_num, min_den, 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_den *= denominator; PPL_DIRTY_TEMP0(mpq_class, q); assign_r(q.get_num(), min_num, ROUND_NOT_NEEDED); assign_r(q.get_den(), min_den, ROUND_NOT_NEEDED); q.canonicalize(); if (denominator > 0) seq_v.build(i_constraint(min_included ? GREATER_OR_EQUAL : GREATER_THAN, q)); else seq_v.build(i_constraint(min_included ? LESS_OR_EQUAL : LESS_THAN, 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 void Box ::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_generic("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)", "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)", "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_TEMP0(mpq_class, q_seq_var_lower); PPL_DIRTY_TEMP(Coefficient, num_lower); PPL_DIRTY_TEMP(Coefficient, den_lower); if (!unbounded_lower) { assign_r(q_seq_var_lower, seq_var.lower(), ROUND_NOT_NEEDED); assign_r(num_lower, q_seq_var_lower.get_num(), ROUND_NOT_NEEDED); assign_r(den_lower, q_seq_var_lower.get_den(), ROUND_NOT_NEEDED); if (negative_denom) neg_assign(den_lower, den_lower); num_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_TEMP0(mpq_class, q_seq_var_upper); PPL_DIRTY_TEMP(Coefficient, num_upper); PPL_DIRTY_TEMP(Coefficient, den_upper); if (!unbounded_upper) { assign_r(q_seq_var_upper, seq_var.upper(), ROUND_NOT_NEEDED); assign_r(num_upper, q_seq_var_upper.get_num(), ROUND_NOT_NEEDED); assign_r(den_upper, q_seq_var_upper.get_den(), ROUND_NOT_NEEDED); if (negative_denom) neg_assign(den_upper, den_upper); num_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, den_lower); revised_lb_expr *= d; revised_lb_expr += num_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, den); if (minimize(revised_lb_expr, num_lower, den, included)) { den_lower *= (den * ub_var_coeff); PPL_DIRTY_TEMP0(mpq_class, q); assign_r(q.get_num(), num_lower, ROUND_NOT_NEEDED); assign_r(q.get_den(), den_lower, ROUND_NOT_NEEDED); q.canonicalize(); open_lower |= !included; if ((ub_var_coeff >= 0) ? !negative_denom : negative_denom) seq_var.add_constraint(i_constraint(open_lower ? GREATER_THAN : GREATER_OR_EQUAL, q)); else seq_var.add_constraint(i_constraint(open_lower ? LESS_THAN : LESS_OR_EQUAL, 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, den_upper); revised_ub_expr *= d; revised_ub_expr += num_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, den); if (maximize(revised_ub_expr, num_upper, den, included)) { den_upper *= (den * lb_var_coeff); PPL_DIRTY_TEMP0(mpq_class, q); assign_r(q.get_num(), num_upper, ROUND_NOT_NEEDED); assign_r(q.get_den(), den_upper, ROUND_NOT_NEEDED); q.canonicalize(); open_upper |= !included; if ((lb_var_coeff >= 0) ? !negative_denom : negative_denom) seq_var.add_constraint(i_constraint(open_upper ? LESS_THAN : LESS_OR_EQUAL, q)); else seq_var.add_constraint(i_constraint(open_upper ? GREATER_THAN : GREATER_OR_EQUAL, 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 void Box ::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_generic("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_generic("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. throw std::runtime_error("PPL internal error"); } PPL_ASSERT(OK()); } template void Box ::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_generic("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_generic("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. throw std::runtime_error("PPL internal error"); } // 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_num); PPL_DIRTY_TEMP(Coefficient, max_den); bool max_included; bool bound_above = maximize(denominator*var, max_num, max_den, max_included); PPL_DIRTY_TEMP(Coefficient, min_num); PPL_DIRTY_TEMP(Coefficient, min_den); bool min_included; bool bound_below = minimize(denominator*var, min_num, min_den, 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/minimim value for `var'. PPL_DIRTY_TEMP(Linear_Expression, revised_expr); dimension_type dim = space_dim; PPL_DIRTY_TEMP_COEFFICIENT(d); if (corrected_relsym == LESS_THAN || corrected_relsym == LESS_OR_EQUAL) { if (bound_below) { for ( ; dim > 0; dim--) { d = min_den * expr.coefficient(Variable(dim - 1)); revised_expr += d * Variable(dim - 1); } } } else { if (bound_above) { for ( ; dim > 0; dim--) { d = max_den * expr.coefficient(Variable(dim - 1)); revised_expr += d * Variable(dim - 1); } } } switch (corrected_relsym) { case LESS_THAN: if (bound_below) refine_with_constraint(min_num < revised_expr); break; case LESS_OR_EQUAL: if (bound_below) (min_included) ? refine_with_constraint(min_num <= revised_expr) : refine_with_constraint(min_num < revised_expr); break; case GREATER_OR_EQUAL: if (bound_above) (max_included) ? refine_with_constraint(max_num >= revised_expr) : refine_with_constraint(max_num > revised_expr); break; case GREATER_THAN: if (bound_above) refine_with_constraint(max_num > revised_expr); break; default: // The EQUAL and NOT_EQUAL cases have been already dealt with. throw std::runtime_error("PPL internal error"); } // 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 void Box ::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_generic("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_num); PPL_DIRTY_TEMP(Coefficient, max_den); bool max_included; bool max_rhs = maximize(rhs, max_num, max_den, max_included); PPL_DIRTY_TEMP(Coefficient, min_num); PPL_DIRTY_TEMP(Coefficient, min_den); bool min_included; bool min_rhs = minimize(rhs, min_num, min_den, 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; bool has_more_than_one_var = false; // Initialization is just to avoid an annoying warning. dimension_type has_var_id = 0; for ( ; lhs_space_dim > 0; --lhs_space_dim) if (lhs.coefficient(Variable(lhs_space_dim - 1)) != 0) { if (has_var) { ITV& seq_i = seq[lhs_space_dim - 1]; seq_i.assign(UNIVERSE); has_more_than_one_var = true; } else { has_var = true; has_var_id = lhs_space_dim - 1; } } if (has_more_than_one_var) { // There is more than one dimension with non-zero coefficient, so // we cannot have any information about the dimensions in the lhs. // 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_var = seq[has_var_id]; seq_var.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_TEMP0(mpq_class, q_max); PPL_DIRTY_TEMP0(mpq_class, q_min); if (max_rhs) { max_num -= inhomo * max_den; max_den *= coeff; assign_r(q_max.get_num(), max_num, ROUND_NOT_NEEDED); assign_r(q_max.get_den(), max_den, ROUND_NOT_NEEDED); q_max.canonicalize(); } if (min_rhs) { min_num -= inhomo * min_den; min_den *= coeff; assign_r(q_min.get_num(), min_num, ROUND_NOT_NEEDED); assign_r(q_min.get_den(), min_den, 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 +ve. switch (relsym) { case LESS_OR_EQUAL: max_rhs ? seq_var.build(i_constraint(max_included ? LESS_OR_EQUAL : LESS_THAN, q_max)) : seq_var.assign(UNIVERSE); break; case LESS_THAN: max_rhs ? seq_var.build(i_constraint(LESS_THAN, q_max)) : seq_var.assign(UNIVERSE); break; case EQUAL: { I_Constraint l; I_Constraint 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: min_rhs ? seq_var.build(i_constraint(min_included ? GREATER_OR_EQUAL : GREATER_THAN, q_min)) : seq_var.assign(UNIVERSE); break; case GREATER_THAN: min_rhs ? seq_var.build(i_constraint(GREATER_THAN, q_min)) : seq_var.assign(UNIVERSE); break; default: // The NOT_EQUAL case has been already dealt with. throw std::runtime_error("PPL internal error"); } else // The coefficient of the dimension in the lhs is -ve. switch (relsym) { case GREATER_OR_EQUAL: min_rhs ? seq_var.build(i_constraint(min_included ? LESS_OR_EQUAL : LESS_THAN, q_min)) : seq_var.assign(UNIVERSE); break; case GREATER_THAN: min_rhs ? seq_var.build(i_constraint(LESS_THAN, q_min)) : seq_var.assign(UNIVERSE); break; case EQUAL: { I_Constraint l; I_Constraint 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: max_rhs ? seq_var.build(i_constraint(max_included ? GREATER_OR_EQUAL : GREATER_THAN, q_max)) : seq_var.assign(UNIVERSE); break; case LESS_THAN: max_rhs ? seq_var.build(i_constraint(GREATER_THAN, q_max)) : seq_var.assign(UNIVERSE); break; default: // The NOT_EQUAL case has been already dealt with. throw std::runtime_error("PPL internal error"); } } 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. throw std::runtime_error("PPL internal error"); } } PPL_ASSERT(OK()); } template void Box::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_generic("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 (dimension_type d = lhs_space_dim; d-- > 0; ) { const Variable& var = Variable(d); if (lhs.coefficient(var) != 0) { PPL_DIRTY_TEMP(Coefficient, temp); temp = rhs.coefficient(var) + lhs.coefficient(var); revised_rhs -= temp * var; revised_lhs -= temp * var; } } generalized_affine_image(revised_lhs, relsym, revised_rhs); PPL_ASSERT(OK()); } template template typename Enable_If >::value && Is_Same_Or_Derived::value, void>::type Box::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 template typename Enable_If >::value && Is_Same_Or_Derived::value, void>::type Box::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 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 void Box::get_limiting_box(const Constraint_System& cs, Box& limiting_box) 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_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 (!extract_interval_constraint(c, cs_space_dim, 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 void Box::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; #ifndef NDEBUG { // We assume that `y' is contained in or equal to `*this'. const Box x_copy = *this; const Box y_copy = y; PPL_ASSERT(x_copy.contains(y_copy)); } #endif // 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 template typename Enable_If >::value && Is_Same_Or_Derived::value, void>::type Box::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); #ifndef NDEBUG { // We assume that `*this' is contained in or equal to `y'. const Box x_copy = *this; const Box y_copy = y; PPL_ASSERT(y_copy.contains(x_copy)); } #endif // 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 Constraint_System Box::constraints() const { Constraint_System cs; const dimension_type space_dim = space_dimension(); if (space_dim == 0) { if (marked_empty()) cs = Constraint_System::zero_dim_empty(); } else if (marked_empty()) cs.insert(0*Variable(space_dim-1) <= -1); else { // KLUDGE: in the future `cs' will be constructed of the right dimension. // For the time being, we force the dimension with the following line. cs.insert(0*Variable(space_dim-1) <= 0); for (dimension_type k = 0; k < space_dim; ++k) { bool closed = false; PPL_DIRTY_TEMP(Coefficient, n); PPL_DIRTY_TEMP(Coefficient, d); if (get_lower_bound(k, closed, n, d)) { if (closed) cs.insert(d*Variable(k) >= n); else cs.insert(d*Variable(k) > n); } if (get_upper_bound(k, closed, n, d)) { if (closed) cs.insert(d*Variable(k) <= n); else cs.insert(d*Variable(k) < n); } } } return cs; } template Constraint_System Box::minimized_constraints() const { Constraint_System cs; const dimension_type space_dim = space_dimension(); if (space_dim == 0) { if (marked_empty()) cs = Constraint_System::zero_dim_empty(); } // Make sure emptiness is detected. else if (is_empty()) cs.insert(0*Variable(space_dim-1) <= -1); else { // KLUDGE: in the future `cs' will be constructed of the right dimension. // For the time being, we force the dimension with the following line. cs.insert(0*Variable(space_dim-1) <= 0); for (dimension_type k = 0; k < space_dim; ++k) { bool closed = false; PPL_DIRTY_TEMP(Coefficient, n); PPL_DIRTY_TEMP(Coefficient, d); if (get_lower_bound(k, closed, n, d)) { if (closed) // Make sure equality constraints are detected. if (seq[k].is_singleton()) { cs.insert(d*Variable(k) == n); continue; } else cs.insert(d*Variable(k) >= n); else cs.insert(d*Variable(k) > n); } if (get_upper_bound(k, closed, n, d)) { if (closed) cs.insert(d*Variable(k) <= n); else cs.insert(d*Variable(k) < n); } } } return cs; } template Congruence_System Box::congruences() const { Congruence_System cgs; const dimension_type space_dim = space_dimension(); if (space_dim == 0) { if (marked_empty()) cgs = Congruence_System::zero_dim_empty(); } // Make sure emptiness is detected. else if (is_empty()) cgs.insert((0*Variable(space_dim-1) %= -1) / 0); else { // KLUDGE: in the future `cgs' will be constructed of the right dimension. // For the time being, we force the dimension with the following line. cgs.insert(0*Variable(space_dim-1) %= 0); for (dimension_type k = 0; k < space_dim; ++k) { bool closed = false; PPL_DIRTY_TEMP(Coefficient, n); PPL_DIRTY_TEMP(Coefficient, d); if (get_lower_bound(k, closed, n, d) && closed) // Make sure equality congruences are detected. if (seq[k].is_singleton()) cgs.insert((d*Variable(k) %= n) / 0); } } return cgs; } template memory_size_type Box::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 std::ostream& IO_Operators::operator<<(std::ostream& s, const Box& 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 void Box::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) template bool Box::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 void Box::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 void Box ::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 void Box::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 void Box::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 void Box::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 void Box::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 void Box::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 void Box::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 void Box::throw_expression_too_complex(const char* method, const Linear_Expression& e) { using namespace IO_Operators; std::ostringstream s; s << "PPL::Box::" << method << ":" << std::endl << e << " is too complex."; throw std::invalid_argument(s.str()); } template void Box::throw_dimension_incompatible(const char* method, const char* name_row, const Linear_Expression& e) const { std::ostringstream s; s << "PPL::Box::" << method << ":" << std::endl << "this->space_dimension() == " << space_dimension() << ", " << name_row << "->space_dimension() == " << e.space_dimension() << "."; throw std::invalid_argument(s.str()); } template void Box::throw_generic(const char* method, const char* reason) { std::ostringstream s; s << "PPL::Box::" << method << ":" << std::endl << reason; throw std::invalid_argument(s.str()); } template void Box::throw_space_dimension_overflow(const char* method, const char* reason) { std::ostringstream s; s << "PPL::Box::" << method << ":" << std::endl << reason; throw std::length_error(s.str()); } #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \relates Box */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template bool l_m_distance_assign(Checked_Number& r, const Box& x, const Box& 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 2220. */ /* 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_Box; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/max_space_dimension.hh line 34. */ #include 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::max_space_dimension()); d = std::min(d, BD_Shape::max_space_dimension()); d = std::min(d, BD_Shape::max_space_dimension()); d = std::min(d, BD_Shape::max_space_dimension()); d = std::min(d, BD_Shape::max_space_dimension()); d = std::min(d, BD_Shape::max_space_dimension()); d = std::min(d, BD_Shape::max_space_dimension()); d = std::min(d, BD_Shape::max_space_dimension()); d = std::min(d, BD_Shape::max_space_dimension()); d = std::min(d, Octagonal_Shape::max_space_dimension()); d = std::min(d, Octagonal_Shape::max_space_dimension()); d = std::min(d, Octagonal_Shape::max_space_dimension()); d = std::min(d, Octagonal_Shape::max_space_dimension()); d = std::min(d, Octagonal_Shape::max_space_dimension()); d = std::min(d, Octagonal_Shape::max_space_dimension()); d = std::min(d, Octagonal_Shape::max_space_dimension()); d = std::min(d, Octagonal_Shape::max_space_dimension()); d = std::min(d, Octagonal_Shape::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 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 std::ostream& operator<<(std::ostream& s, const Partially_Reduced_Product& dp); } // namespace IO_Operators /*! \brief Returns true 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 false is returned. */ template bool operator==(const Partially_Reduced_Product& x, const Partially_Reduced_Product& y); /*! \brief Returns true 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 true is returned. */ template bool operator!=(const Partially_Reduced_Product& x, const Partially_Reduced_Product& 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 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 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 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 equalitites the shapes of the domains are unaltered. */ template 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 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: C_Polyhedron, NNC_Polyhedron, Grid, Octagonal_Shape, BD_Shape, Box. An object of the class Partially_Reduced_Product 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 No_Reduction class, then the class Partially_Reduced_Product 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 is_universe() on both components returns true. \par In all the examples it is assumed that the template \c R is the No_Reduction 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 > 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 > 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 > 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 > 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 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 Partially_Reduced_Product(const Box& 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 Partially_Reduced_Product(const BD_Shape& 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 Partially_Reduced_Product(const Octagonal_Shape& 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 explicit Partially_Reduced_Product(const Partially_Reduced_Product& 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 is_included() if and only if one or both of \p r1 and \p r2 imply is_included(); \p r implies saturates() if and only if one or both of \p r1 and \p r2 imply saturates(); \p r implies is_disjoint() if and only if one or both of \p r1 and \p r2 imply is_disjoint(); and \p r implies nothing() if and only if both \p r1 and \p r2 imply strictly_intersects(). */ 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 = subsumes() if and only if \p r1 = \p r2 = subsumes(); and \p r = nothing() if and only if one or both of \p r1 and \p r2 = nothing(); */ Poly_Gen_Relation relation_with(const Generator& g) const; /*! \brief Returns true if and only if either of the components of \p *this are empty. */ bool is_empty() const; /*! \brief Returns true if and only if both of the components of \p *this are the universe. */ bool is_universe() const; /*! \brief Returns true 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 true 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 true if and only if a component of \p *this is discrete. */ bool is_discrete() const; /*! \brief Returns true if and only if a component of \p *this is bounded. */ bool is_bounded() const; /*! \brief Returns true 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 true 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 true 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 true 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 true 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, false 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 true 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 true if the supremum value can be reached in \p this. \param point When maximization succeeds, will be assigned a generator 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, false 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 true 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 true 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, false 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 true 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 true if the infimum value can be reached in \p this. \param point When minimization succeeds, will be assigned a generator 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, false 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 true 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 true 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, true is returned, otherwise false 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 must 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 ANY_COMPLEXITY. */ 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 ANY_COMPLEXITY. */ 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 max_space_dimension(). */ 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 max_space_dimension(). */ 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 max_space_dimension(). */ 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 Partial_Function must provide the following methods. \code bool has_empty_codomain() const \endcode returns true if and only if the represented partial function has an empty codomain (i.e., it is always undefined). The has_empty_codomain() method will always be called before the methods below. However, if has_empty_codomain() returns true, 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 max_in_codomain() 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 true is returned. If \f$f\f$ is undefined in \f$k\f$, then false 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 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 max_space_dimension(). If \p *this has space dimension \f$n\f$, with \f$n > 0\f$, and var 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$, dest 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& x, const Partially_Reduced_Product& y); friend std::ostream& Parma_Polyhedra_Library::IO_Operators:: operator<<<>(std::ostream& s, const Partially_Reduced_Product& 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 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 true if successful, false 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 x == y, then x.hash_code() == y.hash_code(). */ int32_t hash_code() const; //@} // Miscellaneous Member Functions //! Reduce. /* \return true 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 true 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; }; 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 Domain_Product::Direct_Product will simply be denoted by Direct_Product. */ template class Domain_Product { public: typedef Partially_Reduced_Product > Direct_Product; typedef Partially_Reduced_Product > Smash_Product; typedef Partially_Reduced_Product > Constraints_Product; typedef Partially_Reduced_Product > Congruences_Product; typedef Partially_Reduced_Product > Shape_Preserving_Product; }; } // namespace Parma_Polyhedra_Library namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Partially_Reduced_Product */ template void swap(Parma_Polyhedra_Library::Partially_Reduced_Product& x, Parma_Polyhedra_Library::Partially_Reduced_Product& y); } // namespace std /* 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 inline dimension_type Partially_Reduced_Product::max_space_dimension() { return (D1::max_space_dimension() < D2::max_space_dimension()) ? D1::max_space_dimension() : D2::max_space_dimension(); } template inline Partially_Reduced_Product ::Partially_Reduced_Product(dimension_type num_dimensions, const Degenerate_Element kind) : d1(num_dimensions, kind), d2(num_dimensions, kind) { set_reduced_flag(); } template inline Partially_Reduced_Product ::Partially_Reduced_Product(const Congruence_System& ccgs) : d1(ccgs), d2(ccgs) { clear_reduced_flag(); } template inline Partially_Reduced_Product ::Partially_Reduced_Product(Congruence_System& cgs) : d1(const_cast(cgs)), d2(cgs) { clear_reduced_flag(); } template inline Partially_Reduced_Product ::Partially_Reduced_Product(const Constraint_System& ccs) : d1(ccs), d2(ccs) { clear_reduced_flag(); } template inline Partially_Reduced_Product ::Partially_Reduced_Product(Constraint_System& cs) : d1(const_cast(cs)), d2(cs) { clear_reduced_flag(); } template inline Partially_Reduced_Product ::Partially_Reduced_Product(const C_Polyhedron& ph, Complexity_Class complexity) : d1(ph, complexity), d2(ph, complexity) { set_reduced_flag(); } template inline Partially_Reduced_Product ::Partially_Reduced_Product(const NNC_Polyhedron& ph, Complexity_Class complexity) : d1(ph, complexity), d2(ph, complexity) { set_reduced_flag(); } template inline Partially_Reduced_Product ::Partially_Reduced_Product(const Grid& gr, Complexity_Class) : d1(gr), d2(gr) { set_reduced_flag(); } template template inline Partially_Reduced_Product ::Partially_Reduced_Product(const Box& box, Complexity_Class) : d1(box), d2(box) { set_reduced_flag(); } template template inline Partially_Reduced_Product ::Partially_Reduced_Product(const BD_Shape& bd, Complexity_Class) : d1(bd), d2(bd) { set_reduced_flag(); } template template inline Partially_Reduced_Product ::Partially_Reduced_Product(const Octagonal_Shape& os, Complexity_Class) : d1(os), d2(os) { set_reduced_flag(); } template inline Partially_Reduced_Product ::Partially_Reduced_Product(const Partially_Reduced_Product& y, Complexity_Class) : d1(y.d1), d2(y.d2) { reduced = y.reduced; } template template inline Partially_Reduced_Product ::Partially_Reduced_Product(const Partially_Reduced_Product& y, Complexity_Class complexity) : d1(y.space_dimension()), d2(y.space_dimension()) { Partially_Reduced_Product pg1(y.domain1(), complexity); Partially_Reduced_Product pg2(y.domain2(), complexity); pg1.intersection_assign(pg2); swap(pg1); /* Even if y is reduced, the built product may not be reduced as the reduction method may have changed (i.e., S != R). */ clear_reduced_flag(); } template inline Partially_Reduced_Product::~Partially_Reduced_Product() { } template inline memory_size_type Partially_Reduced_Product::external_memory_in_bytes() const { return d1.external_memory_in_bytes() + d2.external_memory_in_bytes(); } template inline memory_size_type Partially_Reduced_Product::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } template inline dimension_type Partially_Reduced_Product::space_dimension() const { PPL_ASSERT(d1.space_dimension() == d2.space_dimension()); return d1.space_dimension(); } template inline dimension_type Partially_Reduced_Product::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 inline void Partially_Reduced_Product ::unconstrain(const Variable var) { reduce(); d1.unconstrain(var); d2.unconstrain(var); } template inline void Partially_Reduced_Product::unconstrain(const Variables_Set& vars) { reduce(); d1.unconstrain(vars); d2.unconstrain(vars); } template inline void Partially_Reduced_Product ::intersection_assign(const Partially_Reduced_Product& y) { d1.intersection_assign(y.d1); d2.intersection_assign(y.d2); clear_reduced_flag(); } template inline void Partially_Reduced_Product ::difference_assign(const Partially_Reduced_Product& y) { reduce(); y.reduce(); d1.difference_assign(y.d1); d2.difference_assign(y.d2); clear_reduced_flag(); } template inline void Partially_Reduced_Product ::upper_bound_assign(const Partially_Reduced_Product& y) { reduce(); y.reduce(); d1.upper_bound_assign(y.d1); d2.upper_bound_assign(y.d2); } template inline bool Partially_Reduced_Product ::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; std::swap(d1,d1_copy); return true; } template inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product::topological_closure_assign() { d1.topological_closure_assign(); d2.topological_closure_assign(); } template inline void Partially_Reduced_Product::swap(Partially_Reduced_Product& y) { std::swap(d1, y.d1); std::swap(d2, y.d2); std::swap(reduced, y.reduced); } template inline void Partially_Reduced_Product::add_constraint(const Constraint& c) { d1.add_constraint(c); d2.add_constraint(c); clear_reduced_flag(); } template inline void Partially_Reduced_Product::refine_with_constraint(const Constraint& c) { d1.refine_with_constraint(c); d2.refine_with_constraint(c); clear_reduced_flag(); } template inline void Partially_Reduced_Product::add_congruence(const Congruence& cg) { d1.add_congruence(cg); d2.add_congruence(cg); clear_reduced_flag(); } template inline void Partially_Reduced_Product::refine_with_congruence(const Congruence& cg) { d1.refine_with_congruence(cg); d2.refine_with_congruence(cg); clear_reduced_flag(); } template inline void Partially_Reduced_Product ::add_constraints(const Constraint_System& cs) { d1.add_constraints(cs); d2.add_constraints(cs); clear_reduced_flag(); } template inline void Partially_Reduced_Product ::refine_with_constraints(const Constraint_System& cs) { d1.refine_with_constraints(cs); d2.refine_with_constraints(cs); clear_reduced_flag(); } template inline void Partially_Reduced_Product ::add_congruences(const Congruence_System& cgs) { d1.add_congruences(cgs); d2.add_congruences(cgs); clear_reduced_flag(); } template inline void Partially_Reduced_Product ::refine_with_congruences(const Congruence_System& cgs) { d1.refine_with_congruences(cgs); d2.refine_with_congruences(cgs); clear_reduced_flag(); } template inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product ::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 inline Partially_Reduced_Product& Partially_Reduced_Product ::operator=(const Partially_Reduced_Product& y) { d1 = y.d1; d2 = y.d2; reduced = y.reduced; return *this; } template inline const D1& Partially_Reduced_Product::domain1() const { reduce(); return d1; } template inline const D2& Partially_Reduced_Product::domain2() const { reduce(); return d2; } template inline bool Partially_Reduced_Product::is_empty() const { reduce(); return d1.is_empty() || d2.is_empty(); } template inline bool Partially_Reduced_Product::is_universe() const { return d1.is_universe() && d2.is_universe(); } template inline bool Partially_Reduced_Product::is_topologically_closed() const { reduce(); return d1.is_topologically_closed() && d2.is_topologically_closed(); } template inline bool Partially_Reduced_Product ::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 inline bool Partially_Reduced_Product::is_discrete() const { reduce(); return d1.is_discrete() || d2.is_discrete(); } template inline bool Partially_Reduced_Product::is_bounded() const { reduce(); return d1.is_bounded() || d2.is_bounded(); } template inline bool Partially_Reduced_Product ::bounds_from_above(const Linear_Expression& expr) const { reduce(); return d1.bounds_from_above(expr) || d2.bounds_from_above(expr); } template inline bool Partially_Reduced_Product ::bounds_from_below(const Linear_Expression& expr) const { reduce(); return d1.bounds_from_below(expr) || d2.bounds_from_below(expr); } template inline bool Partially_Reduced_Product::constrains(Variable var) const { reduce(); return d1.constrains(var) || d2.constrains(var); } template inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product ::add_space_dimensions_and_embed(dimension_type m) { d1.add_space_dimensions_and_embed(m); d2.add_space_dimensions_and_embed(m); } template inline void Partially_Reduced_Product ::add_space_dimensions_and_project(dimension_type m) { d1.add_space_dimensions_and_project(m); d2.add_space_dimensions_and_project(m); } template inline void Partially_Reduced_Product ::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 inline void Partially_Reduced_Product ::remove_space_dimensions(const Variables_Set& vars) { d1.remove_space_dimensions(vars); d2.remove_space_dimensions(vars); } template inline void Partially_Reduced_Product ::remove_higher_space_dimensions(dimension_type new_dimension) { d1.remove_higher_space_dimensions(new_dimension); d2.remove_higher_space_dimensions(new_dimension); } template template inline void Partially_Reduced_Product ::map_space_dimensions(const Partial_Function& pfunc) { d1.map_space_dimensions(pfunc); d2.map_space_dimensions(pfunc); } template inline void Partially_Reduced_Product ::expand_space_dimension(Variable var, dimension_type m) { d1.expand_space_dimension(var, m); d2.expand_space_dimension(var, m); } template inline void Partially_Reduced_Product ::fold_space_dimensions(const Variables_Set& vars, Variable dest) { d1.fold_space_dimensions(vars, dest); d2.fold_space_dimensions(vars, dest); } template inline bool Partially_Reduced_Product ::contains(const Partially_Reduced_Product& y) const { reduce(); y.reduce(); return d1.contains(y.d1) && d2.contains(y.d2); } template inline bool Partially_Reduced_Product ::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 inline bool Partially_Reduced_Product::reduce() const { Partially_Reduced_Product& dp = const_cast(*this); if (dp.is_reduced()) return false; R r; r.product_reduce(dp.d1, dp.d2); set_reduced_flag(); return true; } template inline bool Partially_Reduced_Product::is_reduced() const { return reduced; } template inline void Partially_Reduced_Product::clear_reduced_flag() const { const_cast(*this).reduced = false; } template inline void Partially_Reduced_Product::set_reduced_flag() const { const_cast(*this).reduced = true; } PPL_OUTPUT_3_PARAM_TEMPLATE_DEFINITIONS(D1, D2, R, Partially_Reduced_Product) template inline void Partially_Reduced_Product::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 inline int32_t Partially_Reduced_Product::hash_code() const { return space_dimension() & 0x7fffffff; } /*! \relates Parma_Polyhedra_Library::Partially_Reduced_Product */ template inline bool operator==(const Partially_Reduced_Product& x, const Partially_Reduced_Product& y) { x.reduce(); y.reduce(); return x.d1 == y.d1 && x.d2 == y.d2; } /*! \relates Parma_Polyhedra_Library::Partially_Reduced_Product */ template inline bool operator!=(const Partially_Reduced_Product& x, const Partially_Reduced_Product& y) { return !(x == y); } /*! \relates Parma_Polyhedra_Library::Partially_Reduced_Product */ template inline std::ostream& IO_Operators::operator<<(std::ostream& s, const Partially_Reduced_Product& pd) { return s << "Domain 1:\n" << pd.d1 << "Domain 2:\n" << pd.d2; } } // namespace Parma_Polyhedra_Library namespace Parma_Polyhedra_Library { template inline No_Reduction::No_Reduction() { } template void No_Reduction::product_reduce(D1&, D2&) { } template inline No_Reduction::~No_Reduction() { } template inline Smash_Reduction::Smash_Reduction() { } template inline Smash_Reduction::~Smash_Reduction() { } template inline Constraints_Reduction::Constraints_Reduction() { } template inline Constraints_Reduction::~Constraints_Reduction() { } template inline Congruences_Reduction::Congruences_Reduction() { } template inline Congruences_Reduction::~Congruences_Reduction() { } template inline Shape_Preserving_Reduction::Shape_Preserving_Reduction() { } template inline Shape_Preserving_Reduction::~Shape_Preserving_Reduction() { } } // namespace Parma_Polyhedra_Library /*! \relates Parma_Polyhedra_Library::Partially_Reduced_Product */ template inline void std::swap(Parma_Polyhedra_Library::Partially_Reduced_Product& x, Parma_Polyhedra_Library::Partially_Reduced_Product& y) { x.swap(y); } /* 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 #include namespace Parma_Polyhedra_Library { template Constraint_System Partially_Reduced_Product::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 Constraint_System Partially_Reduced_Product::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 Congruence_System Partially_Reduced_Product::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 Congruence_System Partially_Reduced_Product::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 void Partially_Reduced_Product ::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 void Partially_Reduced_Product ::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 Poly_Gen_Relation Partially_Reduced_Product ::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 Poly_Con_Relation Partially_Reduced_Product ::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 Poly_Con_Relation Partially_Reduced_Product ::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 bool Partially_Reduced_Product ::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 bool Partially_Reduced_Product ::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 bool Partially_Reduced_Product ::maximize(const Linear_Expression& expr, Coefficient& sup_n, Coefficient& sup_d, bool& maximum, Generator& pnt) 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 pnt1(point()); Generator pnt2(point()); bool r1 = d1.maximize(expr, sup1_n, sup1_d, maximum1, pnt1); bool r2 = d2.maximize(expr, sup2_n, sup2_d, maximum2, pnt2); // 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; pnt = pnt2; 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; pnt = pnt1; 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; pnt = pnt1; } else { sup_n = sup2_n; sup_d = sup2_d; maximum = maximum2; pnt = pnt2; } return true; } template bool Partially_Reduced_Product ::minimize(const Linear_Expression& expr, Coefficient& inf_n, Coefficient& inf_d, bool& minimum, Generator& pnt) 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 pnt1(point()); Generator pnt2(point()); bool r1 = d1.minimize(expr, inf1_n, inf1_d, minimum1, pnt1); bool r2 = d2.minimize(expr, inf2_n, inf2_d, minimum2, pnt2); // 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; pnt = pnt2; 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; pnt = pnt1; 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; pnt = pnt1; } else { inf_n = inf2_n; inf_d = inf2_d; minimum = minimum2; pnt = pnt2; } return true; } template inline bool Partially_Reduced_Product::OK() const { if (reduced) { Partially_Reduced_Product pd1 = *this; Partially_Reduced_Product pd2 = *this; /* Force pd1 reduction */ pd1.clear_reduced_flag(); pd1.reduce(); if (pd1 != pd2) return false; } return d1.OK() && d2.OK(); } template bool Partially_Reduced_Product::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); return ((s >> str) && str == "Domain" && (s >> str) && str == "1:" && d1.ascii_load(s) && (s >> str) && str == "Domain" && (s >> str) && str == "2:" && d2.ascii_load(s)); } template void Smash_Reduction::product_reduce(D1& d1, D2& d2) { if (d2.is_empty()) { if (!d1.is_empty()) { D1 new_d1(d1.space_dimension(), EMPTY); std::swap(d1, new_d1); } } else if (d1.is_empty()) { D2 new_d2(d2.space_dimension(), EMPTY); std::swap(d2, new_d2); } } template void Constraints_Reduction::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 sr; sr.product_reduce(d1, d2); return; } else { dimension_type space_dim = d1.space_dimension(); d1.refine_with_constraints(d2.minimized_constraints()); if (d1.is_empty()) { D2 new_d2(space_dim, EMPTY); std::swap(d2, new_d2); return; } d2.refine_with_constraints(d1.minimized_constraints()); if (d2.is_empty()) { D1 new_d1(space_dim, EMPTY); std::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 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()); // Build the linear expression for the congruence cg. Linear_Expression e; for (dimension_type i = cg.space_dimension(); i-- > 0; ) e += cg.coefficient(Variable(i)) * Variable(i); e += cg.inhomogeneous_term(); // Find the maximum and minimum bounds for the domain element d with the // linear expression e. PPL_DIRTY_TEMP_COEFFICIENT(max_num); PPL_DIRTY_TEMP_COEFFICIENT(max_den); bool max_included; PPL_DIRTY_TEMP_COEFFICIENT(min_num); PPL_DIRTY_TEMP_COEFFICIENT(min_den); bool min_included; if (d2.maximize(e, max_num, max_den, max_included)) { if (d2.minimize(e, min_num, min_den, min_included)) { // Adjust values to allow for the denominators max_den and min_den. max_num *= min_den; min_num *= max_den; PPL_DIRTY_TEMP_COEFFICIENT(den); PPL_DIRTY_TEMP_COEFFICIENT(mod); den = max_den * min_den; mod = cg.modulus() * den; // 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_num - min_num < mod2 || (max_num - min_num == 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_num % mod; if (!max_included && shrink_amount == 0) shrink_amount = mod; if (shrink_amount < 0) shrink_amount += mod; max_decreased = max_num - shrink_amount; // Find the amount by which the minimum value may be increased. shrink_amount = min_num % mod; if (!min_included && shrink_amount == 0) shrink_amount = - mod; if (shrink_amount > 0) shrink_amount -= mod; min_increased = min_num - 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(den * e == min_increased); d1.refine_with_constraint(new_c); d2.refine_with_constraint(new_c); return true; } else { if (max_decreased < min_increased) { // 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); std::swap(d1, new_d1); D2 new_d2(d2.space_dimension(), EMPTY); std::swap(d2, new_d2); return false; } } } } } return true; } template void Congruences_Reduction::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 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 void Shape_Preserving_Reduction::product_reduce(D1& d1, D2& d2) { // First do the congruences reduction. Parma_Polyhedra_Library::Congruences_Reduction 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); 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 aleady shared. continue; // Check the frequency and value of the linear expression for // the constraint `c'. Linear_Expression le(c); 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 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 class Determinate; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/Determinate.defs.hh line 32. */ #include /* Automatically generated from PPL source file ../src/Determinate.defs.hh line 34. */ namespace Parma_Polyhedra_Library { /*! \brief Returns true if and only if \p x and \p y are the same COW-wrapped pointset. \relates Determinate */ template bool operator==(const Determinate& x, const Determinate& y); /*! \brief Returns true if and only if \p x and \p y are different COW-wrapped pointsets. \relates Determinate */ template bool operator!=(const Determinate& x, const Determinate& y); namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Determinate */ template std::ostream& operator<<(std::ostream&, const Determinate&); } // namespace IO_Operators } // namespace Parma_Polyhedra_Library /*! \brief A wrapper for PPL pointsets, providing them with a determinate constraint system interface, as defined in \ref Bag98 "[Bag98]". The implementation uses a copy-on-write optimization, making the class suitable for constructions, like the finite powerset and ask-and-tell of \ref Bag98 "[Bag98]", that are likely to perform many copies. \ingroup PPL_CXX_interface */ template class Parma_Polyhedra_Library::Determinate { public: //! \name Constructors and Destructor //@{ /*! \brief Constructs a COW-wrapped object corresponding to the pointset \p p. */ Determinate(const PSET& p); /*! \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 true if and only if \p *this embeds the universe element \p PSET. */ bool is_top() const; /*! \brief Returns true if and only if \p *this embeds the empty element of \p PSET. */ bool is_bottom() const; //! Returns true if and only if \p *this entails \p y. bool definitely_entails(const Determinate& y) const; /*! \brief Returns true 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 true 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 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. The template parameter Binary_Operator_Assign is supposed to implement an apply and assign function, i.e., a function having signature void foo(PSET& x, const PSET& y) 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 std::mem_fun_ref(&C_Polyhedron::intersection_assign). */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template 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 static Binary_Operator_Assign_Lifter lift_op_assign(Binary_Operator_Assign op_assign); private: //! The possibly shared representation of a Determinate object. /*! \ingroup PPL_CXX_interface By adopting the copy-on-write 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 true 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==(const Determinate& x, const Determinate& y); friend bool operator!=(const Determinate& x, const Determinate& y); }; namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Determinate */ template void swap(Parma_Polyhedra_Library::Determinate& x, Parma_Polyhedra_Library::Determinate& y); } // namespace std /* 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 inline Determinate::Rep::Rep(dimension_type num_dimensions, Degenerate_Element kind) : references(0), pset(num_dimensions, kind) { } template inline Determinate::Rep::Rep(const PSET& p) : references(0), pset(p) { } template inline Determinate::Rep::Rep(const Constraint_System& cs) : references(0), pset(cs) { } template inline Determinate::Rep::Rep(const Congruence_System& cgs) : references(0), pset(cgs) { } template inline Determinate::Rep::~Rep() { PPL_ASSERT(references == 0); } template inline void Determinate::Rep::new_reference() const { ++references; } template inline bool Determinate::Rep::del_reference() const { return --references == 0; } template inline bool Determinate::Rep::is_shared() const { return references > 1; } template inline memory_size_type Determinate::Rep::external_memory_in_bytes() const { return pset.external_memory_in_bytes(); } template inline memory_size_type Determinate::Rep::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } template inline Determinate::Determinate(const PSET& pset) : prep(new Rep(pset)) { prep->new_reference(); } template inline Determinate::Determinate(const Constraint_System& cs) : prep(new Rep(cs)) { prep->new_reference(); } template inline Determinate::Determinate(const Congruence_System& cgs) : prep(new Rep(cgs)) { prep->new_reference(); } template inline Determinate::Determinate(const Determinate& y) : prep(y.prep) { prep->new_reference(); } template inline Determinate::~Determinate() { if (prep->del_reference()) delete prep; } template inline Determinate& Determinate::operator=(const Determinate& y) { y.prep->new_reference(); if (prep->del_reference()) delete prep; prep = y.prep; return *this; } template inline void Determinate::swap(Determinate& y) { std::swap(prep, y.prep); } template inline void Determinate::mutate() { if (prep->is_shared()) { Rep* new_prep = new Rep(prep->pset); (void) prep->del_reference(); new_prep->new_reference(); prep = new_prep; } } template inline const PSET& Determinate::pointset() const { return prep->pset; } template inline PSET& Determinate::pointset() { mutate(); return prep->pset; } template inline void Determinate::upper_bound_assign(const Determinate& y) { pointset().upper_bound_assign(y.pointset()); } template inline void Determinate::meet_assign(const Determinate& y) { pointset().intersection_assign(y.pointset()); } template inline bool Determinate::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 inline void Determinate::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 inline void Determinate::concatenate_assign(const Determinate& y) { pointset().concatenate_assign(y.pointset()); } template inline bool Determinate::definitely_entails(const Determinate& y) const { return prep == y.prep || y.prep->pset.contains(prep->pset); } template inline bool Determinate::is_definitely_equivalent_to(const Determinate& y) const { return prep == y.prep || prep->pset == y.prep->pset; } template inline bool Determinate::is_top() const { return prep->pset.is_universe(); } template inline bool Determinate::is_bottom() const { return prep->pset.is_empty(); } template inline memory_size_type Determinate::external_memory_in_bytes() const { return prep->total_memory_in_bytes(); } template inline memory_size_type Determinate::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } template inline bool Determinate::OK() const { return prep->pset.OK(); } namespace IO_Operators { /*! \relates Parma_Polyhedra_Library::Determinate */ template inline std::ostream& operator<<(std::ostream& s, const Determinate& x) { s << x.pointset(); return s; } } // namespace IO_Operators /*! \relates Determinate */ template inline bool operator==(const Determinate& x, const Determinate& y) { return x.prep == y.prep || x.prep->pset == y.prep->pset; } /*! \relates Determinate */ template inline bool operator!=(const Determinate& x, const Determinate& y) { return x.prep != y.prep && x.prep->pset != y.prep->pset; } template template inline Determinate::Binary_Operator_Assign_Lifter:: Binary_Operator_Assign_Lifter(Binary_Operator_Assign op_assign) : op_assign_(op_assign) { } template template inline void Determinate::Binary_Operator_Assign_Lifter:: operator()(Determinate& x, const Determinate& y) const { op_assign_(x.pointset(), y.pointset()); } template template inline Determinate::Binary_Operator_Assign_Lifter Determinate::lift_op_assign(Binary_Operator_Assign op_assign) { return Binary_Operator_Assign_Lifter(op_assign); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Determinate */ template inline void swap(Parma_Polyhedra_Library::Determinate& x, Parma_Polyhedra_Library::Determinate& y) { x.swap(y); } } // namespace std /* Automatically generated from PPL source file ../src/Determinate.defs.hh line 336. */ /* 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 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 class iterator_to_const; template class const_iterator_to_const; } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/iterator_to_const.defs.hh line 29. */ //#include "Ask_Tell.types.hh" #include #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 read-only iterator on the sequence of objects Container. By using this iterator class it is not possible to modify the objects contained in Container; rather, object modification has to be implemented by object replacement, i.e., by using the methods provided by Container 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 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 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; template friend class Powerset; //template friend class Ask_Tell; 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 true if and only if \p *this and \p y are identical. */ bool operator==(const iterator_to_const& y) const; /*! \brief Returns true 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 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 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; template friend class Powerset; //template friend class Ask_Tell; 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& 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 true if and only if \p *this and \p y are identical. */ bool operator==(const const_iterator_to_const& y) const; /*! \brief Returns true 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 true 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 bool operator==(const iterator_to_const& x, const const_iterator_to_const& y); #ifdef PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS /*! \brief Mixed comparison operator: returns true 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 bool operator!=(const iterator_to_const& x, const const_iterator_to_const& 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 inline iterator_to_const::iterator_to_const() : base() { } template inline iterator_to_const::iterator_to_const(const iterator_to_const& y) : base(y.base) { } template inline iterator_to_const::iterator_to_const(const Base& b) : base(b) { } template inline typename iterator_to_const::reference iterator_to_const::operator*() const { return *base; } template inline typename iterator_to_const::pointer iterator_to_const::operator->() const { return &*base; } template inline iterator_to_const& iterator_to_const::operator++() { ++base; return *this; } template inline iterator_to_const iterator_to_const::operator++(int) { iterator_to_const tmp = *this; operator++(); return tmp; } template inline iterator_to_const& iterator_to_const::operator--() { --base; return *this; } template inline iterator_to_const iterator_to_const::operator--(int) { iterator_to_const tmp = *this; operator--(); return tmp; } template inline bool iterator_to_const::operator==(const iterator_to_const& y) const { return base == y.base; } template inline bool iterator_to_const::operator!=(const iterator_to_const& y) const { return !operator==(y); } template inline const_iterator_to_const::const_iterator_to_const() : base() { } template inline const_iterator_to_const ::const_iterator_to_const(const const_iterator_to_const& y) : base(y.base) { } template inline const_iterator_to_const::const_iterator_to_const(const Base& b) : base(b) { } template inline typename const_iterator_to_const::reference const_iterator_to_const::operator*() const { return *base; } template inline typename const_iterator_to_const::pointer const_iterator_to_const::operator->() const { return &*base; } template inline const_iterator_to_const& const_iterator_to_const::operator++() { ++base; return *this; } template inline const_iterator_to_const const_iterator_to_const::operator++(int) { const_iterator_to_const tmp = *this; operator++(); return tmp; } template inline const_iterator_to_const& const_iterator_to_const::operator--() { --base; return *this; } template inline const_iterator_to_const const_iterator_to_const::operator--(int) { const_iterator_to_const tmp = *this; operator--(); return tmp; } template inline bool const_iterator_to_const ::operator==(const const_iterator_to_const& y) const { return base == y.base; } template inline bool const_iterator_to_const ::operator!=(const const_iterator_to_const& y) const { return !operator==(y); } template inline const_iterator_to_const ::const_iterator_to_const(const iterator_to_const& y) : base(y.base) { } /*! \relates const_iterator_to_const */ template inline bool operator==(const iterator_to_const& x, const const_iterator_to_const& y) { return const_iterator_to_const(x).operator==(y); } /*! \relates const_iterator_to_const */ template inline bool operator!=(const iterator_to_const& x, const const_iterator_to_const& y) { return !(x == y); } } // namespace Parma_Polyhedra_Library /* Automatically generated from PPL source file ../src/iterator_to_const.defs.hh line 222. */ /* Automatically generated from PPL source file ../src/Powerset.defs.hh line 30. */ #include #include #include namespace Parma_Polyhedra_Library { //! Returns true if and only if \p x and \p y are equivalent. /*! \relates Powerset */ template bool operator==(const Powerset& x, const Powerset& y); //! Returns true if and only if \p x and \p y are not equivalent. /*! \relates Powerset */ template bool operator!=(const Powerset& x, const Powerset& y); namespace IO_Operators { //! Output operator. /*! \relates Parma_Polyhedra_Library::Powerset */ template std::ostream& operator<<(std::ostream& s, const Powerset& 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 powerset 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 read-only, meaning that the disjuncts cannot be overwritten. Rather, by using the class iterator, 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 void drop_subsumed(Powerset& ps, const D& d) { for (typename Powerset::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 true if and only if the instance of D is the top element of the domain. \code bool is_bottom() const \endcode Returns true 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 true if the instance of D definitely entails y. Returns false if the instance may not entail y (i.e., if the instance does not entail y 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 y. \code void meet_assign(const D& y) \endcode Assigns to the instance of D the meet of the instance and y. \code bool OK() const \endcode Returns true if the instance of D is in a consistent state, else returns false. 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 s. \code operator==(const D& x, const D& y) \endcode Returns true if and only if x and y are equivalent D's. \code operator!=(const D& x, const D& y) \endcode Returns true if and only if x and y are different D's. */ template 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 true if \p *this definitely entails \p y. Returns false 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 true 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 true 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 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 true, \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 read-only 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 drop_disjunct(iterator position) and drop_disjuncts(iterator first, iterator last), while still ensuring a correct handling of Omega-reduction. */ typedef iterator_to_const iterator; //! A bidirectional %const_iterator on the disjuncts of a Powerset element. typedef const_iterator_to_const const_iterator; //! The reverse iterator type built from Powerset::iterator. typedef std::reverse_iterator reverse_iterator; //! The reverse iterator type built from Powerset::const_iterator. typedef std::reverse_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 const 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 true 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 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 true 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 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 true 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); }; namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Powerset */ template void swap(Parma_Polyhedra_Library::Powerset& x, Parma_Polyhedra_Library::Powerset& y); } // namespace std /* Automatically generated from PPL source file ../src/Powerset.inlines.hh line 1. */ /* Powerset class implementation: inline functions. */ #include /* Automatically generated from PPL source file ../src/Powerset.inlines.hh line 29. */ namespace Parma_Polyhedra_Library { template inline typename Powerset::iterator Powerset::begin() { return sequence.begin(); } template inline typename Powerset::iterator Powerset::end() { return sequence.end(); } template inline typename Powerset::const_iterator Powerset::begin() const { return sequence.begin(); } template inline typename Powerset::const_iterator Powerset::end() const { return sequence.end(); } template inline typename Powerset::reverse_iterator Powerset::rbegin() { return reverse_iterator(end()); } template inline typename Powerset::reverse_iterator Powerset::rend() { return reverse_iterator(begin()); } template inline typename Powerset::const_reverse_iterator Powerset::rbegin() const { return const_reverse_iterator(end()); } template inline typename Powerset::const_reverse_iterator Powerset::rend() const { return const_reverse_iterator(begin()); } template inline typename Powerset::size_type Powerset::size() const { return sequence.size(); } template inline bool Powerset::empty() const { return sequence.empty(); } template inline typename Powerset::iterator Powerset::drop_disjunct(iterator position) { return sequence.erase(position.base); } template inline void Powerset::drop_disjuncts(iterator first, iterator last) { sequence.erase(first.base, last.base); } template inline void Powerset::clear() { sequence.clear(); } template inline Powerset::Powerset(const Powerset& y) : sequence(y.sequence), reduced(y.reduced) { } template inline Powerset& Powerset::operator=(const Powerset& y) { sequence = y.sequence; reduced = y.reduced; return *this; } template inline void Powerset::swap(Powerset& y) { std::swap(sequence, y.sequence); std::swap(reduced, y.reduced); } template inline Powerset::Powerset() : sequence(), reduced(true) { } template inline Powerset::Powerset(const D& d) : sequence(), reduced(false) { sequence.push_back(d); PPL_ASSERT_HEAVY(OK()); } template inline Powerset::~Powerset() { } template inline void Powerset::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 inline void Powerset::add_disjunct(const D& d) { sequence.push_back(d); reduced = false; } /*! \relates Powerset */ template inline bool operator!=(const Powerset& x, const Powerset& y) { return !(x == y); } template inline bool Powerset::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 inline bool Powerset::is_bottom() const { // Must perform omega-reduction for correctness. omega_reduce(); return empty(); } template inline void Powerset::collapse() { if (!empty()) collapse(sequence.begin()); } template inline void Powerset::meet_assign(const Powerset& y) { pairwise_apply_assign(y, std::mem_fun_ref(&D::meet_assign)); } template inline void Powerset::upper_bound_assign(const Powerset& y) { least_upper_bound_assign(y); } template inline bool Powerset::upper_bound_assign_if_exact(const Powerset& y) { least_upper_bound_assign(y); return true; } template inline memory_size_type Powerset::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Powerset */ template inline void swap(Parma_Polyhedra_Library::Powerset& x, Parma_Polyhedra_Library::Powerset& y) { x.swap(y); } } // namespace std /* 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 28. */ #include /* Automatically generated from PPL source file ../src/Powerset.templates.hh line 30. */ #include namespace Parma_Polyhedra_Library { template void Powerset::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 void Powerset::omega_reduce() const { if (reduced) return; Powerset& x = const_cast(*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 && xi != x.end()) { // Hurry up! x.collapse(xi.base); break; } } reduced = true; PPL_ASSERT_HEAVY(OK()); } template void Powerset::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 bool Powerset::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 bool Powerset::is_omega_reduced() const { if (!reduced && check_omega_reduced()) reduced = true; return reduced; } template typename Powerset::iterator Powerset::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 bool Powerset::definitely_entails(const Powerset& y) const { const Powerset& 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 bool operator==(const Powerset& x, const Powerset& y) { x.omega_reduce(); y.omega_reduce(); if (x.size() != y.size()) return false; // Take a copy of `y' and work with it. Powerset yy = y; for (typename Powerset::const_iterator xi = x.begin(), x_end = x.end(); xi != x_end; ++xi) { typename Powerset::iterator yyi = yy.begin(); typename Powerset::iterator yy_end = yy.end(); yyi = std::find(yyi, yy_end, *xi); if (yyi == yy_end) return false; else yy.drop_disjunct(yyi); } return true; } template template void Powerset::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 void Powerset::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 std::ostream& operator<<(std::ostream& s, const Powerset& x) { if (x.is_bottom()) s << "false"; else if (x.is_top()) s << "true"; else for (typename Powerset::const_iterator i = x.begin(), x_end = x.end(); i != x_end; ) { s << "{ " << *i++ << " }"; if (i != x_end) s << ", "; } return s; } } // namespace IO_Operators template memory_size_type Powerset::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 bool Powerset::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 454. */ /* Automatically generated from PPL source file ../src/Pointset_Powerset.defs.hh line 44. */ #include #include #include //! 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: C_Polyhedron, NNC_Polyhedron, Grid, Octagonal_Shape, BD_Shape, Box. */ template class Parma_Polyhedra_Library::Pointset_Powerset : public Parma_Polyhedra_Library::Powerset > { public: typedef PSET element_type; private: typedef Determinate Det_PSET; typedef Powerset Base; public: //! Returns the maximum space dimension a Pointset_Powerset 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 QH of the disjuncts in the source powerset is different from PSET. \param y The powerset to be used to build the new powerset. \param complexity The maximal complexity of any algorithms used. */ template explicit Pointset_Powerset(const Pointset_Powerset& 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 explicit Pointset_Powerset(const Partially_Reduced_Product& 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 explicit Pointset_Powerset(const Octagonal_Shape& 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 bdss exceeds the maximum allowed space dimension. */ template explicit Pointset_Powerset(const BD_Shape& 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 explicit Pointset_Powerset(const Box& 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 true if and only if \p *this is an empty powerset. */ bool is_empty() const; /*! \brief Returns true if and only if \p *this is the top element of the powerser lattice. */ bool is_universe() const; /*! \brief Returns true if and only if all the disjuncts in \p *this are topologically closed. */ bool is_topologically_closed() const; /*! \brief Returns true if and only if all elements in \p *this are bounded. */ bool is_bounded() const; //! Returns true 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 true if and only if \p *this is discrete. bool is_discrete() const; /*! \brief Returns true 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 true 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 true 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 true 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 true 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, false 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 true 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 true 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, false 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 true 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 true 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, false 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 true 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 true 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, false 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 true 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 really expensive! */ bool geometrically_covers(const Pointset_Powerset& y) const; /*! \brief Returns true 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 really expensive! */ bool geometrically_equals(const Pointset_Powerset& y) const; /*! \brief Returns true 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 true 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 true 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 x == y, then x.hash_code() == y.hash_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 c. /*! \exception std::invalid_argument Thrown if \p *this and congruence \p c are topology-incompatible or dimension-incompatible. */ void add_congruence(const Congruence& c); /*! \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 ANY_COMPLEXITY. */ 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 ANY_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(); //! 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 pcs Possibly null pointer to a constraint system whose variables are contained in \p vars. If *pcs 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 *pcs. \param complexity_threshold A precision parameter of the \ref Wrapping_Operator "wrapping operator": higher values result in possibly improved precision. \param wrap_individually true 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 *pcs is dimension-incompatible with \p vars, or if \p *this is dimension-incompatible \p vars or with *pcs. */ 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* pcs = 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 wf and the cardinality threshold \p max_disjuncts. \param y A powerset that must definitely entail \p *this; \param wf 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., widen_fun_ref(&Polyhedron::H79_widening_assign) and widen_fun_ref(&Polyhedron::limited_H79_extrapolation_assign, cs); \param max_disjuncts The maximum number of disjuncts occurring in the powerset \p *this before 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 void BGP99_extrapolation_assign(const Pointset_Powerset& y, Widening wf, 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 wf certified by the convergence certificate \p Cert. \param y The finite powerset computed in the previous iteration step. It must definitely entail \p *this; \param wf 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., widen_fun_ref(&Polyhedron::H79_widening_assign) and widen_fun_ref(&Polyhedron::limited_H79_extrapolation_assign, cs). \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 wf; 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 void BHZ03_widening_assign(const Pointset_Powerset& y, Widening wf); //@} // 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 QH of the disjuncts in the source powerset is different from PSET (\p *this and \p y can be dimension-incompatible). */ template Pointset_Powerset& operator=(const Pointset_Powerset& y); //! Swaps \p *this with \p y. void 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 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 max_space_dimension(). If \p *this has space dimension \f$n\f$, with \f$n > 0\f$, and var 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$, dest 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 true if successful, false 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 wf. */ template void BGP99_heuristics_assign(const Pointset_Powerset& y, Widening wf); //! Records in \p cert_ms the certificates for this set of disjuncts. template void collect_certificates(std::map& cert_ms) const; /*! \brief Returns true if and only if the current set of dijsuncts is stabilizing with respect to the multiset of certificates \p y_cert_ms. */ template bool is_cert_multiset_stabilizing(const std::map& y_cert_ms) const; // FIXME: here it should be enough to befriend the template constructor // template // Pointset_Powerset(const Pointset_Powerset&), // but, apparently, this cannot be done. friend class Pointset_Powerset; }; namespace Parma_Polyhedra_Library { //! 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 r of type std::pair\ \> such that - r.first is the intersection of \p p and \p q; - r.second has the property that all its elements are pairwise disjoint and disjoint from \p p; - the set-theoretical union of r.first with all the elements of r.second gives \p q (i.e., r is the representation of a partition of \p q). \if Include_Implementation_Details See this paper for more information about the implementation. \endif */ template std::pair > linear_partition(const PSET& p, const PSET& q); /*! \brief Returns true 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& 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 r of type std::pair\ \> such that - r.first is the intersection of \p p and \p q; - If there is a finite partition of \p q wrt \p p the Boolean finite_partition is set to true and r.second has the property that all its elements are pairwise disjoint and disjoint from \p p and the set-theoretical union of r.first with all the elements of r.second gives \p q (i.e., r is the representation of a partition of \p q). - Otherwise the Boolean finite_partition is set to false and the singleton set that contains \p q is stored in r.secondr. */ std::pair > approximate_partition(const Grid& p, const Grid& q, bool& finite_partition); /*! \brief Returns true 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& ps); /*! \brief Returns true 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 bool check_containment(const PSET& ph, const Pointset_Powerset& 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 Pointset_Powerset ::Pointset_Powerset(const Pointset_Powerset& 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 ::Pointset_Powerset(const Pointset_Powerset& y, Complexity_Class); template <> template <> Pointset_Powerset ::Pointset_Powerset(const Pointset_Powerset& y, Complexity_Class); template <> template <> Pointset_Powerset ::Pointset_Powerset(const Pointset_Powerset& y, Complexity_Class); template <> void Pointset_Powerset ::difference_assign(const Pointset_Powerset& y); template <> void Pointset_Powerset ::difference_assign(const Pointset_Powerset& y); template <> bool Pointset_Powerset ::geometrically_covers(const Pointset_Powerset& y) const; template <> bool Pointset_Powerset ::geometrically_covers(const Pointset_Powerset& y) const; } // namespace Parma_Polyhedra_Library namespace std { //! Specializes std::swap. /*! \relates Parma_Polyhedra_Library::Pointset_Powerset */ template void swap(Parma_Polyhedra_Library::Pointset_Powerset& x, Parma_Polyhedra_Library::Pointset_Powerset& y); } // namespace std /* 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 #include namespace Parma_Polyhedra_Library { template inline dimension_type Pointset_Powerset::space_dimension() const { return space_dim; } template inline dimension_type Pointset_Powerset::max_space_dimension() { return PSET::max_space_dimension(); } template inline Pointset_Powerset::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(num_dimensions, kind))); PPL_ASSERT_HEAVY(x.OK()); } template inline Pointset_Powerset::Pointset_Powerset(const Pointset_Powerset& y, Complexity_Class) : Base(y), space_dim(y.space_dim) { } template inline Pointset_Powerset::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(ph, complexity))); x.reduced = false; PPL_ASSERT_HEAVY(OK()); } template inline Pointset_Powerset::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(ph, complexity))); PPL_ASSERT_HEAVY(OK()); } template inline Pointset_Powerset::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(gr))); } PPL_ASSERT_HEAVY(OK()); } template template inline Pointset_Powerset ::Pointset_Powerset(const Partially_Reduced_Product& 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(prp, complexity))); x.reduced = false; PPL_ASSERT_HEAVY(OK()); } template template Pointset_Powerset::Pointset_Powerset(const Box& box, Complexity_Class) : Base(), space_dim(box.space_dimension()) { Pointset_Powerset& x = *this; if (!box.is_empty()) x.sequence.push_back(Determinate(PSET(box))); PPL_ASSERT_HEAVY(OK()); } template template Pointset_Powerset::Pointset_Powerset(const Octagonal_Shape& os, Complexity_Class) : Base(), space_dim(os.space_dimension()) { Pointset_Powerset& x = *this; if (!os.is_empty()) x.sequence.push_back(Determinate(PSET(os))); PPL_ASSERT_HEAVY(OK()); } template template Pointset_Powerset::Pointset_Powerset(const BD_Shape& bds, Complexity_Class) : Base(), space_dim(bds.space_dimension()) { Pointset_Powerset& x = *this; if (!bds.is_empty()) x.sequence.push_back(Determinate(PSET(bds))); PPL_ASSERT_HEAVY(OK()); } template inline Pointset_Powerset::Pointset_Powerset(const Constraint_System& cs) : Base(Determinate(cs)), space_dim(cs.space_dimension()) { PPL_ASSERT_HEAVY(OK()); } template inline Pointset_Powerset::Pointset_Powerset(const Congruence_System& cgs) : Base(Determinate(cgs)), space_dim(cgs.space_dimension()) { PPL_ASSERT_HEAVY(OK()); } template inline Pointset_Powerset& Pointset_Powerset::operator=(const Pointset_Powerset& y) { Pointset_Powerset& x = *this; x.Base::operator=(y); x.space_dim = y.space_dim; return x; } template inline void Pointset_Powerset::swap(Pointset_Powerset& y) { Pointset_Powerset& x = *this; x.Base::swap(y); std::swap(x.space_dim, y.space_dim); } template template inline Pointset_Powerset& Pointset_Powerset::operator=(const Pointset_Powerset& y) { Pointset_Powerset& x = *this; Pointset_Powerset pps(y); x.swap(pps); return x; } template inline void Pointset_Powerset::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 inline void Pointset_Powerset::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 inline bool Pointset_Powerset ::geometrically_covers(const Pointset_Powerset& y) const { // This code is only used when PSET is an abstraction of NNC_Polyhedron. const Pointset_Powerset xx(*this); const Pointset_Powerset yy(y); return xx.geometrically_covers(yy); } template inline bool Pointset_Powerset ::geometrically_equals(const Pointset_Powerset& y) const { // This code is only used when PSET is an abstraction of NNC_Polyhedron. const Pointset_Powerset xx(*this); const Pointset_Powerset yy(y); return xx.geometrically_covers(yy) && yy.geometrically_covers(xx); } template <> inline bool Pointset_Powerset ::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 ::geometrically_equals(const Pointset_Powerset& y) const { const Pointset_Powerset& x = *this; return x.geometrically_covers(y) && y.geometrically_covers(x); } template inline memory_size_type Pointset_Powerset::external_memory_in_bytes() const { return Base::external_memory_in_bytes(); } template inline memory_size_type Pointset_Powerset::total_memory_in_bytes() const { return sizeof(*this) + external_memory_in_bytes(); } template inline int32_t Pointset_Powerset::hash_code() const { return space_dimension() & 0x7fffffff; } template inline void Pointset_Powerset ::difference_assign(const Pointset_Powerset& y) { // This code is only used when PSET is an abstraction of NNC_Polyhedron. Pointset_Powerset nnc_this(*this); Pointset_Powerset nnc_y(y); nnc_this.difference_assign(nnc_y); *this = nnc_this; } /*! \relates Pointset_Powerset */ template inline bool check_containment(const PSET& ph, const Pointset_Powerset& ps) { // This code is only used when PSET is an abstraction of NNC_Polyhedron. const NNC_Polyhedron pph = NNC_Polyhedron(ph.constraints()); const Pointset_Powerset pps(ps); return check_containment(pph, pps); } /*! \relates Pointset_Powerset */ template <> inline bool check_containment(const C_Polyhedron& ph, const Pointset_Powerset& ps) { return check_containment(NNC_Polyhedron(ph), Pointset_Powerset(ps)); } } // namespace Parma_Polyhedra_Library namespace std { /*! \relates Parma_Polyhedra_Library::Pointset_Powerset */ template inline void swap(Parma_Polyhedra_Library::Pointset_Powerset& x, Parma_Polyhedra_Library::Pointset_Powerset& y) { x.swap(y); } } // namespace std /* 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 #include #include #include #include #include namespace Parma_Polyhedra_Library { template void Pointset_Powerset::add_disjunct(const PSET& ph) { Pointset_Powerset& x = *this; if (x.space_dimension() != ph.space_dimension()) { std::ostringstream s; s << "PPL::Pointset_Powerset::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(ph)); x.reduced = false; PPL_ASSERT_HEAVY(x.OK()); } template <> template Pointset_Powerset ::Pointset_Powerset(const Pointset_Powerset& y, Complexity_Class complexity) : Base(), space_dim(y.space_dimension()) { Pointset_Powerset& x = *this; for (typename Pointset_Powerset::const_iterator i = y.begin(), y_end = y.end(); i != y_end; ++i) x.sequence.push_back(Determinate (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 template Pointset_Powerset ::Pointset_Powerset(const Pointset_Powerset& y, Complexity_Class complexity) : Base(), space_dim(y.space_dimension()) { Pointset_Powerset& x = *this; for (typename Pointset_Powerset::const_iterator i = y.begin(), y_end = y.end(); i != y_end; ++i) x.sequence.push_back(Determinate(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 void Pointset_Powerset::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 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 && xi != x_end && y_begin != y_end) { // Hurry up! PSET xph = xi->pointset(); for (++xi; xi != x_end; ++xi) xph.upper_bound_assign(xi->pointset()); const_iterator yi = y_begin; PSET yph = yi->pointset(); for (++yi; yi != y_end; ++yi) yph.upper_bound_assign(yi->pointset()); xph.concatenate_assign(yph); x.swap(new_x); x.add_disjunct(xph); PPL_ASSERT_HEAVY(x.OK()); return; } } x.swap(new_x); PPL_ASSERT_HEAVY(x.OK()); } template void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset::add_congruence(const Congruence& c) { Pointset_Powerset& x = *this; for (Sequence_iterator si = x.sequence.begin(), s_end = x.sequence.end(); si != s_end; ++si) si->pointset().add_congruence(c); x.reduced = false; PPL_ASSERT_HEAVY(x.OK()); } template void Pointset_Powerset::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 void Pointset_Powerset::add_congruences(const Congruence_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_congruences(cs); x.reduced = false; PPL_ASSERT_HEAVY(x.OK()); } template void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset ::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 template void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset::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 void Pointset_Powerset ::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 void Pointset_Powerset ::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 void Pointset_Powerset ::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 void Pointset_Powerset ::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 void Pointset_Powerset ::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 void Pointset_Powerset ::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 dimension_type Pointset_Powerset::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 bool Pointset_Powerset::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 universe(x.space_dimension(), UNIVERSE); Pointset_Powerset& xx = const_cast(x); xx.swap(universe); } return true; } return false; } template bool Pointset_Powerset::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 bool Pointset_Powerset::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 bool Pointset_Powerset::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 bool Pointset_Powerset::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 bool Pointset_Powerset::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::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 bool Pointset_Powerset::is_disjoint_from(const Pointset_Powerset& y) const { const Pointset_Powerset& x = *this; for (Sequence_const_iterator si = x.sequence.begin(), xs_end = x.sequence.end(); si != xs_end; ++si) { const PSET& pi = si->pointset(); for (Sequence_const_iterator sj = y.sequence.begin(), ys_end = y.sequence.end(); sj != ys_end; ++sj) { const PSET& pj = sj->pointset(); if (!pi.is_disjoint_from(pj)) return false; } } return true; } template void Pointset_Powerset ::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 void Pointset_Powerset ::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 void Pointset_Powerset::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 bool Pointset_Powerset ::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); nonempty_intersection |= enlarged_i.simplify_using_context_assign(context_i); // TODO: merge the sorted constraints of `enlarged' and `enlarged_i'? enlarged.intersection_assign(enlarged_i); } dest.swap(enlarged); return nonempty_intersection; } template bool Pointset_Powerset ::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 bool Pointset_Powerset::contains(const Pointset_Powerset& y) const { const Pointset_Powerset& x = *this; for (Sequence_const_iterator si = y.sequence.begin(), ys_end = y.sequence.end(); si != ys_end; ++si) { const PSET& pi = si->pointset(); bool pi_is_contained = false; for (Sequence_const_iterator sj = x.sequence.begin(), xs_end = x.sequence.end(); (sj != xs_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 bool Pointset_Powerset::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(), ys_end = y.sequence.end(); si != ys_end; ++si) { const PSET& pi = si->pointset(); bool pi_is_strictly_contained = false; for (Sequence_const_iterator sj = x.sequence.begin(), xs_end = x.sequence.end(); (sj != xs_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 Poly_Con_Relation Pointset_Powerset::relation_with(const Congruence& cg) const { const Pointset_Powerset& x = *this; /* *this is included in cg if every disjunct is included in cg */ bool is_included = true; /* *this is disjoint with cg if every disjunct is disjoint with cg */ bool is_disjoint = true; /* *this strictly_intersects with cg if some disjunct strictly intersects with cg */ bool is_strictly_intersecting = false; /* *this saturates cg if some disjunct saturates cg and every disjunct is either disjoint from cg or saturates cg */ bool saturates_once = false; bool may_saturate = 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(cg); if (!relation_i.implies(Poly_Con_Relation::is_included())) is_included = false; if (!relation_i.implies(Poly_Con_Relation::is_disjoint())) 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_once = true; else if (!relation_i.implies(Poly_Con_Relation::is_disjoint())) may_saturate = 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) result = result && Poly_Con_Relation::strictly_intersects(); if (saturates_once && may_saturate) result = result && Poly_Con_Relation::saturates(); return result; } template Poly_Con_Relation Pointset_Powerset::relation_with(const Constraint& 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 */ bool is_strictly_intersecting = false; /* *this saturates c if some disjunct saturates c and every disjunct is either disjoint from c or saturates c */ bool saturates_once = false; bool may_saturate = 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())) is_included = false; if (!relation_i.implies(Poly_Con_Relation::is_disjoint())) 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_once = true; else if (!relation_i.implies(Poly_Con_Relation::is_disjoint())) may_saturate = 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) result = result && Poly_Con_Relation::strictly_intersects(); if (saturates_once && may_saturate) result = result && Poly_Con_Relation::saturates(); return result; } template Poly_Gen_Relation Pointset_Powerset::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 bool Pointset_Powerset ::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 bool Pointset_Powerset ::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 bool Pointset_Powerset::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(supt_n); PPL_DIRTY_TEMP_COEFFICIENT(supt_d); supt_n = 0; supt_d = 1; bool maxt = 0; PPL_DIRTY_TEMP_COEFFICIENT(supi_n); PPL_DIRTY_TEMP_COEFFICIENT(supi_d); supi_n = 0; supi_d = 1; bool maxi = 0; 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, supi_n, supi_d, maxi)) return false; else if (first) { first = false; supt_n = supi_n; supt_d = supi_d; maxt = maxi; } else { tmp = (supt_n * supi_d) - (supi_n * supt_d); if (tmp < 0) { supt_n = supi_n; supt_d = supi_d; maxt = maxi; } else if (tmp == 0) maxt = maxt || maxi; } } sup_n = supt_n; sup_d = supt_d; maximum = maxt; return true; } template bool Pointset_Powerset::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(supt_n); PPL_DIRTY_TEMP_COEFFICIENT(supt_d); supt_n = 0; supt_d = 1; bool maxt = 0; Generator gt = point(); PPL_DIRTY_TEMP_COEFFICIENT(supi_n); PPL_DIRTY_TEMP_COEFFICIENT(supi_d); supi_n = 0; supi_d = 1; bool maxi = 0; Generator gi = 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, supi_n, supi_d, maxi, gi)) return false; else if (first) { first = false; supt_n = supi_n; supt_d = supi_d; maxt = maxi; gt = gi; } else { tmp = (supt_n * supi_d) - (supi_n * supt_d); if (tmp < 0) { supt_n = supi_n; supt_d = supi_d; maxt = maxi; gt = gi; } else if (tmp == 0) { maxt = maxt || maxi; gt = gi; } } } sup_n = supt_n; sup_d = supt_d; maximum = maxt; g = gt; return true; } template bool Pointset_Powerset::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(inft_n); PPL_DIRTY_TEMP_COEFFICIENT(inft_d); inft_n = 0; inft_d = 1; bool mint = 0; PPL_DIRTY_TEMP_COEFFICIENT(infi_n); PPL_DIRTY_TEMP_COEFFICIENT(infi_d); infi_n = 0; infi_d = 1; bool mini = 0; 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, infi_n, infi_d, mini)) return false; else if (first) { first = false; inft_n = infi_n; inft_d = infi_d; mint = mini; } else { tmp = (inft_n * infi_d) - (infi_n * inft_d); if (tmp > 0) { inft_n = infi_n; inft_d = infi_d; mint = mini; } else if (tmp == 0) mint = mint || mini; } } inf_n = inft_n; inf_d = inft_d; minimum = mint; return true; } template bool Pointset_Powerset::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(inft_n); PPL_DIRTY_TEMP_COEFFICIENT(inft_d); inft_n = 0; inft_d = 1; bool mint = 0; Generator gt = point(); PPL_DIRTY_TEMP_COEFFICIENT(infi_n); PPL_DIRTY_TEMP_COEFFICIENT(infi_d); infi_n = 0; infi_d = 1; bool mini = 0; Generator gi = 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, infi_n, infi_d, mini, gi)) return false; else if (first) { first = false; inft_n = infi_n; inft_d = infi_d; mint = mini; gt = gi; } else { tmp = (inft_n * infi_d) - (infi_n * inft_d); if (tmp > 0) { inft_n = infi_n; inft_d = infi_d; mint = mini; gt = gi; } else if (tmp == 0) { mint = mint || mini; gt = gi; } } } inf_n = inft_n; inf_d = inft_d; minimum = mint; g = gt; return true; } template bool Pointset_Powerset::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 void Pointset_Powerset::wrap_assign(const Variables_Set& vars, Bounded_Integer_Type_Width w, Bounded_Integer_Type_Representation r, Bounded_Integer_Type_Overflow o, const Constraint_System* pcs, 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, pcs, complexity_threshold, wrap_individually); x.reduced = false; PPL_ASSERT_HEAVY(x.OK()); } template void Pointset_Powerset::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 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] = marked[sj_index] = true; new_x.add_non_bottom_disjunct_preserve_reduction(pi); ++deleted; goto next; } } next: ; } iterator nx_begin = new_x.begin(); iterator nx_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]) nx_begin = new_x.add_non_bottom_disjunct_preserve_reduction(*xi, nx_begin, nx_end); std::swap(x.sequence, new_x.sequence); n -= deleted; } while (deleted > 0); PPL_ASSERT_HEAVY(x.OK()); } template template void Pointset_Powerset:: BGP99_heuristics_assign(const Pointset_Powerset& y, Widening wf) { // `x' is the current iteration value. Pointset_Powerset& x = *this; #ifndef NDEBUG { // We assume that `y' entails `x'. const Pointset_Powerset x_copy = x; const Pointset_Powerset 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 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; wf(pi_copy, pj); new_x.add_non_bottom_disjunct_preserve_reduction(pi_copy); marked[i_index] = true; } } iterator nx_begin = new_x.begin(); iterator nx_end = new_x.end(); i_index = 0; for (const_iterator i = x_begin; i != x_end; ++i, ++i_index) if (!marked[i_index]) nx_begin = new_x.add_non_bottom_disjunct_preserve_reduction(*i, nx_begin, nx_end); std::swap(x.sequence, new_x.sequence); PPL_ASSERT_HEAVY(x.OK()); PPL_ASSERT(x.is_omega_reduced()); } template template void Pointset_Powerset:: BGP99_extrapolation_assign(const Pointset_Powerset& y, Widening wf, unsigned max_disjuncts) { // `x' is the current iteration value. Pointset_Powerset& x = *this; #ifndef NDEBUG { // We assume that `y' entails `x'. const Pointset_Powerset x_copy = x; const Pointset_Powerset 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, wf); } template template void Pointset_Powerset:: collect_certificates(std::map& 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 template bool Pointset_Powerset:: is_cert_multiset_stabilizing(const std::map& y_cert_ms) const { typedef std::map Cert_Multiset; Cert_Multiset x_cert_ms; collect_certificates(x_cert_ms); typename Cert_Multiset::const_iterator xi = x_cert_ms.begin(), x_cert_ms_end = x_cert_ms.end(), yi = y_cert_ms.begin(), 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 template void Pointset_Powerset::BHZ03_widening_assign(const Pointset_Powerset& y, Widening wf) { // `x' is the current iteration value. Pointset_Powerset& x = *this; #ifndef NDEBUG { // We assume that `y' entails `x'. const Pointset_Powerset x_copy = x; const Pointset_Powerset 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_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 bgp99_heuristics = x; bgp99_heuristics.BGP99_heuristics_assign(y, wf); // Compute the poly-hull of `bgp99_heuristics'. PSET bgp99_heuristics_hull(x.space_dim, EMPTY); for (const_iterator i = bgp99_heuristics.begin(), bh_end = bgp99_heuristics.end(); i != bh_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. std::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)) { std::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 reduced_bgp99_heuristics(bgp99_heuristics); reduced_bgp99_heuristics.pairwise_reduce(); if (reduced_bgp99_heuristics.is_cert_multiset_stabilizing(y_cert_ms)) { std::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; wf(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 x_hull_singleton(x.space_dim, EMPTY); x_hull_singleton.add_disjunct(x_hull); std::swap(x, x_hull_singleton); } template void Pointset_Powerset::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) template bool Pointset_Powerset::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); } x.swap(new_x); // Check invariants. PPL_ASSERT_HEAVY(x.OK()); return true; } template bool Pointset_Powerset::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 qq according to constraint \p c. /*! \relates Parma_Polyhedra_Library::Pointset_Powerset On exit, the intersection of \p qq and constraint \p c is stored in \p qq, whereas the intersection of \p qq 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 void linear_partition_aux(const Constraint& c, PSET& qq, Pointset_Powerset& r) { Linear_Expression le(c); const Constraint& neg_c = c.is_strict_inequality() ? (le <= 0) : (le < 0); NNC_Polyhedron qqq(qq); qqq.add_constraint(neg_c); if (!qqq.is_empty()) r.add_disjunct(qqq); qq.add_constraint(c); } } // namespace Pointset_Powersets } // namespace Implementation /*! \relates Pointset_Powerset */ template std::pair > linear_partition(const PSET& p, const PSET& q) { using Implementation::Pointset_Powersets::linear_partition_aux; Pointset_Powerset r(p.space_dimension(), EMPTY); PSET qq = q; const Constraint_System& pcs = p.constraints(); for (Constraint_System::const_iterator i = pcs.begin(), pcs_end = pcs.end(); i != pcs_end; ++i) { const Constraint& c = *i; if (c.is_equality()) { Linear_Expression le(c); linear_partition_aux(le <= 0, qq, r); linear_partition_aux(le >= 0, qq, r); } else linear_partition_aux(c, qq, r); } return std::make_pair(qq, 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 /* Automatically generated from PPL source file ../src/algorithms.hh line 31. */ 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 true is returned, otherwise false is returned. \relates Polyhedron */ #endif // defined(PPL_DOXYGEN_INCLUDE_IMPLEMENTATION_DETAILS) template 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 bool poly_hull_assign_if_exact(PH& p, const PH& q) { PH phull = p; NNC_Polyhedron nnc_p(p); phull.poly_hull_assign(q); std::pair > partition = linear_partition(q, phull); const Pointset_Powerset& s = partition.second; typedef Pointset_Powerset::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 = phull; 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 30. */ namespace Parma_Polyhedra_Library { /*! \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 minimized_constraints() 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 true if any loop approximated by \p pset definitely terminates; false if the test is inconclusive. However, if \p pset precisely characterizes the effect of the loop body onto the loop-relevant program variables, then true is returned if and only if the loop terminates. */ template bool termination_test_MS(const PSET& pset); /*! \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 minimized_constraints() method. \param pset_before A pointset approximating the values of loop-relevant variables before 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 after 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 true if any loop approximated by \p pset definitely terminates; false if the test is inconclusive. However, if \p pset_before and \p pset_after precisely characterize the effect of the loop body onto the loop-relevant program variables, then true is returned if and only if the loop terminates. */ template bool termination_test_MS_2(const PSET& pset_before, const PSET& pset_after); /*! \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 minimized_constraints() 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 true 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 true if any loop approximated by \p pset definitely terminates; false if the test is inconclusive. However, if \p pset precisely characterizes the effect of the loop body onto the loop-relevant program variables, then true is returned if and only if the loop terminates. */ template bool one_affine_ranking_function_MS(const PSET& pset, Generator& mu); /*! \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 minimized_constraints() method. \param pset_before A pointset approximating the values of loop-relevant variables before 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 after 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 true 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 true if any loop approximated by \p pset definitely terminates; false if the test is inconclusive. However, if \p pset_before and \p pset_after precisely characterize the effect of the loop body onto the loop-relevant program variables, then true is returned if and only if the loop terminates. */ template bool one_affine_ranking_function_MS_2(const PSET& pset_before, const PSET& pset_after, Generator& mu); /*! \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 minimized_constraints() 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 precisely characterizes the effect of the loop body onto the loop-relevant program variables, then \p mu_space is empty if and only if the loop does not terminate. */ template void all_affine_ranking_functions_MS(const PSET& pset, C_Polyhedron& mu_space); /*! \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 minimized_constraints() method. \param pset_before A pointset approximating the values of loop-relevant variables before 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 after 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 precisely characterize the effect of the loop body onto the loop-relevant program variables, then \p mu_space is empty if and only if the loop does not terminate. */ template void all_affine_ranking_functions_MS_2(const PSET& pset_before, const PSET& pset_after, C_Polyhedron& mu_space); /*! \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 minimized_constraints() 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 precisely 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 if and only if there is no decreasing (resp., lower bounded) affine function, so that the loop does not terminate. */ template void all_affine_quasi_ranking_functions_MS(const PSET& pset, C_Polyhedron& decreasing_mu_space, C_Polyhedron& bounded_mu_space); /*! \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 minimized_constraints() method. \param pset_before A pointset approximating the values of loop-relevant variables before 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 after 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 precisely 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 if and only if there is no decreasing (resp., lower bounded) affine function, so that the loop does not terminate. */ template 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); /*! \brief Like termination_test_MS() but using the method by Podelski and Rybalchenko \ref BMPZ10 "[BMPZ10]". */ template bool termination_test_PR(const PSET& pset); /*! \brief Like termination_test_MS_2() but using an alternative formalization of the method by Podelski and Rybalchenko \ref BMPZ10 "[BMPZ10]". */ template bool termination_test_PR_2(const PSET& pset_before, const PSET& pset_after); /*! \brief Like one_affine_ranking_function_MS() but using the method by Podelski and Rybalchenko \ref BMPZ10 "[BMPZ10]". */ template bool one_affine_ranking_function_PR(const PSET& pset, Generator& mu); /*! \brief Like one_affine_ranking_function_MS_2() but using an alternative formalization of the method by Podelski and Rybalchenko \ref BMPZ10 "[BMPZ10]". */ template bool one_affine_ranking_function_PR_2(const PSET& pset_before, const PSET& pset_after, Generator& mu); /*! \brief Like all_affine_ranking_functions_MS() but using the method by Podelski and Rybalchenko \ref BMPZ10 "[BMPZ10]". */ template void all_affine_ranking_functions_PR(const PSET& pset, NNC_Polyhedron& mu_space); /*! \brief Like all_affine_ranking_functions_MS_2() but using an alternative formalization of the method by Podelski and Rybalchenko \ref BMPZ10 "[BMPZ10]". */ template void all_affine_ranking_functions_PR_2(const PSET& pset_before, const PSET& pset_after, NNC_Polyhedron& mu_space); } // 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 #define PRINT_DEBUG_INFO 0 #if PRINT_DEBUG_INFO #include #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; /* Debuggin 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; /* Debuggin 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 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); void shift_unprimed_variables(Constraint_System& cs); template void assign_all_inequalities_approximation(const PSET& pset_before, const PSET& pset_after, Constraint_System& cs) { assign_all_inequalities_approximation(pset_before, cs); shift_unprimed_variables(cs); Constraint_System cs_after; 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); } 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 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 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; assign_all_inequalities_approximation(pset_before, pset_after, cs); return termination_test_MS(cs); } template 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 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; assign_all_inequalities_approximation(pset_before, pset_after, cs); return one_affine_ranking_function_MS(cs, mu); } template 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 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; assign_all_inequalities_approximation(pset_before, pset_after, cs); all_affine_ranking_functions_MS(cs, mu_space); } template 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 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; assign_all_inequalities_approximation(pset_before, pset_after, cs); all_affine_quasi_ranking_functions_MS(cs, decreasing_mu_space, bounded_mu_space); } template 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 bool termination_test_PR(const PSET& pset_after) { const dimension_type space_dim = pset_after.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_after, cs); return termination_test_PR_original(cs); } template 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 bool one_affine_ranking_function_PR(const PSET& pset_after, Generator& mu) { const dimension_type space_dim = pset_after.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_after, cs); return one_affine_ranking_function_PR_original(cs, mu); } template 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 void all_affine_ranking_functions_PR(const PSET& pset_after, NNC_Polyhedron& mu_space) { const dimension_type space_dim = pset_after.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_after.is_empty()) { mu_space = NNC_Polyhedron(1 + space_dim/2); return; } using namespace Implementation::Termination; Constraint_System cs; assign_all_inequalities_approximation(pset_after, 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 469. */ /* 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 #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 again in order to make the latest changes to // NDEBUG effective. #include #ifdef PPL_NO_AUTOMATIC_INITIALIZATION #undef PPL_NO_AUTOMATIC_INITIALIZATION #endif #endif