#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 |