blob: 8911415c2f33a29523232c1b9cb29db3cae85354 [file] [log] [blame]
#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