/* Checked extended arithmetic functions. 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_checked_ext_defs_hh #define PPL_checked_ext_defs_hh 1 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 #endif // !defined(PPL_checked_ext_defs_hh)