/* Helper functions for checked numbers. 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/ . */ #include "ppl-config.h" #include "checked_defs.hh" #include "C_Integer.hh" namespace Parma_Polyhedra_Library { Minus_Infinity MINUS_INFINITY; Plus_Infinity PLUS_INFINITY; Not_A_Number NOT_A_NUMBER; namespace Checked { //! Holds the precision parameter used for irrational calculations. unsigned irrational_precision; struct number_struct { unsigned int base; bool neg_mantissa; bool neg_exponent; std::string mantissa; unsigned int base_for_exponent; unsigned long exponent; }; /*! \brief Returns the integer value associated with the ASCII code \p c, in the base \p base positional number system, if there is such an association; returns \f$-1\f$ otherwise. */ inline int get_digit(char c, unsigned int base = 10) { unsigned int n; switch (c) { case '0': n = 0; break; case '1': n = 1; break; case '2': n = 2; break; case '3': n = 3; break; case '4': n = 4; break; case '5': n = 5; break; case '6': n = 6; break; case '7': n = 7; break; case '8': n = 8; break; case '9': n = 9; break; case 'a': case 'A': n = 10; break; case 'b': case 'B': n = 11; break; case 'c': case 'C': n = 12; break; case 'd': case 'D': n = 13; break; case 'e': case 'E': n = 14; break; case 'f': case 'F': n = 15; break; case 'g': case 'G': n = 16; break; case 'h': case 'H': n = 17; break; case 'i': case 'I': n = 18; break; case 'j': case 'J': n = 19; break; case 'k': case 'K': n = 20; break; case 'l': case 'L': n = 21; break; case 'm': case 'M': n = 22; break; case 'n': case 'N': n = 23; break; case 'o': case 'O': n = 24; break; case 'p': case 'P': n = 25; break; case 'q': case 'Q': n = 26; break; case 'r': case 'R': n = 27; break; case 's': case 'S': n = 28; break; case 't': case 'T': n = 29; break; case 'u': case 'U': n = 30; break; case 'v': case 'V': n = 31; break; case 'w': case 'W': n = 32; break; case 'x': case 'X': n = 33; break; case 'y': case 'Y': n = 34; break; case 'z': case 'Z': n = 35; break; default: return -1; } if (n >= base) { return -1; } return static_cast(n); } /*! \brief Adds the number represented (in the modulus-and-sign representation) by \p b_neg and \p b_mod to the number represented by \p a_neg and \p a_mod, assigning the result to the latter. Returns false is the result cannot be represented; returns true otherwise. */ inline bool sum_sign(bool& a_neg, unsigned long& a_mod, bool b_neg, unsigned long b_mod) { if (a_neg == b_neg) { if (a_mod > C_Integer::max - b_mod) { return false; } a_mod += b_mod; } else if (a_mod >= b_mod) { a_mod -= b_mod; } else { a_neg = !a_neg; a_mod = b_mod - a_mod; } return true; } /*! \brief Helper function for parse_number(): reads the numerator or denominator part of a number from \p is into \p numer, returning the appropriate Result value. */ Result parse_number_part(std::istream& is, number_struct& numer) { enum anonymous_enum { BASE, INTEGER, FRACTIONAL, EXPONENT } state = BASE; PPL_UNINITIALIZED(unsigned long, max_exp_div); PPL_UNINITIALIZED(int, max_exp_rem); bool empty_exponent = true; bool empty_mantissa = true; long exponent_offset = 0; unsigned exponent_offset_scale = 1; numer.base = 10; numer.base_for_exponent = 10; numer.neg_mantissa = false; numer.neg_exponent = false; numer.mantissa.erase(); numer.exponent = 0; char c; do { if (!is.get(c)) { return V_CVT_STR_UNK; } } while (is_space(c)); switch (c) { case '-': numer.neg_mantissa = true; // Fall through. case '+': if (!is.get(c)) { return V_CVT_STR_UNK; } if (c == 'i' || c == 'I') { goto infinity; } if (c != '.') { break; } // Fall through. case '.': state = FRACTIONAL; if (!is.get(c)) { return V_CVT_STR_UNK; } break; case 'n': case 'N': if (!is.get(c)) { return V_CVT_STR_UNK; } if (c != 'a' && c != 'A') { goto unexpected_char; } if (!is.get(c)) { return V_CVT_STR_UNK; } if (c != 'n' && c != 'N') { goto unexpected_char; } return V_NAN; infinity: case 'i': case 'I': if (!is.get(c)) { return V_CVT_STR_UNK; } if (c != 'n' && c != 'n') { goto unexpected_char; } if (!is.get(c)) { return V_CVT_STR_UNK; } if (c != 'f' && c != 'F') { goto unexpected_char; } return numer.neg_mantissa ? V_EQ_MINUS_INFINITY : V_EQ_PLUS_INFINITY; } if (state != FRACTIONAL) { if (get_digit(c, 10) < 0) { goto unexpected_char; } char d; if (c == '0' && !is.get(d).fail()) { if (d == 'x' || d == 'X') { numer.base = 16; numer.base_for_exponent = 16; state = INTEGER; if (!is.get(c)) { return V_CVT_STR_UNK; } } else { is.unget(); } } } do { switch (state) { case BASE: if (get_digit(c, 10) >= 0) { if (c != '0' || !numer.mantissa.empty()) { numer.mantissa += c; } empty_mantissa = false; break; } if (c == '^') { if (!is.get(c)) { return V_CVT_STR_UNK; } if (c != '^') { goto unexpected_char; } numer.base = 0; for (std::string::const_iterator i = numer.mantissa.begin(); i != numer.mantissa.end(); ++i) { numer.base = numer.base * 10 + static_cast(get_digit(*i, 10)); if (numer.base > 36) { goto unexpected_char; } } if (numer.base < 2) { goto unexpected_char; } numer.base_for_exponent = numer.base; numer.mantissa.erase(); empty_mantissa = true; state = INTEGER; break; } goto integer; case INTEGER: if (get_digit(c, numer.base) >= 0) { if (c != '0' || !numer.mantissa.empty()) { numer.mantissa += c; } empty_mantissa = false; break; } integer: if (c == '.') { state = FRACTIONAL; break; } goto fractional; case FRACTIONAL: if (get_digit(c, numer.base) >= 0) { --exponent_offset; if (c != '0' || !numer.mantissa.empty()) { numer.mantissa += c; } empty_mantissa = false; break; } fractional: if (empty_mantissa) { goto unexpected_char; } if (c == 'e' || c == 'E') { goto exponent; } if (c == 'p' || c == 'P') { if (numer.base == 16) { numer.base_for_exponent = 2; exponent_offset_scale = 4; goto exponent; } else { goto unexpected_char; } } if (c == '*') { if (!is.get(c)) { return V_CVT_STR_UNK; } if (c != '^') { goto unexpected_char; } exponent: state = EXPONENT; PPL_ASSERT(numer.base >= 2); const long l_max = C_Integer::max; max_exp_div = static_cast(l_max) / numer.base; max_exp_rem = static_cast(l_max % static_cast(numer.base)); if (!is.get(c)) { return V_CVT_STR_UNK; } if (c == '-') { numer.neg_exponent = true; break; } if (c == '+') { break; } continue; } is.unget(); goto ok; case EXPONENT: const int d = get_digit(c, 10); if (d >= 0) { empty_exponent = false; if (numer.exponent > max_exp_div || (numer.exponent == max_exp_div && d > max_exp_rem)) { return V_CVT_STR_UNK; } numer.exponent = 10 * numer.exponent + static_cast(d); break; } if (empty_exponent) { goto unexpected_char; } is.unget(); goto ok; } is.get(c); } while (!is.fail()); if (empty_mantissa || is.bad()) { return V_CVT_STR_UNK; } ok: { std::string::size_type n = numer.mantissa.size(); while (n > 0 && numer.mantissa[n - 1] == '0') { --n; ++exponent_offset; } numer.mantissa.erase(n); bool neg; if (exponent_offset < 0) { neg = true; exponent_offset = -exponent_offset; } else { neg = false; } sum_sign(numer.neg_exponent, numer.exponent, neg, static_cast(exponent_offset) * exponent_offset_scale); return V_EQ; } unexpected_char: is.unget(); return V_CVT_STR_UNK; } /*! \brief Reads a number from \p is writing it into \p numer, the numerator, and \p denom, the denominator; the appropriate Result value is returned. */ Result parse_number(std::istream& is, number_struct& numer, number_struct& denom) { // Read the numerator. Result r = parse_number_part(is, numer); if (r != V_EQ) { return r; } char c; is.get(c); if (is.bad()) { return V_CVT_STR_UNK; } if (!is) { denom.base = 0; return r; } if (c != '/') { is.unget(); denom.base = 0; return r; } // Read the denominator. r = parse_number_part(is, denom); if (r != V_EQ) { return V_CVT_STR_UNK; } if (numer.base == denom.base && numer.base_for_exponent == denom.base_for_exponent) { if (sum_sign(numer.neg_exponent, numer.exponent, !denom.neg_exponent, denom.exponent)) { if (numer.neg_exponent) { denom.neg_exponent = false; denom.exponent = numer.exponent; numer.exponent = 0; } else { denom.exponent = 0; } } } return V_EQ; } Result input_mpq(mpq_class& to, std::istream& is) { number_struct numer_struct; number_struct denom_struct; const Result r = parse_number(is, numer_struct, denom_struct); if (r == V_CVT_STR_UNK) { is.setstate(is.failbit); return r; } is.clear(is.rdstate() & ~is.failbit); if (r != V_EQ) { return r; } if (denom_struct.base != 0 && denom_struct.mantissa.empty()) { return V_NAN; } if (numer_struct.mantissa.empty()) { to = 0; return V_EQ; } const mpz_ptr numer = to.get_num().get_mpz_t(); const mpz_ptr denom = to.get_den().get_mpz_t(); mpz_set_str(numer, numer_struct.mantissa.c_str(), static_cast(numer_struct.base)); if (denom_struct.base != 0) { if (numer_struct.neg_mantissa != denom_struct.neg_mantissa) { mpz_neg(numer, numer); } mpz_set_str(denom, denom_struct.mantissa.c_str(), static_cast(denom_struct.base)); if (numer_struct.exponent != 0 || denom_struct.exponent != 0) { // Multiply the exponents into the numerator and denominator. mpz_t z; mpz_init(z); if (numer_struct.exponent != 0) { mpz_ui_pow_ui(z, numer_struct.base_for_exponent, numer_struct.exponent); if (numer_struct.neg_exponent) { mpz_mul(denom, denom, z); } else { mpz_mul(numer, numer, z); } } if (denom_struct.exponent != 0) { mpz_ui_pow_ui(z, denom_struct.base_for_exponent, denom_struct.exponent); if (denom_struct.neg_exponent) { mpz_mul(numer, numer, z); } else { mpz_mul(denom, denom, z); } } mpz_clear(z); } } else { if (numer_struct.neg_mantissa) { mpz_neg(numer, numer); } if (numer_struct.exponent != 0) { if (numer_struct.neg_exponent) { // Add the negative exponent as a denominator. mpz_ui_pow_ui(denom, numer_struct.base_for_exponent, numer_struct.exponent); goto end; } // Multiply the exponent into the numerator. mpz_t z; mpz_init(z); mpz_ui_pow_ui(z, numer_struct.base_for_exponent, numer_struct.exponent); mpz_mul(numer, numer, z); mpz_clear(z); } mpz_set_ui(denom, 1); return V_EQ; } end: // GMP operators require rationals in canonical form. to.canonicalize(); return V_EQ; } /* NOTE: q is overwritten! */ std::string float_mpq_to_string(mpq_class& q) { const mpz_ptr n = q.get_num().get_mpz_t(); const mpz_ptr d = q.get_den().get_mpz_t(); const unsigned long decimals = mpz_sizeinbase(d, 2) - 1; if (decimals != 0) { mpz_ui_pow_ui(d, 5, decimals); mpz_mul(n, n, d); } size_t bufsize = mpz_sizeinbase(n, 10); if (bufsize < decimals) { bufsize = decimals + 4; } else { bufsize += 3; } char buf[bufsize]; mpz_get_str(buf, 10, n); if (decimals != 0) { const size_t len = strlen(buf); if (decimals < len) { memmove(&buf[len - decimals + 1], &buf[len - decimals], decimals + 1); buf[len - decimals] = '.'; } else { const size_t zeroes = decimals - len; memmove(&buf[2 + zeroes], &buf[0], len + 1); buf[0] = '0'; buf[1] = '.'; memset(&buf[2], '0', zeroes); } } return buf; } } // namespace Checked } // namespace Parma_Polyhedra_Library