Break maths some more
[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 float // 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
46                         if ((ParanoidOp<T>(c, b, DIVIDE) || ParanoidOp(d, b, DIVIDE))
47                                 && TrustingOp<T>(c, d, MULTIPLY) && TrustingOp<T>(a,c,ADD)
48                                 && TrustingOp<T>(a, b, MULTIPLY)) // (a + (cd)/b) * b
49                         {
50                                 aa = a;
51                                 bb = 1;
52                                 cc = 1;
53                                 dd = 1;
54                                 return true;
55                         }
56                         if ((ParanoidOp<T>(a, d, DIVIDE) || ParanoidOp(b, d, DIVIDE))
57                                 && TrustingOp<T>(a, b, MULTIPLY) && TrustingOp<T>(a,c,ADD)
58                                 && TrustingOp<T>(a, d, MULTIPLY)) // ((ab)/d + c)*d
59                         {
60                                 aa = a;
61                                 bb = 1;
62                                 cc = 1;
63                                 dd = 1;
64                                 return true;
65                         }
66                         return false;
67                 }
68                 else if (aop == DIVIDE && cop == DIVIDE)
69                 {
70
71         
72                         if (TrustingOp<T>(a, d, MULTIPLY) && TrustingOp<T>(c, b, MULTIPLY)
73                                 && TrustingOp<T>(a, c, ADD) && TrustingOp<T>(b, d, MULTIPLY))
74                         {
75                                 cc = 1;
76                                 dd = 1;
77                                 if (ParanoidOp<T>(a, b, DIVIDE))
78                                 {
79                                         aa = a;
80                                         bb = 1;
81                                         return true;
82                                 }
83                                 aa = a;
84                                 bb = b;
85                                 return true;
86                         }
87                         return false;
88                 }
89                 return false;
90         }
91
92         class ParanoidNumber
93         {
94                 
95                 public:
96                         typedef PARANOID_DIGIT_T digit_t;
97
98                         ParanoidNumber(digit_t value=0, Optype type = ADD) : m_value(value), m_op(type), m_next_term(NULL), m_next_factor(NULL)
99                         {
100                                 Construct();
101                         }
102                         
103                         ParanoidNumber(const ParanoidNumber & cpy) : m_value(cpy.m_value), m_op(cpy.m_op), m_next_term(NULL), m_next_factor(NULL)
104                         {
105                                 if (cpy.m_next_term != NULL)
106                                 {
107                                         m_next_term = new ParanoidNumber(*(cpy.m_next_term));
108                                 }
109                                 if (cpy.m_next_factor != NULL)
110                                 {
111                                         m_next_factor = new ParanoidNumber(*(cpy.m_next_factor));
112                                 }
113                                 Construct();
114                         }
115                         
116                         ParanoidNumber(const ParanoidNumber & cpy, Optype type) : ParanoidNumber(cpy)
117                         {
118                                 m_op = type;
119                         }
120                         
121                         ParanoidNumber(const char * str);
122                         ParanoidNumber(const std::string & str) : ParanoidNumber(str.c_str()) {Construct();}
123                         
124                         virtual ~ParanoidNumber()
125                         {
126                                 if (m_next_term != NULL)
127                                         delete m_next_term;
128                                 if (m_next_factor != NULL)
129                                         delete m_next_factor;
130                                 g_count--;
131                         }
132                         
133                         inline void Construct() {g_count++;}
134                         
135                         
136                         template <class T> T Convert() const;
137                         template <class T> T AddTerms() const;
138                         template <class T> T MultiplyFactors() const;
139                         template <class T> T Head() const {return (m_op == SUBTRACT) ? T(-m_value) : T(m_value);}
140                         
141
142                         
143                         
144                         double ToDouble() const {return Convert<double>();}
145                         float ToFloat() const {return Convert<float>();}
146                         digit_t Digit() const {return Convert<digit_t>();}
147                         
148                         bool Floating() const {return (m_next_term == NULL && m_next_factor == NULL);}
149                         bool Sunken() const {return !Floating();} // I could not resist...
150                         
151                         ParanoidNumber & operator+=(const ParanoidNumber & a);
152                         ParanoidNumber & operator-=(const ParanoidNumber & a);
153                         ParanoidNumber & operator*=(const ParanoidNumber & a);
154                         ParanoidNumber & operator/=(const ParanoidNumber & a);
155                         ParanoidNumber & operator=(const ParanoidNumber & a);
156                         
157                         
158                         bool operator<(const ParanoidNumber & a) const {return ToDouble() < a.ToDouble();}
159                         bool operator<=(const ParanoidNumber & a) const {return this->operator<(a) || this->operator==(a);}
160                         bool operator>(const ParanoidNumber & a) const {return !(this->operator<=(a));}
161                         bool operator>=(const ParanoidNumber & a) const {return !(this->operator<(a));}
162                         bool operator==(const ParanoidNumber & a) const {return ToDouble() == a.ToDouble();}
163                         bool operator!=(const ParanoidNumber & a) const {return !(this->operator==(a));}
164                         
165                         ParanoidNumber operator+(const ParanoidNumber & a) const
166                         {
167                                 ParanoidNumber result(*this);
168                                 result += a;
169                                 return result;
170                         }
171                         ParanoidNumber operator-(const ParanoidNumber & a) const
172                         {
173                                 ParanoidNumber result(*this);
174                                 result -= a;
175                                 return result;
176                         }
177                         ParanoidNumber operator*(const ParanoidNumber & a) const
178                         {
179                                 ParanoidNumber result(*this);
180                                 result *= a;
181                                 return result;
182                         }
183                         ParanoidNumber operator/(const ParanoidNumber & a) const
184                         {
185                                 ParanoidNumber result(*this);
186                                 result /= a;
187                                 return result;
188                         }
189                         
190                         std::string Str() const;
191                         static char OpChar(Optype op) 
192                         {
193                                 static char opch[] = {'+','-','*','/'};
194                                 return opch[(int)op];
195                         }
196                 
197                         static int64_t Paranoia() {return g_count;}
198                 
199                 private:
200                         static int64_t g_count;
201                         void Simplify();
202                         void SimplifyTerms();
203                         void SimplifyFactors();
204                         
205                         
206                         digit_t m_value;
207                         Optype m_op;
208                         ParanoidNumber * m_next_term;
209                         ParanoidNumber * m_next_factor;
210         };
211
212 template <class T>
213 T ParanoidNumber::AddTerms() const
214 {
215         T value(0);
216         for (ParanoidNumber * a = m_next_term; a != NULL; a = a->m_next_term)
217         {
218                 value += a->Head<T>() * a->MultiplyFactors<T>();
219         }
220         return value;
221 }
222
223 template <class T>
224 T ParanoidNumber::MultiplyFactors() const
225 {
226         T value(1);
227         for (ParanoidNumber * a = m_next_factor; a != NULL; a = a->m_next_factor)
228         {
229                 if (a->m_op == DIVIDE)
230                         value /= (a->Head<T>() + a->AddTerms<T>());     
231                 else
232                         value *= (a->Head<T>() + a->AddTerms<T>());     
233         }
234         return value;
235 }
236
237
238
239 template <class T>
240 T ParanoidNumber::Convert() const
241 {
242         return Head<T>() * MultiplyFactors<T>() + AddTerms<T>();
243 }
244
245
246
247 }
248
249 #endif //_PARANOIDNUMBER_H
250

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