David's final changes: more profiler features, fixes.
[ipdf/code.git] / src / tests / paranoidtester.cpp
1 #include "main.h"
2 #include "real.h"
3 #include <cmath>
4 #include <cassert>
5 #include <list>
6 #include <bitset>
7 #include <iostream>
8 #include <cfloat>
9 #include <fenv.h>
10 #include "paranoidnumber.h"
11 #include "progressbar.h"
12
13 using namespace std;
14 using namespace IPDF;
15
16 string RandomNumberAsString(int max_digits = 3)
17 {
18         string result("");
19         int digits = 1+(rand() % max_digits);
20         int dp = (rand() % digits)+1;
21         for (int i = 0; i < digits; ++i)
22         {
23                 if (i == dp)
24                 {
25                         result += ".";
26                         continue;
27                 }
28                 result += ('0'+rand() % 10);
29         }
30         return result;
31 }
32
33 bool CloseEnough(long double d, ParanoidNumber & p, long double eps = 1e-6)
34 {
35         double pd = p.ToDouble();
36                 
37         if (d == 0)
38                 return fabs(pd) <= eps;
39         return fabs((fabs(pd - d) / d)) <= eps;
40 }
41
42 void TestOp(ParanoidNumber & p, double & d, Optype op, const double amount)
43 {
44         string p0str(p.Str());
45         double p0 = p.ToDouble();
46         switch (op)
47         {
48                 case ADD:
49                         p += amount;
50                         d += amount;
51                         break;
52                 case SUBTRACT:
53                         p -= amount;
54                         d -= amount;
55                         break;
56                 case MULTIPLY:
57                         p *= amount;
58                         d *= amount;
59                         break;
60                 case DIVIDE:
61                         p /= amount;
62                         d /= amount;
63                         break;
64                 default:
65                         break;
66         }
67         if (false)//(!CloseEnough(d, p))
68         {
69                 Debug("%.40lf %c= %.40lf failed", p0, OpChar(op), amount);
70                 Debug("%.40lf vs %.40lf", p.ToDouble(), d);
71                 Debug("Before: {%s}\n", p0str.c_str());
72                 Debug("After: {%s}\n", p.Str().c_str());
73                 Fatal(":-(");
74         }
75
76 }
77
78 void TestAddSubIntegers(int max=100)
79 {
80         Debug("Test add/sub integers 0 -> %i", max);
81         ParanoidNumber p;
82         double d(0);
83         for (int a = 0; a < max; ++a)
84         {
85                 TestOp(p, d, ADD, a);
86                 for (int b = 0; b < max; ++b)
87                 {
88                         TestOp(p, d, SUBTRACT, b);
89                 }
90                 for (int b = 0; b < max; ++b)
91                 {
92                         TestOp(p, d, ADD, b);
93                 }
94         }
95         for (int a = 0; a < max; ++a)
96         {
97                 TestOp(p, d, SUBTRACT, a);
98                 for (int b = 0; b < max; ++b)
99                 {
100                         TestOp(p, d, ADD, b);
101                 }
102                 for (int b = 0; b < max; ++b)
103                 {
104                         TestOp(p, d, SUBTRACT, b);
105                 }
106         }
107         Debug("PN Yields: %.40lf", p.ToDouble());
108         Debug("Doubles Yield: %.40lf", d);
109         Debug("Complete!");
110
111 }
112
113 void TestMulDivIntegers(int max=50)
114 {
115         Debug("Test mul/div integers 1 -> %i", max);
116         ParanoidNumber p(1.0);
117         double d(1.0);
118         for (int a = 1; a < max; ++a)
119         {
120                 TestOp(p, d, MULTIPLY, a);
121                 for (int b = 1; b < max; ++b)
122                 {
123                         TestOp(p, d, DIVIDE, b);
124                 }
125                 for (int b = 1; b < max; ++b)
126                 {
127                         TestOp(p, d, MULTIPLY, b);
128                 }
129         }
130         for (int a = 1; a < max; ++a)
131         {
132                 TestOp(p, d, DIVIDE, a);
133                 for (int b = 1; b < max; ++b)
134                 {
135                         TestOp(p, d, MULTIPLY, b);
136                 }
137                 for (int b = 1; b < max; ++b)
138                 {
139                         TestOp(p, d, DIVIDE, b);
140                 }
141         }
142         Debug("PN Yields: %.40lf", p.ToDouble());
143         Debug("PN is: %s", p.Str().c_str());
144         
145         Debug("Doubles Yield: %.40lf", d);
146         Debug("Complete!");
147
148 }
149
150 void TestRandomisedOps(int test_cases = 1000, int ops_per_case = 1, int max_digits = 4)
151 {
152         Debug("Test %i*%i randomised ops (max digits = %i)", test_cases, ops_per_case, max_digits);
153         long double eps = 1; //* (1e4*ops_per_case);
154         for (int i = 0; i < test_cases; ++i)
155         {
156                 string s = RandomNumberAsString(max_digits);
157                 ParanoidNumber a(s);
158                 
159                 double da(a.ToDouble());                
160                 for (int j = 1; j <= ops_per_case; ++j)
161                 {
162                         double da2(a.ToDouble());
163                         s = RandomNumberAsString(max_digits);
164                         ParanoidNumber b(s);
165                         double db(b.ToDouble());
166         
167                 
168         
169                         Optype op = Optype(rand() % 4);
170                         
171                         ParanoidNumber a_before(a);
172                         
173                 
174                         switch (op)
175                         {
176                         case ADD:
177                                 a += b;
178                                 da += db;
179                                 da2 += db;
180                                 break;
181                         case SUBTRACT:
182                                 a -= b;
183                                 da -= db;
184                                 da2 -= db;
185                                 break;
186                         case MULTIPLY:
187                                 a *= b;
188                                 da *= db;
189                                 da2 *= db;
190                                 break;
191                         case DIVIDE:
192                                 if (db == 0)
193                                 {
194                                         --i;
195                                 }
196                                 else
197                                 {
198                                         a /= b;
199                                         da /= db;
200                                         da2 /= db;
201                                 }
202                                 break;
203                         case NOP:
204                                 break;
205                         }
206                         if (!CloseEnough(da2, a, eps))
207                         {
208                                 Error("{%s} %c= {%s}", a_before.Str().c_str(), OpChar(op), b.Str().c_str());
209                                 Error("{%s}", a.Str().c_str());
210                                 Error("double Yields: %.40lf", da);
211                                 Error("PN Yields: %.40lf", a.ToDouble());
212                                 Fatal("Failed on case %i", i*ops_per_case + j-1);
213                         }
214                 }
215                 if (!CloseEnough(da, a, eps))
216                 {
217                         Warn("double Yields: %.40lf", da);
218                         Warn("PN Yields: %.40lf", a.ToDouble());
219                 }
220                 if (i == test_cases - 1)
221                 {
222                         Debug("double: %.40lf", da);
223                         Debug("PN: %.40lf", a.ToDouble());
224                 }
225         }
226
227
228 }
229
230 #define TEST_CASES 1000
231
232 int main(int argc, char ** argv)
233 {
234         TestAddSubIntegers(100);
235         TestMulDivIntegers(100);
236         for (int i = 1; i <= 100; ++i)
237                 TestRandomisedOps(1000, i);
238         return 0;
239         srand(0);//time(NULL)); //always test off same set
240         string number(RandomNumberAsString());
241         ParanoidNumber a(number);
242
243         float fa = strtof(number.c_str(), NULL);
244         double da = strtod(number.c_str(), NULL);
245         double diff = 0;
246         long double lda = strtold(number.c_str(), NULL);
247         Debug("a is %s", a.Str().c_str());
248         if (fabs(a.ToDouble() - da) > 1e-6)
249         {
250                 Error("double %lf, pn %lf {%s}", da, a.ToDouble(), a.Str().c_str());
251                 Fatal("Didn't construct correctly off %s", number.c_str());
252                 
253         }
254         
255         char opch[] = {'+','-','*','/'};
256         int opcount[] = {0,0,0,0};
257         for (int i = 0; i < TEST_CASES; ++i)
258         {
259                 ProgressBar(i, TEST_CASES); fprintf(stderr, "%.30lf (%d)+ (%d)- (%d)* (%d)/ [Paranoia %ld]",diff, opcount[0],opcount[1],opcount[2],opcount[3], ParanoidNumber::Paranoia());
260                 number = RandomNumberAsString();
261                 ParanoidNumber b(number);
262                 float fb = strtof(number.c_str(), NULL);
263                 double db = strtod(number.c_str(), NULL);
264                 if (db == 0)
265                 {
266                         --i;
267                         continue;
268                 }
269                 long double ldb = strtold(number.c_str(), NULL);
270                 int op = rand() % 4;//= 2*(rand() % 2);
271                 while (op == 1)
272                         op = rand() % 4;
273                 //if (rand() % 2 == 0)
274                         //op = 2+(rand() % 2);
275                         
276                 opcount[op]++;
277                 ParanoidNumber olda(a);
278                 double oldda(da);
279                 switch (op)
280                 {
281                         case 0:
282                                 a += b;
283                                 fa += fb;
284                                 da += db;
285                                 lda += ldb;
286                                 break;
287                         case 1:
288                                 a -= b;
289                                 fa -= fb;
290                                 da -= db;
291                                 lda -= ldb;
292                                 break;
293                         case 2:
294                                 a *= b;
295                                 fa *= fb;
296                                 da *= db;
297                                 lda *= ldb;
298                                 break;
299                         case 3:
300                                 a /= b;
301                                 fa /= fb;
302                                 da /= db;
303                                 lda /= ldb;
304                                 break;
305                 }
306                 diff = 100.0*(fabs(a.ToDouble() - da) / da);
307                 if (!CloseEnough(lda, a))
308                 {
309                         Error("Op %i: ParanoidNumber probably doesn't work", i);
310                         Error("Operation: %lf %c %lf", oldda, opch[op], db);
311                         Error("As PN: %lf %c %lf", olda.ToDouble(), opch[op], b.ToDouble());
312                         Error("PN String before: %s", olda.Str().c_str());
313                         Error("PN String: %s", a.Str().c_str());
314                         Error("LONG double gives %.40llf", lda);
315                         Error("%.40llf, expected aboout %.40llf", a.Convert<long double>(), lda);
316                         a = da;
317                         
318                 }
319                 
320         }
321         printf("ParanoidNumber: {%s} = %.40lf\n", a.Str().c_str(), a.ToDouble());
322         printf("float: %.40f\n", fa);
323         printf("double: %.40lf\n", da);
324         printf("long double: %.40Lf\n", lda);
325         printf("diff %.40lf\n", diff);
326 }

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