/* Interval boundary functions. Copyright (C) 2001-2010 Roberto Bagnara Copyright (C) 2010-2016 BUGSENG srl (http://bugseng.com) This file is part of the Parma Polyhedra Library (PPL). The PPL is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. The PPL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA. For the most up-to-date information see the Parma Polyhedra Library site: http://bugseng.com/products/ppl/ . */ #ifndef PPL_Boundary_defs_hh #define PPL_Boundary_defs_hh 1 #include "Checked_Number_defs.hh" namespace Parma_Polyhedra_Library { namespace Boundary_NS { struct Property { enum Type { SPECIAL_, OPEN_ }; typedef bool Value; static const Value default_value = true; static const Value unsupported_value = false; Property(Type t) : type(t) { } Type type; }; static const Property SPECIAL(Property::SPECIAL_); static const Property OPEN(Property::OPEN_); enum Boundary_Type { LOWER = ROUND_DOWN, UPPER = ROUND_UP }; inline Rounding_Dir round_dir_check(Boundary_Type t, bool check = false) { if (check) { return static_cast(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) { 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) { 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 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 infinity_sign(Boundary_Type type, const T& x, const Info& info) { if (is_boundary_infinity(type, x, info)) { return (type == LOWER) ? -1 : 1; } else if (is_reverse_infinity(type, x, info)) { return (type == UPPER) ? -1 : 1; } else { return 0; } } template 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) { info.set_boundary_property(type, OPEN); } return r; default: PPL_UNREACHABLE; return V_NAN; } } else { switch (r) { case V_LT_PLUS_INFINITY: open = true; /* Fall through */ case V_EQ_PLUS_INFINITY: if (!Info::store_special) { return r; } if (open) { info.set_boundary_property(type, OPEN); } return special_set_boundary_infinity(type, x, info); case V_LT: open = true; /* Fall through */ case V_LE: case V_EQ: if (open) { info.set_boundary_property(type, OPEN); } return r; default: PPL_UNREACHABLE; return V_NAN; } } } template inline Result complement(Boundary_Type to_type, To& to, To_Info& to_info, Boundary_Type type, const T& x, const Info& info) { PPL_ASSERT(to_type != type); bool should_shrink; if (info.get_boundary_property(type, SPECIAL)) { should_shrink = !special_is_open(type, x, info); if (type == LOWER) { return set_minus_infinity(to_type, to, to_info, should_shrink); } else { return set_plus_infinity(to_type, to, to_info, should_shrink); } } should_shrink = !normal_is_open(type, x, info); bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open)); Result r = assign_r(to, x, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, should_shrink, r); } template inline Result assign(Boundary_Type to_type, To& to, To_Info& to_info, Boundary_Type type, const T& x, const Info& info, bool should_shrink = false) { PPL_ASSERT(to_type == type); if (info.get_boundary_property(type, SPECIAL)) { should_shrink = (should_shrink || special_is_open(type, x, info)); return set_boundary_infinity(to_type, to, to_info, should_shrink); } should_shrink = (should_shrink || normal_is_open(type, x, info)); const bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open)); const Result r = assign_r(to, x, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, should_shrink, r); } template 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 should_shrink; if (info.get_boundary_property(type, SPECIAL)) { should_shrink = special_is_open(type, x, info); return set_boundary_infinity(to_type, to, to_info, should_shrink); } should_shrink = normal_is_open(type, x, info); bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open)); Result r = neg_assign_r(to, x, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, should_shrink, r); } template inline Result add_assign(Boundary_Type to_type, To& to, To_Info& to_info, Boundary_Type type1, const T1& x1, const Info1& info1, Boundary_Type type2, const T2& x2, const Info2& info2) { PPL_ASSERT(type1 == type2); bool should_shrink; if (is_boundary_infinity(type1, x1, info1)) { should_shrink = (boundary_infinity_is_open(type1, info1) && !is_boundary_infinity_closed(type2, x2, info2)); return set_boundary_infinity(to_type, to, to_info, should_shrink); } else if (is_boundary_infinity(type2, x2, info2)) { should_shrink = (boundary_infinity_is_open(type2, info2) && !is_boundary_infinity_closed(type1, x1, info1)); return set_boundary_infinity(to_type, to, to_info, should_shrink); } should_shrink = (normal_is_open(type1, x1, info1) || normal_is_open(type2, x2, info2)); bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open)); // FIXME: extended handling is not needed Result r = add_assign_r(to, x1, x2, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, should_shrink, r); } template inline Result sub_assign(Boundary_Type to_type, To& to, To_Info& to_info, Boundary_Type type1, const T1& x1, const Info1& info1, Boundary_Type type2, const T2& x2, const Info2& info2) { PPL_ASSERT(type1 != type2); bool should_shrink; if (is_boundary_infinity(type1, x1, info1)) { should_shrink = (boundary_infinity_is_open(type1, info1) && !is_boundary_infinity_closed(type2, x2, info2)); return set_boundary_infinity(to_type, to, to_info, should_shrink); } else if (is_boundary_infinity(type2, x2, info2)) { should_shrink = (boundary_infinity_is_open(type2, info2) && !is_boundary_infinity_closed(type1, x1, info1)); return set_boundary_infinity(to_type, to, to_info, should_shrink); } should_shrink = (normal_is_open(type1, x1, info1) || normal_is_open(type2, x2, info2)); bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open)); // FIXME: extended handling is not needed Result r = sub_assign_r(to, x1, x2, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, should_shrink, r); } template inline Result mul_assign(Boundary_Type to_type, To& to, To_Info& to_info, Boundary_Type type1, const T1& x1, const Info1& info1, Boundary_Type type2, const T2& x2, const Info2& info2) { bool should_shrink; if (is_boundary_infinity(type1, x1, info1)) { should_shrink = (boundary_infinity_is_open(type1, info1) && !is_boundary_infinity_closed(type2, x2, info2)); return set_boundary_infinity(to_type, to, to_info, should_shrink); } else if (is_boundary_infinity(type2, x2, info2)) { should_shrink = (boundary_infinity_is_open(type2, info2) && !is_boundary_infinity_closed(type1, x1, info1)); return set_boundary_infinity(to_type, to, to_info, should_shrink); } should_shrink = (normal_is_open(type1, x1, info1) || normal_is_open(type2, x2, info2)); bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open)); PPL_ASSERT(x1 != Constant<0>::value && x2 != Constant<0>::value); // FIXME: extended handling is not needed Result r = mul_assign_r(to, x1, x2, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, should_shrink, r); } template inline Result set_zero(Boundary_Type to_type, To& to, To_Info& to_info, bool should_shrink) { bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open)); Result r = assign_r(to, Constant<0>::value, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, should_shrink, r); } template inline Result mul_assign_z(Boundary_Type to_type, To& to, To_Info& to_info, Boundary_Type type1, const T1& x1, const Info1& info1, int x1s, Boundary_Type type2, const T2& x2, const Info2& info2, int x2s) { bool should_shrink; if (x1s != 0) { if (x2s != 0) { return mul_assign(to_type, to, to_info, type1, x1, info1, type2, x2, info2); } else { should_shrink = info2.get_boundary_property(type2, OPEN); } } else { should_shrink = (info1.get_boundary_property(type1, OPEN) && (x2s != 0 || info2.get_boundary_property(type2, OPEN))); } return set_zero(to_type, to, to_info, should_shrink); } template inline Result div_assign(Boundary_Type to_type, To& to, To_Info& to_info, Boundary_Type type1, const T1& x1, const Info1& info1, Boundary_Type type2, const T2& x2, const Info2& info2) { bool should_shrink; if (is_boundary_infinity(type1, x1, info1)) { should_shrink = boundary_infinity_is_open(type1, info1); return set_boundary_infinity(to_type, to, to_info, should_shrink); } else if (is_boundary_infinity(type2, x2, info2)) { should_shrink = boundary_infinity_is_open(type2, info2); return set_zero(to_type, to, to_info, should_shrink); } should_shrink = (normal_is_open(type1, x1, info1) || normal_is_open(type2, x2, info2)); bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open)); PPL_ASSERT(x1 != Constant<0>::value && x2 != Constant<0>::value); // FIXME: extended handling is not needed Result r = div_assign_r(to, x1, x2, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, should_shrink, r); } template inline Result div_assign_z(Boundary_Type to_type, To& to, To_Info& to_info, Boundary_Type type1, const T1& x1, const Info1& info1, int x1s, Boundary_Type type2, const T2& x2, const Info2& info2, int x2s) { if (x1s != 0) { if (x2s != 0) { return div_assign(to_type, to, to_info, type1, x1, info1, type2, x2, info2); } else { return set_boundary_infinity(to_type, to, to_info, true); } } else { bool should_shrink = info1.get_boundary_property(type1, OPEN) && !is_boundary_infinity_closed(type2, x2, info2); return set_zero(to_type, to, to_info, should_shrink); } } template inline Result umod_2exp_assign(Boundary_Type to_type, To& to, To_Info& to_info, Boundary_Type type, const T& x, const Info& info, unsigned int exp) { PPL_ASSERT(to_type == type); bool should_shrink; if (is_boundary_infinity(type, x, info)) { should_shrink = boundary_infinity_is_open(type, info); return set_boundary_infinity(to_type, to, to_info, should_shrink); } should_shrink = normal_is_open(type, x, info); bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open)); Result r = umod_2exp_assign_r(to, x, exp, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, should_shrink, r); } template inline Result smod_2exp_assign(Boundary_Type to_type, To& to, To_Info& to_info, Boundary_Type type, const T& x, const Info& info, unsigned int exp) { PPL_ASSERT(to_type == type); bool should_shrink; if (is_boundary_infinity(type, x, info)) { should_shrink = boundary_infinity_is_open(type, info); return set_boundary_infinity(to_type, to, to_info, should_shrink); } should_shrink = normal_is_open(type, x, info); bool check = (To_Info::check_inexact || (!should_shrink && To_Info::store_open)); Result r = smod_2exp_assign_r(to, x, exp, round_dir_check(to_type, check)); return adjust_boundary(to_type, to, to_info, should_shrink, r); } } // namespace Boundary_NS } // namespace Parma_Polyhedra_Library #endif // !defined(PPL_Boundary_defs_hh)