e88cfe75b9b94439b0b87064a7fb1c8f6e5c1311
[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 #include <vector>
11 #include <cmath>
12
13 #define PARANOID_DIGIT_T float // we could theoretically replace this with a template
14                                                                 // but let's not do that...
15
16 namespace IPDF
17 {
18         typedef enum {ADD, SUBTRACT, MULTIPLY, DIVIDE, NOP} Optype;
19         inline Optype InverseOp(Optype op)
20         {
21                 return ((op == ADD) ? SUBTRACT :
22                                 (op == SUBTRACT) ? ADD :
23                                 (op == MULTIPLY) ? DIVIDE :
24                                 (op == DIVIDE) ? MULTIPLY :
25                                 (op == NOP) ? NOP : NOP);
26         }
27         
28
29         inline char OpChar(int op) 
30         {
31                 static char opch[] = {'+','-','*','/'};
32                 return (op < NOP && op >= 0) ? opch[op] : '?';
33         }
34         
35
36         /** Performs an operation, returning if the result was exact **/
37         // NOTE: DIFFERENT to ParanoidOp (although that wraps to this...)
38         template <class T> bool TrustingOp(T & a, const T & b, Optype op);
39
40         /** Performs an operation _only_ if the result would be exact **/
41         template <class T> bool ParanoidOp(T & a, const T & b, Optype op)
42         {
43                 T cpy(a);
44                 if (TrustingOp<T>(cpy, b, op))
45                 {
46                         a = cpy;
47                         return true;
48                 }
49                 return false;
50         }
51         template <> bool TrustingOp<float>(float & a, const float & b, Optype op);
52         template <> bool TrustingOp<double>(double & a, const double & b, Optype op);
53         template <> bool TrustingOp<int8_t>(int8_t & a, const int8_t & b, Optype op);
54         
55         /**
56          * A ParanoidNumber
57          * Idea: Perform regular floating point arithmetic but rearrange operations to only ever use exact results
58          * Memory Usage: O(all of it)
59          * CPU Usage: O(all of it)
60          * Accuracy: O(gives better result for 0.3+0.3+0.3, gives same result for everything else, or worse result)
61          * 
62          * The ParanoidNumber basically stores 4 linked lists which can be split into two "dimensions"
63          *  1. Terms to ADD and terms to SUBTRACT
64          *  2. Factors to MULTIPLY and DIVIDE
65          * Because ADD and SUBTRACT are inverse operations and MULTIPLY and DIVIDE are inverse operations
66          * See paranoidnumber.cpp and the ParanoidNumber::Operation function
67          */
68         class ParanoidNumber
69         {
70                 
71                 public:
72                         typedef PARANOID_DIGIT_T digit_t;
73
74                         ParanoidNumber(digit_t value=0) : m_value(value)
75                         {
76                                 Construct();
77                         }
78                         
79                         ParanoidNumber(const ParanoidNumber & cpy) : m_value(cpy.m_value)
80                         {
81                                 Construct();
82                                 for (int i = 0; i < NOP; ++i)
83                                 {
84                                         for (auto next : cpy.m_next[i])
85                                                 m_next[i].push_back(new ParanoidNumber(*next));
86                                 }
87                         }
88                         
89                         ParanoidNumber(const char * str);
90                         ParanoidNumber(const std::string & str) : ParanoidNumber(str.c_str()) {}
91                         
92                         virtual ~ParanoidNumber();
93                         
94                         inline void Construct() 
95                         {
96                                 g_count++;
97                         }
98                         
99                         
100                         template <class T> T Convert() const;
101
102                         double ToDouble() const {return Convert<double>();}
103                         digit_t Digit() const {return Convert<digit_t>();}
104                         
105                         bool Floating() const 
106                         {
107                                 return NoFactors() && NoTerms();
108                         }
109                         bool Sunken() const {return !Floating();} // I could not resist...
110                         
111                         bool NoFactors() const {return (m_next[MULTIPLY].size() == 0 && m_next[DIVIDE].size() == 0);}
112                         bool NoTerms() const {return (m_next[ADD].size() == 0 && m_next[SUBTRACT].size() == 0);}
113                         
114                         ParanoidNumber & operator+=(const ParanoidNumber & a);
115                         ParanoidNumber & operator-=(const ParanoidNumber & a);
116                         ParanoidNumber & operator*=(const ParanoidNumber & a);
117                         ParanoidNumber & operator/=(const ParanoidNumber & a);
118                         ParanoidNumber & operator=(const ParanoidNumber & a);
119                         
120                         ParanoidNumber * OperationTerm(ParanoidNumber * b, Optype op, ParanoidNumber ** merge_point = NULL, Optype * mop = NULL);
121                         ParanoidNumber * OperationFactor(ParanoidNumber * b, Optype op, ParanoidNumber ** merge_point = NULL, Optype * mop = NULL);
122                         ParanoidNumber * TrivialOp(ParanoidNumber * b, Optype op);
123                         ParanoidNumber * Operation(ParanoidNumber * b, Optype op, ParanoidNumber ** merge_point = NULL, Optype * mop = NULL);
124                         bool Simplify(Optype op);
125                         
126                         
127                         bool operator<(const ParanoidNumber & a) const {return ToDouble() < a.ToDouble();}
128                         bool operator<=(const ParanoidNumber & a) const {return this->operator<(a) || this->operator==(a);}
129                         bool operator>(const ParanoidNumber & a) const {return !(this->operator<=(a));}
130                         bool operator>=(const ParanoidNumber & a) const {return !(this->operator<(a));}
131                         bool operator==(const ParanoidNumber & a) const {return ToDouble() == a.ToDouble();}
132                         bool operator!=(const ParanoidNumber & a) const {return !(this->operator==(a));}
133                         
134                         ParanoidNumber operator+(const ParanoidNumber & a) const
135                         {
136                                 ParanoidNumber result(*this);
137                                 result += a;
138                                 return result;
139                         }
140                         ParanoidNumber operator-(const ParanoidNumber & a) const
141                         {
142                                 ParanoidNumber result(*this);
143                                 result -= a;
144                                 return result;
145                         }
146                         ParanoidNumber operator*(const ParanoidNumber & a) const
147                         {
148                                 ParanoidNumber result(*this);
149                                 result *= a;
150                                 return result;
151                         }
152                         ParanoidNumber operator/(const ParanoidNumber & a) const
153                         {
154                                 ParanoidNumber result(*this);
155                                 result /= a;
156                                 return result;
157                         }
158                         
159                         std::string Str() const;
160
161                         ParanoidNumber * CopyTerms()
162                         {
163                                 ParanoidNumber * copy = new ParanoidNumber(*this);
164                                 copy->m_value = 0;
165                                 copy->Simplify(ADD);
166                                 copy->Simplify(SUBTRACT);
167                                 return copy;
168                         }
169                         
170                         ParanoidNumber * CopyFactors()
171                         {
172                                 ParanoidNumber * copy = new ParanoidNumber(*this);
173                                 copy->m_value = 1;
174                                 copy->Simplify(MULTIPLY);
175                                 copy->Simplify(DIVIDE);
176                                 return copy;
177                         }
178
179                 
180                         static int64_t Paranoia() {return g_count;}
181                         
182                         std::string PStr() const;
183                 
184                 private:
185                         static int64_t g_count;
186                         void Simplify();
187                         void SimplifyTerms();
188                         void SimplifyFactors();
189                         
190                         
191                         digit_t m_value;
192                         Optype m_op;
193                         std::vector<ParanoidNumber*> m_next[4];
194                         
195                         int m_size;
196         };
197
198 template <class T>
199 T ParanoidNumber::Convert() const
200 {
201         T value(m_value);
202         for (auto mul : m_next[MULTIPLY])
203         {
204                 value *= mul->Digit();
205         }
206         for (auto div : m_next[DIVIDE])
207         {
208                 value /= div->Digit();
209         }
210         for (auto add : m_next[ADD])
211                 value += add->Digit();
212         for (auto sub : m_next[SUBTRACT])
213                 value -= sub->Digit();
214         return value;
215 }
216
217 }
218
219 #endif //_PARANOIDNUMBER_H
220

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