fixed lots of warnings in testprograms
[libfirm] / ir / be / test / fbench.c
1 /*
2
3         John Walker's Floating Point Benchmark, derived from...
4
5         Marinchip Interactive Lens Design System
6
7                                      John Walker   December 1980
8
9         By John Walker
10            http://www.fourmilab.ch/
11
12         This  program may be used, distributed, and modified freely as
13         long as the origin information is preserved.
14
15         This  is  a  complete  optical  design  raytracing  algorithm,
16         stripped of its user interface and recast into portable C.  It
17         not only determines execution speed on an  extremely  floating
18         point   (including   trig   function)   intensive   real-world
19         application, it  checks  accuracy  on  an  algorithm  that  is
20         exquisitely  sensitive  to  errors.   The  performance of this
21         program is typically far more  sensitive  to  changes  in  the
22         efficiency  of  the  trigonometric  library  routines than the
23         average floating point program.
24
25         The benchmark may be compiled in two  modes.   If  the  symbol
26         INTRIG  is  defined,  built-in  trigonometric  and square root
27         routines will be used for all calculations.  Timings made with
28         INTRIG  defined  reflect  the  machine's  basic floating point
29         performance for the arithmetic operators.  If  INTRIG  is  not
30         defined,  the  system  library  <math.h>  functions  are used.
31         Results with INTRIG not defined reflect the  system's  library
32         performance  and/or  floating  point hardware support for trig
33         functions and square root.  Results with INTRIG defined are  a
34         good  guide  to  general  floating  point  performance,  while
35         results with INTRIG undefined indicate the performance  of  an
36         application which is math function intensive.
37
38         Special  note  regarding  errors in accuracy: this program has
39         generated numbers identical to the last digit it  formats  and
40         checks on the following machines, floating point
41         architectures, and languages:
42
43         Marinchip 9900    QBASIC    IBM 370 double-precision (REAL * 8) format
44
45         IBM PC / XT / AT  Lattice C IEEE 64 bit, 80 bit temporaries
46                           High C    same, in line 80x87 code
47                           BASICA    "Double precision"
48                           Quick BASIC IEEE double precision, software routines
49
50         Sun 3             C         IEEE 64 bit, 80 bit temporaries,
51                                     in-line 68881 code, in-line FPA code.
52
53         MicroVAX II       C         Vax "G" format floating point
54
55         Macintosh Plus    MPW C     SANE floating point, IEEE 64 bit format
56                                     implemented in ROM.
57
58         Inaccuracies  reported  by  this  program should be taken VERY
59         SERIOUSLY INDEED, as the program has been demonstrated  to  be
60         invariant  under  changes in floating point format, as long as
61         the format is a recognised double precision  format.   If  you
62         encounter errors, please remember that they are just as likely
63         to  be  in  the  floating  point  editing   library   or   the
64         trigonometric  libraries  as  in  the low level operator code.
65
66         The benchmark assumes that results are basically reliable, and
67         only tests the last result computed against the reference.  If
68         you're running on  a  suspect  system  you  can  compile  this
69         program  with  ACCURACY defined.  This will generate a version
70         which executes as an infinite loop, performing the  ray  trace
71         and checking the results on every pass.  All incorrect results
72         will be reported.
73
74         Representative  timings  are  given  below.   All  have   been
75         normalised as if run for 1000 iterations.
76
77   Time in seconds                  Computer, Compiler, and notes
78  Normal      INTRIG
79
80  3466.00    4031.00     Commodore 128, 2 Mhz 8510 with software floating
81                         point.  Abacus Software/Data-Becker Super-C 128,
82                         version 3.00, run in fast (2 Mhz) mode.  Note:
83                         the results generated by this system differed
84                         from the reference results in the 8th to 10th
85                         decimal place.
86
87  3290.00                IBM PC/AT 6 Mhz, Microsoft/IBM BASICA version A3.00.
88                         Run with the "/d" switch, software floating point.
89
90  2131.50                IBM PC/AT 6 Mhz, Lattice C version 2.14, small model.
91                         This version of Lattice compiles subroutine
92                         calls which either do software floating point
93                         or use the 80x87.  The machine on which I ran
94                         this had an 80287, but the results were so bad
95                         I wonder if it was being used.
96
97  1598.00                Macintosh Plus, MPW C, SANE Software floating point.
98
99  1582.13                Marinchip 9900 2 Mhz, QBASIC compiler with software
100                         floating point.  This was a QBASIC version of the
101                         program which contained the identical algorithm.
102
103   404.00                IBM PC/AT 6 Mhz, Microsoft QuickBASIC version 2.0.
104                         Software floating point.
105
106   165.15                IBM PC/AT 6 Mhz, Metaware High C version 1.3, small
107                         model.  This was compiled to call subroutines for
108                         floating point, and the machine contained an 80287
109                         which was used by the subroutines.
110
111   143.20                Macintosh II, MPW C, SANE calls.  I was unable to
112                         determine whether SANE was using the 68881 chip or
113                         not.
114
115   121.80                Sun 3/160 16 Mhz, Sun C.  Compiled with -fsoft switch
116                         which executes floating point in software.
117
118    78.78     110.11     IBM RT PC (Model 6150).  IBM AIX 1.0 C compiler
119                         with -O switch.
120
121    75.2      254.0      Microsoft Quick C 1.0, in-line 8087 instructions,
122                         compiled with 80286 optimisation on.  (Switches
123                         were -Ol -FPi87-G2 -AS).  Small memory model.
124
125    69.50                IBM PC/AT 6Mhz, Borland Turbo BASIC 1.0.  Compiled
126                         in "8087 required" mode to generate in-line
127                         code for the math coprocessor.
128
129    66.96                IBM PC/AT 6Mhz, Microsoft QuickBASIC 4.0.  This
130                         release of QuickBASIC compiles code for the
131                         80287 math coprocessor.
132
133    66.36     206.35     IBM PC/AT 6Mhz, Metaware High C version 1.3, small
134                         model.  This was compiled with in-line code for the
135                         80287 math coprocessor.  Trig functions still call
136                         library routines.
137
138    63.07     220.43     IBM PC/AT, 6Mhz, Borland Turbo C, in-line 8087 code,
139                         small model, word alignment, no stack checking,
140                         8086 code mode.
141
142    17.18                Apollo DN-3000, 12 Mhz 68020 with 68881, compiled
143                         with in-line code for the 68881 coprocessor.
144                         According to Apollo, the library routines are chosen
145                         at runtime based on coprocessor presence.  Since the
146                         coprocessor was present, the library is supposed to
147                         use in-line floating point code.
148
149    15.55      27.56     VAXstation II GPX.  Compiled and executed under
150                         VAX/VMS C.
151
152    15.14      37.93     Macintosh II, Unix system V.  Green Hills 68020
153                         Unix compiler with in-line code for the 68881
154                         coprocessor (-O -ZI switches).
155
156    12.69                Sun 3/160 16 Mhz, Sun C.  Compiled with -fswitch,
157                         which calls a subroutine to select the fastest
158                         floating point processor.  This was using the 68881.
159
160    11.74      26.73     Compaq Deskpro 386, 16 Mhz 80386 with 16 Mhz 80387.
161                         Metaware High C version 1.3, compiled with in-line
162                         for the math coprocessor (but not optimised for the
163                         80386/80387).  Trig functions still call library
164                         routines.
165
166     8.43      30.49     Sun 3/160 16 Mhz, Sun C.  Compiled with -f68881,
167                         generating in-line MC68881 instructions.  Trig
168                         functions still call library routines.
169
170     6.29      25.17     Sun 3/260 25 Mhz, Sun C.  Compiled with -f68881,
171                         generating in-line MC68881 instructions.  Trig
172                         functions still call library routines.
173
174     4.57                Sun 3/260 25 Mhz, Sun FORTRAN 77.  Compiled with
175                         -O -f68881, generating in-line MC68881 instructions.
176                         Trig functions are compiled in-line.  This used
177                         the FORTRAN 77 version of the program, FBFORT77.F.
178
179     4.00      14.20     Sun386i/25 Mhz model 250, Sun C compiler.
180
181     4.00      14.00     Sun386i/25 Mhz model 250, Metaware C.
182
183     3.10      12.00     Compaq 386/387 25 Mhz running SCO Xenix 2.
184                         Compiled with Metaware HighC 386, optimized
185                         for 386.
186
187     3.00      12.00     Compaq 386/387 25MHZ optimized for 386/387.
188
189     2.96       5.17     Sun 4/260, Sparc RISC processor.  Sun C,
190                         compiled with the -O2 switch for global
191                         optimisation.
192
193     2.47                COMPAQ 486/25, secondary cache disabled, High C,
194                         486/387, inline f.p., small memory model.
195
196     2.20       3.40     Data General Motorola 88000, 16 Mhz, Gnu C.
197
198     1.56                COMPAQ 486/25, 128K secondary cache, High C, 486/387,
199                         inline f.p., small memory model.
200
201     0.66       1.50     DEC Pmax, Mips processor.
202
203     0.63       0.91     Sun SparcStation 2, Sun C (SunOS 4.1.1) with
204                         -O4 optimisation and "/usr/lib/libm.il" inline
205                         floating point.
206
207     0.60       1.07     Intel 860 RISC processor, 33 Mhz, Greenhills
208                         C compiler.
209
210     0.40       0.90     Dec 3MAX, MIPS 3000 processor, -O4.
211
212     0.31       0.90     IBM RS/6000, -O.
213
214     0.1129     0.2119   Dell Dimension XPS P133c, Pentium 133 MHz,
215                         Windows 95, Microsoft Visual C 5.0.
216
217     0.0883     0.2166   Silicon Graphics Indigo², MIPS R4400,
218                         175 Mhz, "-O3".
219
220     0.0351     0.0561   Dell Dimension XPS R100, Pentium II 400 MHz,
221                         Windows 98, Microsoft Visual C 5.0.
222
223     0.0312     0.0542   Sun Ultra 2, UltraSPARC V9, 300 MHz, Solaris
224                         2.5.1.
225
226     0.00862    0.01074  Dell Inspiron 9100, Pentium 4, 3.4 GHz, gcc -O3.
227
228 */
229
230 #include <stdio.h>
231 #include <stdlib.h>
232 #include <string.h>
233 #ifndef INTRIG
234 #include <math.h>
235 #endif
236
237 #define cot(x) (1.0 / tan(x))
238
239 #undef ACCURACY
240
241 #define TRUE  1
242 #define FALSE 0
243
244 #define max_surfaces 10
245
246 /*  Local variables  */
247
248 static char tbfr[132];
249
250 static short current_surfaces;
251 static short paraxial;
252
253 static double clear_aperture;
254
255 static double aberr_lspher;
256 static double aberr_osc;
257 static double aberr_lchrom;
258
259 static double max_lspher;
260 static double max_osc;
261 static double max_lchrom;
262
263 static double radius_of_curvature;
264 static double object_distance;
265 static double ray_height;
266 static double axis_slope_angle;
267 static double from_index;
268 static double to_index;
269
270 static double spectral_line[9];
271 static double s[max_surfaces][5];
272 static double od_sa[2][2];
273
274 static char outarr[8][80];         /* Computed output of program goes here */
275
276 int itercount;                     /* The iteration counter for the main loop
277                                       in the program is made global so that
278                                       the compiler should not be allowed to
279                                       optimise out the loop over the ray
280                                       tracing code. */
281
282 #ifndef ITERATIONS
283 #define ITERATIONS 1000
284 #endif
285 int niter = ITERATIONS;            /* Iteration counter */
286
287 static char *refarr[] = {          /* Reference results.  These happen to
288                                       be derived from a run on Microsoft
289                                       Quick BASIC on the IBM PC/AT. */
290
291         "   Marginal ray          47.09479120920   0.04178472683",
292         "   Paraxial ray          47.08372160249   0.04177864821",
293         "Longitudinal spherical aberration:        -0.01106960671",
294         "    (Maximum permissible):                 0.05306749907",
295         "Offense against sine condition (coma):     0.00008954761",
296         "    (Maximum permissible):                 0.00250000000",
297         "Axial chromatic aberration:                0.00448229032",
298         "    (Maximum permissible):                 0.05306749907"
299 };
300
301 /* The  test  case  used  in  this program is the  design for a 4 inch
302    achromatic telescope  objective  used  as  the  example  in  Wyld's
303    classic  work  on  ray  tracing by hand, given in Amateur Telescope
304    Making, Volume 3.  */
305
306 static double testcase[4][4] = {
307         {27.05, 1.5137, 63.6, 0.52},
308         {-16.68, 1, 0, 0.138},
309         {-16.68, 1.6164, 36.7, 0.38},
310         {-78.1, 1, 0, 0}
311 };
312
313 /*  Internal trig functions (used only if INTRIG is  defined).   These
314     standard  functions  may be enabled to obtain timings that reflect
315     the machine's floating point performance rather than the speed  of
316     its trig function evaluation.  */
317
318 #ifdef INTRIG
319
320 /*  The following definitions should keep you from getting intro trouble
321     with compilers which don't let you redefine intrinsic functions.  */
322
323 #define sin I_sin
324 #define cos I_cos
325 #define tan I_tan
326 #define sqrt I_sqrt
327 #define atan I_atan
328 #define atan2 I_atan2
329 #define asin I_asin
330
331 #define fabs(x)  ((x < 0.0) ? -x : x)
332
333 #define pic 3.1415926535897932
334
335 /*  Commonly used constants  */
336
337 static double pi = pic,
338         twopi =pic * 2.0,
339         piover4 = pic / 4.0,
340         fouroverpi = 4.0 / pic,
341         piover2 = pic / 2.0;
342
343 /*  Coefficients for ATAN evaluation  */
344
345 static double atanc[] = {
346         0.0,
347         0.4636476090008061165,
348         0.7853981633974483094,
349         0.98279372324732906714,
350         1.1071487177940905022,
351         1.1902899496825317322,
352         1.2490457723982544262,
353         1.2924966677897852673,
354         1.3258176636680324644
355 };
356
357 /*  aint(x)       Return integer part of number.  Truncates towards 0    */
358
359 double aint(x)
360 double x;
361 {
362         long l;
363
364         /*  Note that this routine cannot handle the full floating point
365             number range.  This function should be in the machine-dependent
366             floating point library!  */
367
368         l = x;
369         if ((int)(-0.5) != 0  &&  l < 0 )
370            l++;
371         x = l;
372         return x;
373 }
374
375 /*  sin(x)        Return sine, x in radians  */
376
377 static double sin(x)
378 double x;
379 {
380         int sign;
381         double y, r, z;
382
383         x = (((sign= (x < 0.0)) != 0) ? -x: x);
384
385         if (x > twopi)
386            x -= (aint(x / twopi) * twopi);
387
388         if (x > pi) {
389            x -= pi;
390            sign = !sign;
391         }
392
393         if (x > piover2)
394            x = pi - x;
395
396         if (x < piover4) {
397            y = x * fouroverpi;
398            z = y * y;
399            r = y * (((((((-0.202253129293E-13 * z + 0.69481520350522E-11) * z -
400               0.17572474176170806E-8) * z + 0.313361688917325348E-6) * z -
401               0.365762041821464001E-4) * z + 0.249039457019271628E-2) * z -
402               0.0807455121882807815) * z + 0.785398163397448310);
403         } else {
404            y = (piover2 - x) * fouroverpi;
405            z = y * y;
406            r = ((((((-0.38577620372E-12 * z + 0.11500497024263E-9) * z -
407               0.2461136382637005E-7) * z + 0.359086044588581953E-5) * z -
408               0.325991886926687550E-3) * z + 0.0158543442438154109) * z -
409               0.308425137534042452) * z + 1.0;
410         }
411         return sign ? -r : r;
412 }
413
414 /*  cos(x)        Return cosine, x in radians, by identity  */
415
416 static double cos(x)
417 double x;
418 {
419         x = (x < 0.0) ? -x : x;
420         if (x > twopi)                /* Do range reduction here to limit */
421            x = x - (aint(x / twopi) * twopi); /* roundoff on add of PI/2    */
422         return sin(x + piover2);
423 }
424
425 /*  tan(x)        Return tangent, x in radians, by identity  */
426
427 static double tan(x)
428 double x;
429 {
430         return sin(x) / cos(x);
431 }
432
433 /*  sqrt(x)       Return square root.  Initial guess, then Newton-
434                   Raphson refinement  */
435
436 double sqrt(x)
437 double x;
438 {
439         double c, cl, y;
440         int n;
441
442         if (x == 0.0)
443            return 0.0;
444
445         if (x < 0.0) {
446            fprintf(stderr,
447               "\nGood work!  You tried to take the square root of %g",
448              x);
449            fprintf(stderr,
450               "\nunfortunately, that is too complex for me to handle.\n");
451            exit(1);
452         }
453
454         y = (0.154116 + 1.893872 * x) / (1.0 + 1.047988 * x);
455
456         c = (y - x / y) / 2.0;
457         cl = 0.0;
458         for (n = 50; c != cl && n--;) {
459            y = y - c;
460            cl = c;
461            c = (y - x / y) / 2.0;
462         }
463         return y;
464 }
465
466 /*  atan(x)       Return arctangent in radians,
467                   range -pi/2 to pi/2  */
468
469 static double atan(x)
470 double x;
471 {
472         int sign, l, y;
473         double a, b, z;
474
475         x = (((sign = (x < 0.0)) != 0) ? -x : x);
476         l = 0;
477
478         if (x >= 4.0) {
479            l = -1;
480            x = 1.0 / x;
481            y = 0;
482            goto atl;
483         } else {
484            if (x < 0.25) {
485               y = 0;
486               goto atl;
487            }
488         }
489
490         y = aint(x / 0.5);
491         z = y * 0.5;
492         x = (x - z) / (x * z + 1);
493
494 atl:
495         z = x * x;
496         b = ((((893025.0 * z + 49116375.0) * z + 425675250.0) * z +
497             1277025750.0) * z + 1550674125.0) * z + 654729075.0;
498         a = (((13852575.0 * z + 216602100.0) * z + 891080190.0) * z +
499             1332431100.0) * z + 654729075.0;
500         a = (a / b) * x + atanc[y];
501         if (l)
502            a=piover2 - a;
503         return sign ? -a : a;
504 }
505
506 /*  atan2(y,x)    Return arctangent in radians of y/x,
507                   range -pi to pi  */
508
509 static double atan2(y, x)
510 double y, x;
511 {
512         double temp;
513
514         if (x == 0.0) {
515            if (y == 0.0)   /*  Special case: atan2(0,0) = 0  */
516               return 0.0;
517            else if (y > 0)
518               return piover2;
519            else
520               return -piover2;
521         }
522         temp = atan(y / x);
523         if (x < 0.0) {
524            if (y >= 0.0)
525               temp += pic;
526            else
527               temp -= pic;
528         }
529         return temp;
530 }
531
532 /*  asin(x)       Return arcsine in radians of x  */
533
534 static double asin(x)
535 double x;
536 {
537         if (fabs(x)>1.0) {
538            fprintf(stderr,
539               "\nInverse trig functions lose much of their gloss when");
540            fprintf(stderr,
541               "\ntheir arguments are greater than 1, such as the");
542            fprintf(stderr,
543               "\nvalue %g you passed.\n", x);
544            exit(1);
545         }
546         return atan2(x, sqrt(1 - x * x));
547 }
548 #endif
549
550 /*            Calculate passage through surface
551
552               If  the variable PARAXIAL is true, the trace through the
553               surface will be done using the paraxial  approximations.
554               Otherwise,  the normal trigonometric trace will be done.
555
556               This routine takes the following inputs:
557
558               RADIUS_OF_CURVATURE         Radius of curvature of surface
559                                           being crossed.  If 0, surface is
560                                           plane.
561
562               OBJECT_DISTANCE             Distance of object focus from
563                                           lens vertex.  If 0, incoming
564                                           rays are parallel and
565                                           the following must be specified:
566
567               RAY_HEIGHT                  Height of ray from axis.  Only
568                                           relevant if OBJECT.DISTANCE == 0
569
570               AXIS_SLOPE_ANGLE            Angle incoming ray makes with axis
571                                           at intercept
572
573               FROM_INDEX                  Refractive index of medium being left
574
575               TO_INDEX                    Refractive index of medium being
576                                           entered.
577
578               The outputs are the following variables:
579
580               OBJECT_DISTANCE             Distance from vertex to object focus
581                                           after refraction.
582
583               AXIS_SLOPE_ANGLE            Angle incoming ray makes with axis
584                                           at intercept after refraction.
585
586 */
587
588 static void transit_surface() {
589         double iang,               /* Incidence angle */
590                rang,               /* Refraction angle */
591                iang_sin,           /* Incidence angle sin */
592                rang_sin,           /* Refraction angle sin */
593                old_axis_slope_angle, sagitta;
594
595         if (paraxial) {
596            if (radius_of_curvature != 0.0) {
597               if (object_distance == 0.0) {
598                  axis_slope_angle = 0.0;
599                  iang_sin = ray_height / radius_of_curvature;
600               } else
601                  iang_sin = ((object_distance -
602                     radius_of_curvature) / radius_of_curvature) *
603                     axis_slope_angle;
604
605               rang_sin = (from_index / to_index) *
606                  iang_sin;
607               old_axis_slope_angle = axis_slope_angle;
608               axis_slope_angle = axis_slope_angle +
609                  iang_sin - rang_sin;
610               if (object_distance != 0.0)
611                  ray_height = object_distance * old_axis_slope_angle;
612               object_distance = ray_height / axis_slope_angle;
613               return;
614            }
615            object_distance = object_distance * (to_index / from_index);
616            axis_slope_angle = axis_slope_angle * (from_index / to_index);
617            return;
618         }
619
620         if (radius_of_curvature != 0.0) {
621            if (object_distance == 0.0) {
622               axis_slope_angle = 0.0;
623               iang_sin = ray_height / radius_of_curvature;
624            } else {
625               iang_sin = ((object_distance -
626                  radius_of_curvature) / radius_of_curvature) *
627                  sin(axis_slope_angle);
628            }
629            iang = asin(iang_sin);
630            rang_sin = (from_index / to_index) *
631               iang_sin;
632            old_axis_slope_angle = axis_slope_angle;
633            axis_slope_angle = axis_slope_angle +
634               iang - asin(rang_sin);
635            sagitta = sin((old_axis_slope_angle + iang) / 2.0);
636            sagitta = 2.0 * radius_of_curvature*sagitta*sagitta;
637            object_distance = ((radius_of_curvature * sin(
638               old_axis_slope_angle + iang)) *
639               cot(axis_slope_angle)) + sagitta;
640            return;
641         }
642
643         rang = -asin((from_index / to_index) *
644            sin(axis_slope_angle));
645         object_distance = object_distance * ((to_index *
646            cos(-rang)) / (from_index *
647            cos(axis_slope_angle)));
648         axis_slope_angle = -rang;
649 }
650
651 /*  Perform ray trace in specific spectral line  */
652
653 static void trace_line(line, ray_h)
654 int line;
655 double ray_h;
656 {
657         int i;
658
659         object_distance = 0.0;
660         ray_height = ray_h;
661         from_index = 1.0;
662
663         for (i = 1; i <= current_surfaces; i++) {
664            radius_of_curvature = s[i][1];
665            to_index = s[i][2];
666            if (to_index > 1.0)
667               to_index = to_index + ((spectral_line[4] -
668                  spectral_line[line]) /
669                  (spectral_line[3] - spectral_line[6])) * ((s[i][2] - 1.0) /
670                  s[i][3]);
671            transit_surface();
672            from_index = to_index;
673            if (i < current_surfaces)
674               object_distance = object_distance - s[i][4];
675         }
676 }
677
678 /*  Initialise when called the first time  */
679
680 int main(argc, argv)
681 int argc;
682 char *argv[];
683 {
684         int i, j, k, errors;
685         double od_fline, od_cline;
686 #ifdef ACCURACY
687         long passes;
688 #endif
689
690         spectral_line[1] = 7621.0;       /* A */
691         spectral_line[2] = 6869.955;     /* B */
692         spectral_line[3] = 6562.816;     /* C */
693         spectral_line[4] = 5895.944;     /* D */
694         spectral_line[5] = 5269.557;     /* E */
695         spectral_line[6] = 4861.344;     /* F */
696         spectral_line[7] = 4340.477;     /* G'*/
697         spectral_line[8] = 3968.494;     /* H */
698
699         /* Process the number of iterations argument, if one is supplied. */
700
701         if (argc > 1) {
702            niter = atoi(argv[1]);
703            if (*argv[1] == '-' || niter < 1) {
704               printf("This is John Walker's floating point accuracy and\n");
705               printf("performance benchmark program.  You call it with\n");
706               printf("\nfbench <itercount>\n\n");
707               printf("where <itercount> is the number of iterations\n");
708               printf("to be executed.  Archival timings should be made\n");
709               printf("with the iteration count set so that roughly five\n");
710               printf("minutes of execution is timed.\n");
711               exit(0);
712            }
713         }
714
715         /* Load test case into working array */
716
717         clear_aperture = 4.0;
718         current_surfaces = 4;
719         for (i = 0; i < current_surfaces; i++)
720            for (j = 0; j < 4; j++)
721               s[i + 1][j + 1] = testcase[i][j];
722
723 #ifdef ACCURACY
724         printf("Beginning execution of floating point accuracy test...\n");
725         passes = 0;
726 #else
727         printf("Ready to begin John Walker's floating point accuracy\n");
728         printf("and performance benchmark.  %d iterations will be made.\n\n",
729            niter);
730
731         printf("\nMeasured run time in seconds should be divided by %.f\n", niter / 1000.0);
732         printf("to normalise for reporting results.  For archival results,\n");
733         printf("adjust iteration count so the benchmark runs about five minutes.\n\n");
734
735 #endif
736
737         /* Perform ray trace the specified number of times. */
738
739 #ifdef ACCURACY
740         while (TRUE) {
741            passes++;
742            if ((passes % 100L) == 0) {
743               printf("Pass %ld.\n", passes);
744            }
745 #else
746         for (itercount = 0; itercount < niter; itercount++) {
747 #endif
748
749            for (paraxial = 0; paraxial <= 1; paraxial++) {
750
751               /* Do main trace in D light */
752
753               trace_line(4, clear_aperture / 2.0);
754               od_sa[paraxial][0] = object_distance;
755               od_sa[paraxial][1] = axis_slope_angle;
756            }
757            paraxial = FALSE;
758
759            /* Trace marginal ray in C */
760
761            trace_line(3, clear_aperture / 2.0);
762            od_cline = object_distance;
763
764            /* Trace marginal ray in F */
765
766            trace_line(6, clear_aperture / 2.0);
767            od_fline = object_distance;
768
769            aberr_lspher = od_sa[1][0] - od_sa[0][0];
770            aberr_osc = 1.0 - (od_sa[1][0] * od_sa[1][1]) /
771               (sin(od_sa[0][1]) * od_sa[0][0]);
772            aberr_lchrom = od_fline - od_cline;
773            max_lspher = sin(od_sa[0][1]);
774
775            /* D light */
776
777            max_lspher = 0.0000926 / (max_lspher * max_lspher);
778            max_osc = 0.0025;
779            max_lchrom = max_lspher;
780 #ifndef ACCURACY
781         }
782
783         printf("Stop the timer:\007");
784         //gets(tbfr);
785 #endif
786
787         /* Now evaluate the accuracy of the results from the last ray trace */
788
789         sprintf(outarr[0], "%15s   %21.11f  %14.11f",
790            "Marginal ray", od_sa[0][0], od_sa[0][1]);
791         sprintf(outarr[1], "%15s   %21.11f  %14.11f",
792            "Paraxial ray", od_sa[1][0], od_sa[1][1]);
793         sprintf(outarr[2],
794            "Longitudinal spherical aberration:      %16.11f",
795            aberr_lspher);
796         sprintf(outarr[3],
797            "    (Maximum permissible):              %16.11f",
798            max_lspher);
799         sprintf(outarr[4],
800            "Offense against sine condition (coma):  %16.11f",
801            aberr_osc);
802         sprintf(outarr[5],
803            "    (Maximum permissible):              %16.11f",
804            max_osc);
805         sprintf(outarr[6],
806            "Axial chromatic aberration:             %16.11f",
807            aberr_lchrom);
808         sprintf(outarr[7],
809            "    (Maximum permissible):              %16.11f",
810            max_lchrom);
811
812         /* Now compare the edited results with the master values from
813            reference executions of this program. */
814
815         errors = 0;
816         for (i = 0; i < 8; i++) {
817            if (strcmp(outarr[i], refarr[i]) != 0) {
818 #ifdef ACCURACY
819               printf("\nError in pass %ld for results on line %d...\n",
820                      passes, i + 1);
821 #else
822               printf("\nError in results on line %d...\n", i + 1);
823 #endif
824               printf("Expected:  \"%s\"\n", refarr[i]);
825               printf("Received:  \"%s\"\n", outarr[i]);
826               printf("(Errors)    ");
827               k = strlen(refarr[i]);
828               for (j = 0; j < k; j++) {
829                  printf("%c", refarr[i][j] == outarr[i][j] ? ' ' : '^');
830                  if (refarr[i][j] != outarr[i][j])
831                     errors++;
832               }
833               printf("\n");
834            }
835         }
836 #ifdef ACCURACY
837         }
838 #else
839         if (errors > 0) {
840            printf("\n%d error%s in results.  This is VERY SERIOUS.\n",
841               errors, errors > 1 ? "s" : "");
842         } else
843            printf("\nNo errors in results.\n");
844 #endif
845
846         return 0;
847 }