All of the things and none of the sleep
[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         long double pd = p.Convert<long double>();
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         }
221         Debug("Complete!");
222
223 }
224
225 #define TEST_CASES 1000
226
227 int main(int argc, char ** argv)
228 {
229         TestAddSubIntegers(100);
230         TestMulDivIntegers(100);
231         //return 0;
232         for (int i = 1; i <= 100; ++i)
233                 TestRandomisedOps(1000, i);
234         return 0;
235         srand(0);//time(NULL)); //always test off same set
236         string number(RandomNumberAsString());
237         ParanoidNumber a(number);
238
239         float fa = strtof(number.c_str(), NULL);
240         double da = strtod(number.c_str(), NULL);
241         double diff = 0;
242         long double lda = strtold(number.c_str(), NULL);
243         Debug("a is %s", a.Str().c_str());
244         if (fabs(a.ToDouble() - da) > 1e-6)
245         {
246                 Error("double %lf, pn %lf {%s}", da, a.ToDouble(), a.Str().c_str());
247                 Fatal("Didn't construct correctly off %s", number.c_str());
248                 
249         }
250         
251         char opch[] = {'+','-','*','/'};
252         int opcount[] = {0,0,0,0};
253         for (int i = 0; i < TEST_CASES; ++i)
254         {
255                 ProgressBar(i, TEST_CASES); fprintf(stderr, "%.30lf (%d)+ (%d)- (%d)* (%d)/ [Paranoia %ld]",diff, opcount[0],opcount[1],opcount[2],opcount[3], ParanoidNumber::Paranoia());
256                 number = RandomNumberAsString();
257                 ParanoidNumber b(number);
258                 float fb = strtof(number.c_str(), NULL);
259                 double db = strtod(number.c_str(), NULL);
260                 if (db == 0)
261                 {
262                         --i;
263                         continue;
264                 }
265                 long double ldb = strtold(number.c_str(), NULL);
266                 int op = rand() % 4;//= 2*(rand() % 2);
267                 while (op == 1)
268                         op = rand() % 4;
269                 //if (rand() % 2 == 0)
270                         //op = 2+(rand() % 2);
271                         
272                 opcount[op]++;
273                 ParanoidNumber olda(a);
274                 double oldda(da);
275                 switch (op)
276                 {
277                         case 0:
278                                 a += b;
279                                 fa += fb;
280                                 da += db;
281                                 lda += ldb;
282                                 break;
283                         case 1:
284                                 a -= b;
285                                 fa -= fb;
286                                 da -= db;
287                                 lda -= ldb;
288                                 break;
289                         case 2:
290                                 a *= b;
291                                 fa *= fb;
292                                 da *= db;
293                                 lda *= ldb;
294                                 break;
295                         case 3:
296                                 a /= b;
297                                 fa /= fb;
298                                 da /= db;
299                                 lda /= ldb;
300                                 break;
301                 }
302                 diff = 100.0*(fabs(a.ToDouble() - da) / da);
303                 if (!CloseEnough(lda, a))
304                 {
305                         Error("Op %i: ParanoidNumber probably doesn't work", i);
306                         Error("Operation: %lf %c %lf", oldda, opch[op], db);
307                         Error("As PN: %lf %c %lf", olda.ToDouble(), opch[op], b.ToDouble());
308                         Error("PN String before: %s", olda.Str().c_str());
309                         Error("PN String: %s", a.Str().c_str());
310                         Error("LONG double gives %.40llf", lda);
311                         Error("%.40llf, expected aboout %.40llf", a.Convert<long double>(), lda);
312                         a = da;
313                         
314                 }
315                 
316         }
317         printf("ParanoidNumber: {%s} = %.40lf\n", a.Str().c_str(), a.ToDouble());
318         printf("float: %.40f\n", fa);
319         printf("double: %.40lf\n", da);
320         printf("long double: %.40Lf\n", lda);
321         printf("diff %.40lf\n", diff);
322 }

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