1 #ifndef _DATE_TIME_INT_ADAPTER_HPP__
2 #define _DATE_TIME_INT_ADAPTER_HPP__
3 
4 /* Copyright (c) 2002,2003 CrystalClear Software, Inc.
5  * Use, modification and distribution is subject to the
6  * Boost Software License, Version 1.0. (See accompanying
7  * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
8  * Author: Jeff Garland, Bart Garst
9  * $Date$
10  */
11 
12 
13 #include "boost/config.hpp"
14 #include "boost/limits.hpp" //work around compilers without limits
15 #include "boost/date_time/special_defs.hpp"
16 #include "boost/date_time/locale_config.hpp"
17 #ifndef BOOST_DATE_TIME_NO_LOCALE
18 #  include <ostream>
19 #endif
20 
21 #if defined(BOOST_MSVC)
22 #pragma warning(push)
23 // conditional expression is constant
24 #pragma warning(disable: 4127)
25 #endif
26 
27 namespace boost {
28 namespace date_time {
29 
30 
31 //! Adapter to create integer types with +-infinity, and not a value
32 /*! This class is used internally in counted date/time representations.
33  *  It adds the floating point like features of infinities and
34  *  not a number. It also provides mathmatical operations with
35  *  consideration to special values following these rules:
36  *@code
37  *  +infinity  -  infinity  == Not A Number (NAN)
38  *   infinity  *  non-zero  == infinity
39  *   infinity  *  zero      == NAN
40  *  +infinity  * -integer   == -infinity
41  *   infinity  /  infinity  == NAN
42  *   infinity  *  infinity  == infinity
43  *@endcode
44  */
45 template<typename int_type_>
46 class int_adapter {
47 public:
48   typedef int_type_ int_type;
int_adapter(int_type v)49   BOOST_CXX14_CONSTEXPR int_adapter(int_type v) :
50     value_(v)
51   {}
has_infinity()52   static BOOST_CONSTEXPR bool has_infinity()
53   {
54     return  true;
55   }
pos_infinity()56   static BOOST_CONSTEXPR int_adapter  pos_infinity()
57   {
58     return (::std::numeric_limits<int_type>::max)();
59   }
neg_infinity()60   static BOOST_CONSTEXPR int_adapter  neg_infinity()
61   {
62     return (::std::numeric_limits<int_type>::min)();
63   }
not_a_number()64   static BOOST_CONSTEXPR int_adapter  not_a_number()
65   {
66     return (::std::numeric_limits<int_type>::max)()-1;
67   }
BOOST_PREVENT_MACRO_SUBSTITUTION()68   static BOOST_CONSTEXPR int_adapter max BOOST_PREVENT_MACRO_SUBSTITUTION ()
69   {
70     return (::std::numeric_limits<int_type>::max)()-2;
71   }
BOOST_PREVENT_MACRO_SUBSTITUTION()72   static BOOST_CONSTEXPR int_adapter min BOOST_PREVENT_MACRO_SUBSTITUTION ()
73   {
74     return (::std::numeric_limits<int_type>::min)()+1;
75   }
from_special(special_values sv)76   static BOOST_CXX14_CONSTEXPR int_adapter from_special(special_values sv)
77   {
78     switch (sv) {
79     case not_a_date_time: return not_a_number();
80     case neg_infin:       return neg_infinity();
81     case pos_infin:       return pos_infinity();
82     case max_date_time:   return (max)();
83     case min_date_time:   return (min)();
84     default:              return not_a_number();
85     }
86   }
is_inf(int_type v)87   static BOOST_CONSTEXPR bool is_inf(int_type v)
88   {
89     return (v == neg_infinity().as_number() ||
90             v == pos_infinity().as_number());
91   }
is_neg_inf(int_type v)92   static BOOST_CXX14_CONSTEXPR bool is_neg_inf(int_type v)
93   {
94     return (v == neg_infinity().as_number());
95   }
is_pos_inf(int_type v)96   static BOOST_CXX14_CONSTEXPR bool is_pos_inf(int_type v)
97   {
98     return (v == pos_infinity().as_number());
99   }
is_not_a_number(int_type v)100   static BOOST_CXX14_CONSTEXPR bool is_not_a_number(int_type v)
101   {
102     return (v == not_a_number().as_number());
103   }
104   //! Returns either special value type or is_not_special
to_special(int_type v)105   static BOOST_CXX14_CONSTEXPR special_values to_special(int_type v)
106   {
107     if (is_not_a_number(v)) return not_a_date_time;
108     if (is_neg_inf(v)) return neg_infin;
109     if (is_pos_inf(v)) return pos_infin;
110     return not_special;
111   }
112 
113   //-3 leaves room for representations of infinity and not a date
maxcount()114   static BOOST_CONSTEXPR int_type maxcount()
115   {
116     return (::std::numeric_limits<int_type>::max)()-3;
117   }
is_infinity() const118   BOOST_CONSTEXPR bool is_infinity() const
119   {
120     return (value_ == neg_infinity().as_number() ||
121             value_ == pos_infinity().as_number());
122   }
is_pos_infinity() const123   BOOST_CONSTEXPR bool is_pos_infinity()const
124   {
125     return(value_ == pos_infinity().as_number());
126   }
is_neg_infinity() const127   BOOST_CONSTEXPR bool is_neg_infinity()const
128   {
129     return(value_ == neg_infinity().as_number());
130   }
is_nan() const131   BOOST_CONSTEXPR bool is_nan() const
132   {
133     return (value_ == not_a_number().as_number());
134   }
is_special() const135   BOOST_CONSTEXPR bool is_special() const
136   {
137     return(is_infinity() || is_nan());
138   }
operator ==(const int_adapter & rhs) const139   BOOST_CONSTEXPR bool operator==(const int_adapter& rhs) const
140   {
141     return (compare(rhs) == 0);
142   }
operator ==(const int & rhs) const143   BOOST_CXX14_CONSTEXPR bool operator==(const int& rhs) const
144   {
145     if(!std::numeric_limits<int_type>::is_signed)
146     {
147       if(is_neg_inf(value_) && rhs == 0)
148       {
149         return false;
150       }
151     }
152     return (compare(rhs) == 0);
153   }
operator !=(const int_adapter & rhs) const154   BOOST_CONSTEXPR bool operator!=(const int_adapter& rhs) const
155   {
156     return (compare(rhs) != 0);
157   }
operator !=(const int & rhs) const158   BOOST_CXX14_CONSTEXPR bool operator!=(const int& rhs) const
159   {
160     if(!std::numeric_limits<int_type>::is_signed)
161     {
162       if(is_neg_inf(value_) && rhs == 0)
163       {
164         return true;
165       }
166     }
167     return (compare(rhs) != 0);
168   }
operator <(const int_adapter & rhs) const169   BOOST_CONSTEXPR bool operator<(const int_adapter& rhs) const
170   {
171     return (compare(rhs) == -1);
172   }
operator <(const int & rhs) const173   BOOST_CXX14_CONSTEXPR bool operator<(const int& rhs) const
174   {
175     // quiets compiler warnings
176     if(!std::numeric_limits<int_type>::is_signed)
177     {
178       if(is_neg_inf(value_) && rhs == 0)
179       {
180         return true;
181       }
182     }
183     return (compare(rhs) == -1);
184   }
operator >(const int_adapter & rhs) const185   BOOST_CONSTEXPR bool operator>(const int_adapter& rhs) const
186   {
187     return (compare(rhs) == 1);
188   }
as_number() const189   BOOST_CONSTEXPR int_type as_number() const
190   {
191     return value_;
192   }
193   //! Returns either special value type or is_not_special
as_special() const194   BOOST_CONSTEXPR special_values as_special() const
195   {
196     return int_adapter::to_special(value_);
197   }
198   //creates nasty ambiguities
199 //   operator int_type() const
200 //   {
201 //     return value_;
202 //   }
203 
204   /*! Operator allows for adding dissimilar int_adapter types.
205    * The return type will match that of the the calling object's type */
206   template<class rhs_type>
207   BOOST_CXX14_CONSTEXPR
operator +(const int_adapter<rhs_type> & rhs) const208   int_adapter operator+(const int_adapter<rhs_type>& rhs) const
209   {
210     if(is_special() || rhs.is_special())
211     {
212       if (is_nan() || rhs.is_nan())
213       {
214         return int_adapter::not_a_number();
215       }
216       if((is_pos_inf(value_) && rhs.is_neg_inf(rhs.as_number())) ||
217       (is_neg_inf(value_) && rhs.is_pos_inf(rhs.as_number())) )
218       {
219         return int_adapter::not_a_number();
220       }
221       if (is_infinity())
222       {
223         return *this;
224       }
225       if (rhs.is_pos_inf(rhs.as_number()))
226       {
227         return int_adapter::pos_infinity();
228       }
229       if (rhs.is_neg_inf(rhs.as_number()))
230       {
231         return int_adapter::neg_infinity();
232       }
233     }
234     return int_adapter<int_type>(value_ + static_cast<int_type>(rhs.as_number()));
235   }
236 
237   BOOST_CXX14_CONSTEXPR
operator +(const int_type rhs) const238   int_adapter operator+(const int_type rhs) const
239   {
240     if(is_special())
241     {
242       if (is_nan())
243       {
244         return int_adapter<int_type>(not_a_number());
245       }
246       if (is_infinity())
247       {
248         return *this;
249       }
250     }
251     return int_adapter<int_type>(value_ + rhs);
252   }
253 
254   /*! Operator allows for subtracting dissimilar int_adapter types.
255    * The return type will match that of the the calling object's type */
256   template<class rhs_type>
257   BOOST_CXX14_CONSTEXPR
operator -(const int_adapter<rhs_type> & rhs) const258   int_adapter operator-(const int_adapter<rhs_type>& rhs)const
259   {
260     if(is_special() || rhs.is_special())
261     {
262       if (is_nan() || rhs.is_nan())
263       {
264         return int_adapter::not_a_number();
265       }
266       if((is_pos_inf(value_) && rhs.is_pos_inf(rhs.as_number())) ||
267          (is_neg_inf(value_) && rhs.is_neg_inf(rhs.as_number())) )
268       {
269         return int_adapter::not_a_number();
270       }
271       if (is_infinity())
272       {
273         return *this;
274       }
275       if (rhs.is_pos_inf(rhs.as_number()))
276       {
277         return int_adapter::neg_infinity();
278       }
279       if (rhs.is_neg_inf(rhs.as_number()))
280       {
281         return int_adapter::pos_infinity();
282       }
283     }
284     return int_adapter<int_type>(value_ - static_cast<int_type>(rhs.as_number()));
285   }
286 
287   BOOST_CXX14_CONSTEXPR
operator -(const int_type rhs) const288   int_adapter operator-(const int_type rhs) const
289   {
290     if(is_special())
291     {
292       if (is_nan())
293       {
294         return int_adapter<int_type>(not_a_number());
295       }
296       if (is_infinity())
297       {
298         return *this;
299       }
300     }
301     return int_adapter<int_type>(value_ - rhs);
302   }
303 
304   // should templatize this to be consistant with op +-
305   BOOST_CXX14_CONSTEXPR
operator *(const int_adapter & rhs) const306   int_adapter operator*(const int_adapter& rhs)const
307   {
308     if(this->is_special() || rhs.is_special())
309     {
310       return mult_div_specials(rhs);
311     }
312     return int_adapter<int_type>(value_ * rhs.value_);
313   }
314 
315   /*! Provided for cases when automatic conversion from
316    * 'int' to 'int_adapter' causes incorrect results. */
317   BOOST_CXX14_CONSTEXPR
operator *(const int rhs) const318   int_adapter operator*(const int rhs) const
319   {
320     if(is_special())
321     {
322       return mult_div_specials(rhs);
323     }
324     return int_adapter<int_type>(value_ * rhs);
325   }
326 
327   // should templatize this to be consistant with op +-
328   BOOST_CXX14_CONSTEXPR
operator /(const int_adapter & rhs) const329   int_adapter operator/(const int_adapter& rhs)const
330   {
331     if(this->is_special() || rhs.is_special())
332     {
333       if(is_infinity() && rhs.is_infinity())
334       {
335         return int_adapter<int_type>(not_a_number());
336       }
337       if(rhs != 0)
338       {
339         return mult_div_specials(rhs);
340       }
341       else { // let divide by zero blow itself up
342         return int_adapter<int_type>(value_ / rhs.value_); //NOLINT
343       }
344     }
345     return int_adapter<int_type>(value_ / rhs.value_);
346   }
347 
348   /*! Provided for cases when automatic conversion from
349    * 'int' to 'int_adapter' causes incorrect results. */
350   BOOST_CXX14_CONSTEXPR
operator /(const int rhs) const351   int_adapter operator/(const int rhs) const
352   {
353     if(is_special() && rhs != 0)
354     {
355       return mult_div_specials(rhs);
356     }
357     // let divide by zero blow itself up like int
358     return int_adapter<int_type>(value_ / rhs); //NOLINT
359   }
360 
361   // should templatize this to be consistant with op +-
362   BOOST_CXX14_CONSTEXPR
operator %(const int_adapter & rhs) const363   int_adapter operator%(const int_adapter& rhs)const
364   {
365     if(this->is_special() || rhs.is_special())
366     {
367       if(is_infinity() && rhs.is_infinity())
368       {
369         return int_adapter<int_type>(not_a_number());
370       }
371       if(rhs != 0)
372       {
373         return mult_div_specials(rhs);
374       }
375       else { // let divide by zero blow itself up
376         return int_adapter<int_type>(value_ % rhs.value_); //NOLINT
377       }
378     }
379     return int_adapter<int_type>(value_ % rhs.value_);
380   }
381 
382   /*! Provided for cases when automatic conversion from
383    * 'int' to 'int_adapter' causes incorrect results. */
384   BOOST_CXX14_CONSTEXPR
operator %(const int rhs) const385   int_adapter operator%(const int rhs) const
386   {
387     if(is_special() && rhs != 0)
388     {
389       return mult_div_specials(rhs);
390     }
391     // let divide by zero blow itself up
392     return int_adapter<int_type>(value_ % rhs); //NOLINT
393   }
394 
395 private:
396   int_type value_;
397 
398   //! returns -1, 0, 1, or 2 if 'this' is <, ==, >, or 'nan comparison' rhs
399   BOOST_CXX14_CONSTEXPR
compare(const int_adapter & rhs) const400   int compare( const int_adapter& rhs ) const
401   {
402     if(this->is_special() || rhs.is_special())
403     {
404       if(this->is_nan() || rhs.is_nan()) {
405         if(this->is_nan() && rhs.is_nan()) {
406           return 0; // equal
407         }
408         else {
409           return 2; // nan
410         }
411       }
412       if((is_neg_inf(value_) && !is_neg_inf(rhs.value_)) ||
413          (is_pos_inf(rhs.value_) && !is_pos_inf(value_)) )
414         {
415           return -1; // less than
416         }
417       if((is_pos_inf(value_) && !is_pos_inf(rhs.value_)) ||
418          (is_neg_inf(rhs.value_) && !is_neg_inf(value_)) ) {
419         return 1; // greater than
420       }
421     }
422     if(value_ < rhs.value_) return -1;
423     if(value_ > rhs.value_) return 1;
424     // implied-> if(value_ == rhs.value_)
425     return 0;
426   }
427 
428   /* When multiplying and dividing with at least 1 special value
429    * very simmilar rules apply. In those cases where the rules
430    * are different, they are handled in the respective operator
431    * function. */
432   //! Assumes at least 'this' or 'rhs' is a special value
433   BOOST_CXX14_CONSTEXPR
mult_div_specials(const int_adapter & rhs) const434   int_adapter mult_div_specials(const int_adapter& rhs) const
435   {
436     if(this->is_nan() || rhs.is_nan()) {
437       return int_adapter<int_type>(not_a_number());
438     }
439     BOOST_CONSTEXPR_OR_CONST int min_value = std::numeric_limits<int_type>::is_signed ? 0 : 1;
440     if((*this > 0 && rhs > 0) || (*this < min_value && rhs < min_value)) {
441         return int_adapter<int_type>(pos_infinity());
442     }
443     if((*this > 0 && rhs < min_value) || (*this < min_value && rhs > 0)) {
444         return int_adapter<int_type>(neg_infinity());
445     }
446     //implied -> if(this->value_ == 0 || rhs.value_ == 0)
447     return int_adapter<int_type>(not_a_number());
448   }
449 
450   /* Overloaded function necessary because of special
451    * situation where int_adapter is instantiated with
452    * 'unsigned' and func is called with negative int.
453    * It would produce incorrect results since 'unsigned'
454    * wraps around when initialized with a negative value */
455   //! Assumes 'this' is a special value
456   BOOST_CXX14_CONSTEXPR
mult_div_specials(const int & rhs) const457   int_adapter mult_div_specials(const int& rhs) const
458   {
459     if(this->is_nan()) {
460       return int_adapter<int_type>(not_a_number());
461     }
462     BOOST_CONSTEXPR_OR_CONST int min_value = std::numeric_limits<int_type>::is_signed ? 0 : 1;
463     if((*this > 0 && rhs > 0) || (*this < min_value && rhs < 0)) {
464         return int_adapter<int_type>(pos_infinity());
465     }
466     if((*this > 0 && rhs < 0) || (*this < min_value && rhs > 0)) {
467         return int_adapter<int_type>(neg_infinity());
468     }
469     //implied -> if(this->value_ == 0 || rhs.value_ == 0)
470     return int_adapter<int_type>(not_a_number());
471   }
472 
473 };
474 
475 #ifndef BOOST_DATE_TIME_NO_LOCALE
476   /*! Expected output is either a numeric representation
477    * or a special values representation.<BR>
478    * Ex. "12", "+infinity", "not-a-number", etc. */
479   //template<class charT = char, class traits = std::traits<charT>, typename int_type>
480   template<class charT, class traits, typename int_type>
481   inline
482   std::basic_ostream<charT, traits>&
operator <<(std::basic_ostream<charT,traits> & os,const int_adapter<int_type> & ia)483   operator<<(std::basic_ostream<charT, traits>& os, const int_adapter<int_type>& ia)
484   {
485     if(ia.is_special()) {
486       // switch copied from date_names_put.hpp
487       switch(ia.as_special())
488         {
489       case not_a_date_time:
490         os << "not-a-number";
491         break;
492       case pos_infin:
493         os << "+infinity";
494         break;
495       case neg_infin:
496         os << "-infinity";
497         break;
498       default:
499         os << "";
500       }
501     }
502     else {
503       os << ia.as_number();
504     }
505     return os;
506   }
507 #endif
508 
509 
510 } } //namespace date_time
511 
512 #if defined(BOOST_MSVC)
513 #pragma warning(pop)
514 #endif
515 
516 #endif
517