#ifndef _DATE_TIME_INT_ADAPTER_HPP__ #define _DATE_TIME_INT_ADAPTER_HPP__ /* Copyright (c) 2002,2003 CrystalClear Software, Inc. * Use, modification and distribution is subject to the * Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) * Author: Jeff Garland, Bart Garst * $Date: 2008-11-12 14:37:53 -0500 (Wed, 12 Nov 2008) $ */ #include "boost/config.hpp" #include "boost/limits.hpp" //work around compilers without limits #include "boost/date_time/special_defs.hpp" #include "boost/date_time/locale_config.hpp" #ifndef BOOST_DATE_TIME_NO_LOCALE # include <ostream> #endif namespace boost { namespace date_time { //! Adapter to create integer types with +-infinity, and not a value /*! This class is used internally in counted date/time representations. * It adds the floating point like features of infinities and * not a number. It also provides mathmatical operations with * consideration to special values following these rules: *@code * +infinity - infinity == Not A Number (NAN) * infinity * non-zero == infinity * infinity * zero == NAN * +infinity * -integer == -infinity * infinity / infinity == NAN * infinity * infinity == infinity *@endcode */ template<typename int_type_> class int_adapter { public: typedef int_type_ int_type; int_adapter(int_type v) : value_(v) {} static bool has_infinity() { return true; } static const int_adapter pos_infinity() { return (::std::numeric_limits<int_type>::max)(); } static const int_adapter neg_infinity() { return (::std::numeric_limits<int_type>::min)(); } static const int_adapter not_a_number() { return (::std::numeric_limits<int_type>::max)()-1; } static int_adapter max BOOST_PREVENT_MACRO_SUBSTITUTION () { return (::std::numeric_limits<int_type>::max)()-2; } static int_adapter min BOOST_PREVENT_MACRO_SUBSTITUTION () { return (::std::numeric_limits<int_type>::min)()+1; } static int_adapter from_special(special_values sv) { switch (sv) { case not_a_date_time: return not_a_number(); case neg_infin: return neg_infinity(); case pos_infin: return pos_infinity(); case max_date_time: return (max)(); case min_date_time: return (min)(); default: return not_a_number(); } } static bool is_inf(int_type v) { return (v == neg_infinity().as_number() || v == pos_infinity().as_number()); } static bool is_neg_inf(int_type v) { return (v == neg_infinity().as_number()); } static bool is_pos_inf(int_type v) { return (v == pos_infinity().as_number()); } static bool is_not_a_number(int_type v) { return (v == not_a_number().as_number()); } //! Returns either special value type or is_not_special static special_values to_special(int_type v) { if (is_not_a_number(v)) return not_a_date_time; if (is_neg_inf(v)) return neg_infin; if (is_pos_inf(v)) return pos_infin; return not_special; } //-3 leaves room for representations of infinity and not a date static int_type maxcount() { return (::std::numeric_limits<int_type>::max)()-3; } bool is_infinity() const { return (value_ == neg_infinity().as_number() || value_ == pos_infinity().as_number()); } bool is_pos_infinity()const { return(value_ == pos_infinity().as_number()); } bool is_neg_infinity()const { return(value_ == neg_infinity().as_number()); } bool is_nan() const { return (value_ == not_a_number().as_number()); } bool is_special() const { return(is_infinity() || is_nan()); } bool operator==(const int_adapter& rhs) const { return (compare(rhs) == 0); } bool operator==(const int& rhs) const { // quiets compiler warnings bool is_signed = std::numeric_limits<int_type>::is_signed; if(!is_signed) { if(is_neg_inf(value_) && rhs == 0) { return false; } } return (compare(rhs) == 0); } bool operator!=(const int_adapter& rhs) const { return (compare(rhs) != 0); } bool operator!=(const int& rhs) const { // quiets compiler warnings bool is_signed = std::numeric_limits<int_type>::is_signed; if(!is_signed) { if(is_neg_inf(value_) && rhs == 0) { return true; } } return (compare(rhs) != 0); } bool operator<(const int_adapter& rhs) const { return (compare(rhs) == -1); } bool operator<(const int& rhs) const { // quiets compiler warnings bool is_signed = std::numeric_limits<int_type>::is_signed; if(!is_signed) { if(is_neg_inf(value_) && rhs == 0) { return true; } } return (compare(rhs) == -1); } bool operator>(const int_adapter& rhs) const { return (compare(rhs) == 1); } int_type as_number() const { return value_; } //! Returns either special value type or is_not_special special_values as_special() const { return int_adapter::to_special(value_); } //creates nasty ambiguities // operator int_type() const // { // return value_; // } /*! Operator allows for adding dissimilar int_adapter types. * The return type will match that of the the calling object's type */ template<class rhs_type> inline int_adapter operator+(const int_adapter<rhs_type>& rhs) const { if(is_special() || rhs.is_special()) { if (is_nan() || rhs.is_nan()) { return int_adapter::not_a_number(); } if((is_pos_inf(value_) && rhs.is_neg_inf(rhs.as_number())) || (is_neg_inf(value_) && rhs.is_pos_inf(rhs.as_number())) ) { return int_adapter::not_a_number(); } if (is_infinity()) { return *this; } if (rhs.is_pos_inf(rhs.as_number())) { return int_adapter::pos_infinity(); } if (rhs.is_neg_inf(rhs.as_number())) { return int_adapter::neg_infinity(); } } return int_adapter<int_type>(value_ + rhs.as_number()); } int_adapter operator+(const int_type rhs) const { if(is_special()) { if (is_nan()) { return int_adapter<int_type>(not_a_number()); } if (is_infinity()) { return *this; } } return int_adapter<int_type>(value_ + rhs); } /*! Operator allows for subtracting dissimilar int_adapter types. * The return type will match that of the the calling object's type */ template<class rhs_type> inline int_adapter operator-(const int_adapter<rhs_type>& rhs)const { if(is_special() || rhs.is_special()) { if (is_nan() || rhs.is_nan()) { return int_adapter::not_a_number(); } if((is_pos_inf(value_) && rhs.is_pos_inf(rhs.as_number())) || (is_neg_inf(value_) && rhs.is_neg_inf(rhs.as_number())) ) { return int_adapter::not_a_number(); } if (is_infinity()) { return *this; } if (rhs.is_pos_inf(rhs.as_number())) { return int_adapter::neg_infinity(); } if (rhs.is_neg_inf(rhs.as_number())) { return int_adapter::pos_infinity(); } } return int_adapter<int_type>(value_ - rhs.as_number()); } int_adapter operator-(const int_type rhs) const { if(is_special()) { if (is_nan()) { return int_adapter<int_type>(not_a_number()); } if (is_infinity()) { return *this; } } return int_adapter<int_type>(value_ - rhs); } // should templatize this to be consistant with op +- int_adapter operator*(const int_adapter& rhs)const { if(this->is_special() || rhs.is_special()) { return mult_div_specials(rhs); } return int_adapter<int_type>(value_ * rhs.value_); } /*! Provided for cases when automatic conversion from * 'int' to 'int_adapter' causes incorrect results. */ int_adapter operator*(const int rhs) const { if(is_special()) { return mult_div_specials(rhs); } return int_adapter<int_type>(value_ * rhs); } // should templatize this to be consistant with op +- int_adapter operator/(const int_adapter& rhs)const { if(this->is_special() || rhs.is_special()) { if(is_infinity() && rhs.is_infinity()) { return int_adapter<int_type>(not_a_number()); } if(rhs != 0) { return mult_div_specials(rhs); } else { // let divide by zero blow itself up return int_adapter<int_type>(value_ / rhs.value_); } } return int_adapter<int_type>(value_ / rhs.value_); } /*! Provided for cases when automatic conversion from * 'int' to 'int_adapter' causes incorrect results. */ int_adapter operator/(const int rhs) const { if(is_special() && rhs != 0) { return mult_div_specials(rhs); } return int_adapter<int_type>(value_ / rhs); } // should templatize this to be consistant with op +- int_adapter operator%(const int_adapter& rhs)const { if(this->is_special() || rhs.is_special()) { if(is_infinity() && rhs.is_infinity()) { return int_adapter<int_type>(not_a_number()); } if(rhs != 0) { return mult_div_specials(rhs); } else { // let divide by zero blow itself up return int_adapter<int_type>(value_ % rhs.value_); } } return int_adapter<int_type>(value_ % rhs.value_); } /*! Provided for cases when automatic conversion from * 'int' to 'int_adapter' causes incorrect results. */ int_adapter operator%(const int rhs) const { if(is_special() && rhs != 0) { return mult_div_specials(rhs); } return int_adapter<int_type>(value_ % rhs); } private: int_type value_; //! returns -1, 0, 1, or 2 if 'this' is <, ==, >, or 'nan comparison' rhs int compare(const int_adapter& rhs)const { if(this->is_special() || rhs.is_special()) { if(this->is_nan() || rhs.is_nan()) { if(this->is_nan() && rhs.is_nan()) { return 0; // equal } else { return 2; // nan } } if((is_neg_inf(value_) && !is_neg_inf(rhs.value_)) || (is_pos_inf(rhs.value_) && !is_pos_inf(value_)) ) { return -1; // less than } if((is_pos_inf(value_) && !is_pos_inf(rhs.value_)) || (is_neg_inf(rhs.value_) && !is_neg_inf(value_)) ) { return 1; // greater than } } if(value_ < rhs.value_) return -1; if(value_ > rhs.value_) return 1; // implied-> if(value_ == rhs.value_) return 0; } /* When multiplying and dividing with at least 1 special value * very simmilar rules apply. In those cases where the rules * are different, they are handled in the respective operator * function. */ //! Assumes at least 'this' or 'rhs' is a special value int_adapter mult_div_specials(const int_adapter& rhs)const { int min_value; // quiets compiler warnings bool is_signed = std::numeric_limits<int_type>::is_signed; if(is_signed) { min_value = 0; } else { min_value = 1;// there is no zero with unsigned } if(this->is_nan() || rhs.is_nan()) { return int_adapter<int_type>(not_a_number()); } if((*this > 0 && rhs > 0) || (*this < min_value && rhs < min_value)) { return int_adapter<int_type>(pos_infinity()); } if((*this > 0 && rhs < min_value) || (*this < min_value && rhs > 0)) { return int_adapter<int_type>(neg_infinity()); } //implied -> if(this->value_ == 0 || rhs.value_ == 0) return int_adapter<int_type>(not_a_number()); } /* Overloaded function necessary because of special * situation where int_adapter is instantiated with * 'unsigned' and func is called with negative int. * It would produce incorrect results since 'unsigned' * wraps around when initialized with a negative value */ //! Assumes 'this' is a special value int_adapter mult_div_specials(const int& rhs) const { int min_value; // quiets compiler warnings bool is_signed = std::numeric_limits<int_type>::is_signed; if(is_signed) { min_value = 0; } else { min_value = 1;// there is no zero with unsigned } if(this->is_nan()) { return int_adapter<int_type>(not_a_number()); } if((*this > 0 && rhs > 0) || (*this < min_value && rhs < 0)) { return int_adapter<int_type>(pos_infinity()); } if((*this > 0 && rhs < 0) || (*this < min_value && rhs > 0)) { return int_adapter<int_type>(neg_infinity()); } //implied -> if(this->value_ == 0 || rhs.value_ == 0) return int_adapter<int_type>(not_a_number()); } }; #ifndef BOOST_DATE_TIME_NO_LOCALE /*! Expected output is either a numeric representation * or a special values representation.<BR> * Ex. "12", "+infinity", "not-a-number", etc. */ //template<class charT = char, class traits = std::traits<charT>, typename int_type> template<class charT, class traits, typename int_type> inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, const int_adapter<int_type>& ia) { if(ia.is_special()) { // switch copied from date_names_put.hpp switch(ia.as_special()) { case not_a_date_time: os << "not-a-number"; break; case pos_infin: os << "+infinity"; break; case neg_infin: os << "-infinity"; break; default: os << ""; } } else { os << ia.as_number(); } return os; } #endif } } //namespace date_time #endif