f767ffa2a65315788be14b6d4a1baf4ba6527b95
[ipdf/code.git] / src / paranoidnumber.h
1 #ifndef _PARANOIDNUMBER_H
2 #define _PARANOIDNUMBER_H
3
4 #include <list>
5 #include <cfloat>
6 #include <map>
7 #include <string>
8 #include "log.h"
9 #include <fenv.h>
10
11 #define PARANOID_DIGIT_T int8_t // we could theoretically replace this with a template
12                                                                 // but let's not do that...
13
14 namespace IPDF
15 {
16         typedef enum {ADD, SUBTRACT, MULTIPLY, DIVIDE} Optype;
17
18         /** Performs an operation, returning if the result was exact **/
19         // NOTE: DIFFERENT to ParanoidOp (although that wraps to this...)
20         template <class T> bool TrustingOp(T & a, const T & b, Optype op);
21
22         /** Performs an operation _only_ if the result would be exact **/
23         template <class T> bool ParanoidOp(T & a, const T & b, Optype op)
24         {
25                 T cpy(a);
26                 if (TrustingOp<T>(cpy, b, op))
27                 {
28                         a = cpy;
29                         return true;
30                 }
31                 return false;
32         }
33
34
35         template <> bool TrustingOp<float>(float & a, const float & b, Optype op);
36         template <> bool TrustingOp<double>(double & a, const double & b, Optype op);
37         template <> bool TrustingOp<int8_t>(int8_t & a, const int8_t & b, Optype op);
38         
39         // Attempt to comine two terms: a*b + c*d or a/b + c/d
40         template <class T> bool CombineTerms(T & aa, Optype aop, T & bb, T & cc, Optype cop, T & dd)
41         {
42                 T a(aa); T b(bb); T c(cc); T d(dd);
43                 if (aop == MULTIPLY && cop == MULTIPLY) // a*b + c*d
44                 {
45                         if ((ParanoidOp<T>(c, b, DIVIDE) || ParanoidOp(d, b, DIVIDE))
46                                 && TrustingOp<T>(c, d, MULTIPLY) && TrustingOp<T>(a,c,ADD)
47                                 && TrustingOp<T>(a, b, MULTIPLY)) // (a + (cd)/b) * b
48                         {
49                                 aa = a;
50                                 bb = 1;
51                                 cc = 1;
52                                 dd = 1;
53                                 return true;
54                         }
55                         if ((ParanoidOp<T>(a, d, DIVIDE) || ParanoidOp(b, d, DIVIDE))
56                                 && TrustingOp<T>(a, b, MULTIPLY) && TrustingOp<T>(a,c,ADD)
57                                 && TrustingOp<T>(a, d, MULTIPLY)) // ((ab)/d + c)*d
58                         {
59                                 aa = a;
60                                 bb = 1;
61                                 cc = 1;
62                                 dd = 1;
63                                 return true;
64                         }
65                         return false;
66                 }
67                 else if (aop == DIVIDE && cop == DIVIDE)
68                 {
69                         if (TrustingOp<T>(a, d, MULTIPLY) && TrustingOp<T>(c, b, MULTIPLY)
70                                 && TrustingOp<T>(a, c, ADD) && TrustingOp<T>(b, d, MULTIPLY))
71                         {
72                                 cc = 1;
73                                 dd = 1;
74                                 if (ParanoidOp<T>(a, b, DIVIDE))
75                                 {
76                                         aa = a;
77                                         bb = 1;
78                                         return true;
79                                 }
80                                 aa = a;
81                                 bb = b;
82                                 return true;
83                         }
84                         return false;
85                 }
86                 return false;
87         }
88
89         class ParanoidNumber
90         {
91                 
92                 public:
93                         typedef PARANOID_DIGIT_T digit_t;
94
95                         ParanoidNumber(digit_t value=0, Optype type = ADD) : m_value(value), m_op(type), m_next_term(NULL), m_next_factor(NULL)
96                         {
97                                 Construct();
98                         }
99                         
100                         ParanoidNumber(const ParanoidNumber & cpy) : m_value(cpy.m_value), m_op(cpy.m_op), m_next_term(NULL), m_next_factor(NULL)
101                         {
102                                 if (cpy.m_next_term != NULL)
103                                 {
104                                         m_next_term = new ParanoidNumber(*(cpy.m_next_term));
105                                 }
106                                 if (cpy.m_next_factor != NULL)
107                                 {
108                                         m_next_factor = new ParanoidNumber(*(cpy.m_next_factor));
109                                 }
110                                 Construct();
111                         }
112                         
113                         ParanoidNumber(const ParanoidNumber & cpy, Optype type) : ParanoidNumber(cpy)
114                         {
115                                 m_op = type;
116                         }
117                         
118                         ParanoidNumber(const char * str);
119                         ParanoidNumber(const std::string & str) : ParanoidNumber(str.c_str()) {Construct();}
120                         
121                         virtual ~ParanoidNumber()
122                         {
123                                 if (m_next_term != NULL)
124                                         delete m_next_term;
125                                 if (m_next_factor != NULL)
126                                         delete m_next_factor;
127                                 g_count--;
128                         }
129                         
130                         inline void Construct() {g_count++;}
131                         
132                         
133                         template <class T> T Convert() const;
134                         template <class T> T AddTerms() const;
135                         template <class T> T MultiplyFactors() const;
136                         template <class T> T Head() const {return (m_op == SUBTRACT) ? T(-m_value) : T(m_value);}
137                         
138
139                         
140                         
141                         double ToDouble() const {return Convert<double>();}
142                         float ToFloat() const {return Convert<float>();}
143                         digit_t Digit() const {return Convert<digit_t>();}
144                         
145                         bool Floating() const {return (m_next_term == NULL && m_next_factor == NULL);}
146                         bool Sunken() const {return !Floating();} // I could not resist...
147                         
148                         ParanoidNumber & operator+=(const ParanoidNumber & a);
149                         ParanoidNumber & operator-=(const ParanoidNumber & a);
150                         ParanoidNumber & operator*=(const ParanoidNumber & a);
151                         ParanoidNumber & operator/=(const ParanoidNumber & a);
152                         ParanoidNumber & operator=(const ParanoidNumber & a);
153                         
154                         
155                         bool operator<(const ParanoidNumber & a) const {return ToDouble() < a.ToDouble();}
156                         bool operator<=(const ParanoidNumber & a) const {return this->operator<(a) || this->operator==(a);}
157                         bool operator>(const ParanoidNumber & a) const {return !(this->operator<=(a));}
158                         bool operator>=(const ParanoidNumber & a) const {return !(this->operator<(a));}
159                         bool operator==(const ParanoidNumber & a) const {return ToDouble() == a.ToDouble();}
160                         bool operator!=(const ParanoidNumber & a) const {return !(this->operator==(a));}
161                         
162                         ParanoidNumber operator+(const ParanoidNumber & a) const
163                         {
164                                 ParanoidNumber result(*this);
165                                 result += a;
166                                 return result;
167                         }
168                         ParanoidNumber operator-(const ParanoidNumber & a) const
169                         {
170                                 ParanoidNumber result(*this);
171                                 result -= a;
172                                 return result;
173                         }
174                         ParanoidNumber operator*(const ParanoidNumber & a) const
175                         {
176                                 ParanoidNumber result(*this);
177                                 result *= a;
178                                 return result;
179                         }
180                         ParanoidNumber operator/(const ParanoidNumber & a) const
181                         {
182                                 ParanoidNumber result(*this);
183                                 result /= a;
184                                 return result;
185                         }
186                         
187                         std::string Str() const;
188                         static char OpChar(Optype op) 
189                         {
190                                 static char opch[] = {'+','-','*','/'};
191                                 return opch[(int)op];
192                         }
193                 
194                         static int64_t Paranoia() {return g_count;}
195                 
196                 private:
197                         static int64_t g_count;
198                         void Simplify();
199                         void SimplifyTerms();
200                         void SimplifyFactors();
201                         
202                         
203                         digit_t m_value;
204                         Optype m_op;
205                         ParanoidNumber * m_next_term;
206                         ParanoidNumber * m_next_factor;
207         };
208
209 template <class T>
210 T ParanoidNumber::AddTerms() const
211 {
212         T value(0);
213         for (ParanoidNumber * a = m_next_term; a != NULL; a = a->m_next_term)
214         {
215                 value += a->Head<T>() * a->MultiplyFactors<T>();
216         }
217         return value;
218 }
219
220 template <class T>
221 T ParanoidNumber::MultiplyFactors() const
222 {
223         T value(1);
224         for (ParanoidNumber * a = m_next_factor; a != NULL; a = a->m_next_factor)
225         {
226                 if (a->m_op == DIVIDE)
227                         value /= (a->Head<T>() + a->AddTerms<T>());     
228                 else
229                         value *= (a->Head<T>() + a->AddTerms<T>());     
230         }
231         return value;
232 }
233
234
235
236 template <class T>
237 T ParanoidNumber::Convert() const
238 {
239         return Head<T>() * MultiplyFactors<T>() + AddTerms<T>();
240 }
241
242
243
244 }
245
246 #endif //_PARANOIDNUMBER_H
247

UCC git Repository :: git.ucc.asn.au