1
2/*
3 * Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc.
4 * 	All Rights Reserved
5 *
6 * This file is a component of an X Window System-specific implementation
7 * of Xcms based on the TekColor Color Management System.  Permission is
8 * hereby granted to use, copy, modify, sell, and otherwise distribute this
9 * software and its documentation for any purpose and without fee, provided
10 * that this copyright, permission, and disclaimer notice is reproduced in
11 * all copies of this software and in supporting documentation.  TekColor
12 * is a trademark of Tektronix, Inc.
13 *
14 * Tektronix makes no representation about the suitability of this software
15 * for any purpose.  It is provided "as is" and with all faults.
16 *
17 * TEKTRONIX DISCLAIMS ALL WARRANTIES APPLICABLE TO THIS SOFTWARE,
18 * INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 * PARTICULAR PURPOSE.  IN NO EVENT SHALL TEKTRONIX BE LIABLE FOR ANY
20 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
21 * RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF
22 * CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
23 * CONNECTION WITH THE USE OR THE PERFORMANCE OF THIS SOFTWARE.
24 */
25
26/*
27 *	It should be pointed out that for simplicity's sake, the
28 *	environment parameters are defined as floating point constants,
29 *	rather than octal or hexadecimal initializations of allocated
30 *	storage areas.  This means that the range of allowed numbers
31 *	may not exactly match the hardware's capabilities.  For example,
32 *	if the maximum positive double precision floating point number
33 *	is EXACTLY 1.11...E100 and the constant "MAXDOUBLE is
34 *	defined to be 1.11E100 then the numbers between 1.11E100 and
35 *	1.11...E100 are considered to be undefined.  For most
36 *	applications, this will cause no problems.
37 *
38 *	An alternate method is to allocate a global static "double" variable,
39 *	say "maxdouble", and use a union declaration and initialization
40 *	to initialize it with the proper bits for the EXACT maximum value.
41 *	This was not done because the only compilers available to the
42 *	author did not fully support union initialization features.
43 *
44 */
45
46#ifdef HAVE_CONFIG_H
47#include <config.h>
48#endif
49#include "Xcmsint.h"
50
51/* forward/static */
52static double _XcmsModulo(double value, double base);
53static double _XcmsPolynomial(
54    register int order,
55    double const *coeffs,
56    double x);
57static double
58_XcmsModuloF(
59    double val,
60    register double *dp);
61
62/*
63 *	DEFINES
64 */
65#define XCMS_MAXERROR       	0.000001
66#define XCMS_MAXITER       	10000
67#define XCMS_PI       		3.14159265358979323846264338327950
68#define XCMS_TWOPI 		6.28318530717958620
69#define XCMS_HALFPI		1.57079632679489660
70#define XCMS_FOURTHPI		0.785398163397448280
71#define XCMS_SIXTHPI		0.523598775598298820
72#define XCMS_RADIANS(d)		((d) * XCMS_PI / 180.0)
73#define XCMS_DEGREES(r)		((r) * 180.0 / XCMS_PI)
74#ifdef __vax__
75#define XCMS_X6_UNDERFLOWS	(3.784659e-07)	/* X**6 almost underflows*/
76#else
77#define XCMS_X6_UNDERFLOWS	(4.209340e-52)	/* X**6 almost underflows */
78#endif
79#define XCMS_X16_UNDERFLOWS	(5.421010e-20)	/* X**16 almost underflows*/
80#define XCMS_CHAR_BIT		8
81#define XCMS_LONG_MAX		0x7FFFFFFF
82#define XCMS_DEXPLEN		11
83#define XCMS_NBITS(type)	(XCMS_CHAR_BIT * (int)sizeof(type))
84#define XCMS_FABS(x)		((x) < 0.0 ? -(x) : (x))
85
86/* XCMS_DMAXPOWTWO - largest power of two exactly representable as a double */
87#define XCMS_DMAXPOWTWO	((double)(XCMS_LONG_MAX) * \
88	    (1L << ((XCMS_NBITS(double)-XCMS_DEXPLEN) - XCMS_NBITS(int) + 1)))
89
90/*
91 *	LOCAL VARIABLES
92 */
93
94static double const cos_pcoeffs[] = {
95    0.12905394659037374438e7,
96   -0.37456703915723204710e6,
97    0.13432300986539084285e5,
98   -0.11231450823340933092e3
99};
100
101static double const cos_qcoeffs[] = {
102    0.12905394659037373590e7,
103    0.23467773107245835052e5,
104    0.20969518196726306286e3,
105    1.0
106};
107
108static double const sin_pcoeffs[] = {
109    0.20664343336995858240e7,
110   -0.18160398797407332550e6,
111    0.35999306949636188317e4,
112   -0.20107483294588615719e2
113};
114
115static double const sin_qcoeffs[] = {
116    0.26310659102647698963e7,
117    0.39270242774649000308e5,
118    0.27811919481083844087e3,
119    1.0
120};
121
122/*
123 *
124 *  FUNCTION
125 *
126 *	_XcmsCosine   double precision cosine
127 *
128 *  KEY WORDS
129 *
130 *	cos
131 *	machine independent routines
132 *	trigonometric functions
133 *	math libraries
134 *
135 *  DESCRIPTION
136 *
137 *	Returns double precision cosine of double precision
138 *	floating point argument.
139 *
140 *  USAGE
141 *
142 *	double _XcmsCosine (x)
143 *	double x;
144 *
145 *  REFERENCES
146 *
147 *	Computer Approximations, J.F. Hart et al, John Wiley & Sons,
148 *	1968, pp. 112-120.
149 *
150 *  RESTRICTIONS
151 *
152 *	The sin and cos routines are interactive in the sense that
153 *	in the process of reducing the argument to the range -PI/4
154 *	to PI/4, each may call the other.  Ultimately one or the
155 *	other uses a polynomial approximation on the reduced
156 *	argument.  The sin approximation has a maximum relative error
157 *	of 10**(-17.59) and the cos approximation has a maximum
158 *	relative error of 10**(-16.18).
159 *
160 *	These error bounds assume exact arithmetic
161 *	in the polynomial evaluation.  Additional rounding and
162 *	truncation errors may occur as the argument is reduced
163 *	to the range over which the polynomial approximation
164 *	is valid, and as the polynomial is evaluated using
165 *	finite-precision arithmetic.
166 *
167 *  PROGRAMMER
168 *
169 *	Fred Fish
170 *
171 *  INTERNALS
172 *
173 *	Computes cos(x) from:
174 *
175 *		(1)	Reduce argument x to range -PI to PI.
176 *
177 *		(2)	If x > PI/2 then call cos recursively
178 *			using relation cos(x) = -cos(x - PI).
179 *
180 *		(3)	If x < -PI/2 then call cos recursively
181 *			using relation cos(x) = -cos(x + PI).
182 *
183 *		(4)	If x > PI/4 then call sin using
184 *			relation cos(x) = sin(PI/2 - x).
185 *
186 *		(5)	If x < -PI/4 then call cos using
187 *			relation cos(x) = sin(PI/2 + x).
188 *
189 *		(6)	If x would cause underflow in approx
190 *			evaluation arithmetic then return
191 *			sqrt(1.0 - x**2).
192 *
193 *		(7)	By now x has been reduced to range
194 *			-PI/4 to PI/4 and the approximation
195 *			from HART pg. 119 can be used:
196 *
197 *			cos(x) = ( p(y) / q(y) )
198 *			Where:
199 *
200 *			y    =	x * (4/PI)
201 *
202 *			p(y) =  SUM [ Pj * (y**(2*j)) ]
203 *			over j = {0,1,2,3}
204 *
205 *			q(y) =  SUM [ Qj * (y**(2*j)) ]
206 *			over j = {0,1,2,3}
207 *
208 *			P0   =	0.12905394659037374438571854e+7
209 *			P1   =	-0.3745670391572320471032359e+6
210 *			P2   =	0.134323009865390842853673e+5
211 *			P3   =	-0.112314508233409330923e+3
212 *			Q0   =	0.12905394659037373590295914e+7
213 *			Q1   =	0.234677731072458350524124e+5
214 *			Q2   =	0.2096951819672630628621e+3
215 *			Q3   =	1.0000...
216 *			(coefficients from HART table #3843 pg 244)
217 *
218 *
219 *	**** NOTE ****    The range reduction relations used in
220 *	this routine depend on the final approximation being valid
221 *	over the negative argument range in addition to the positive
222 *	argument range.  The particular approximation chosen from
223 *	HART satisfies this requirement, although not explicitly
224 *	stated in the text.  This may not be true of other
225 *	approximations given in the reference.
226 *
227 */
228
229double _XcmsCosine(double x)
230{
231    auto double y;
232    auto double yt2;
233    double retval;
234
235    if (x < -XCMS_PI || x > XCMS_PI) {
236	x = _XcmsModulo (x, XCMS_TWOPI);
237        if (x > XCMS_PI) {
238	    x = x - XCMS_TWOPI;
239        } else if (x < -XCMS_PI) {
240	    x = x + XCMS_TWOPI;
241        }
242    }
243    if (x > XCMS_HALFPI) {
244	retval = -(_XcmsCosine (x - XCMS_PI));
245    } else if (x < -XCMS_HALFPI) {
246	retval = -(_XcmsCosine (x + XCMS_PI));
247    } else if (x > XCMS_FOURTHPI) {
248	retval = _XcmsSine (XCMS_HALFPI - x);
249    } else if (x < -XCMS_FOURTHPI) {
250	retval = _XcmsSine (XCMS_HALFPI + x);
251    } else if (x < XCMS_X6_UNDERFLOWS && x > -XCMS_X6_UNDERFLOWS) {
252	retval = _XcmsSquareRoot (1.0 - (x * x));
253    } else {
254	y = x / XCMS_FOURTHPI;
255	yt2 = y * y;
256	retval = _XcmsPolynomial (3, cos_pcoeffs, yt2) / _XcmsPolynomial (3, cos_qcoeffs, yt2);
257    }
258    return (retval);
259}
260
261
262/*
263 *  FUNCTION
264 *
265 *	_XcmsModulo   double precision modulo
266 *
267 *  KEY WORDS
268 *
269 *	_XcmsModulo
270 *	machine independent routines
271 *	math libraries
272 *
273 *  DESCRIPTION
274 *
275 *	Returns double precision modulo of two double
276 *	precision arguments.
277 *
278 *  USAGE
279 *
280 *	double _XcmsModulo (value, base)
281 *	double value;
282 *	double base;
283 *
284 *  PROGRAMMER
285 *
286 *	Fred Fish
287 *
288 */
289static double _XcmsModulo(double value, double base)
290{
291    auto double intpart;
292
293    value /= base;
294    value = _XcmsModuloF (value, &intpart);
295    value *= base;
296    return(value);
297}
298
299
300/*
301 * frac = (double) _XcmsModuloF(double val, double *dp)
302 *	return fractional part of 'val'
303 *	set *dp to integer part of 'val'
304 *
305 * Note  -> only compiled for the CA or KA.  For the KB/MC,
306 * "math.c" instantiates a copy of the inline function
307 * defined in "math.h".
308 */
309static double
310_XcmsModuloF(
311    double val,
312    register double *dp)
313{
314	register double abs;
315	/*
316	 * Don't use a register for this.  The extra precision this results
317	 * in on some systems causes problems.
318	 */
319	double ip;
320
321	/* should check for illegal values here - nan, inf, etc */
322	abs = XCMS_FABS(val);
323	if (abs >= XCMS_DMAXPOWTWO) {
324		ip = val;
325	} else {
326		ip = abs + XCMS_DMAXPOWTWO;	/* dump fraction */
327		ip -= XCMS_DMAXPOWTWO;	/* restore w/o frac */
328		if (ip > abs)		/* if it rounds up */
329			ip -= 1.0;	/* fix it */
330		ip = XCMS_FABS(ip);
331	}
332	*dp = ip;
333	return (val - ip); /* signed fractional part */
334}
335
336
337/*
338 *  FUNCTION
339 *
340 *	_XcmsPolynomial   double precision polynomial evaluation
341 *
342 *  KEY WORDS
343 *
344 *	poly
345 *	machine independent routines
346 *	math libraries
347 *
348 *  DESCRIPTION
349 *
350 *	Evaluates a polynomial and returns double precision
351 *	result.  Is passed a the order of the polynomial,
352 *	a pointer to an array of double precision polynomial
353 *	coefficients (in ascending order), and the independent
354 *	variable.
355 *
356 *  USAGE
357 *
358 *	double _XcmsPolynomial (order, coeffs, x)
359 *	int order;
360 *	double *coeffs;
361 *	double x;
362 *
363 *  PROGRAMMER
364 *
365 *	Fred Fish
366 *
367 *  INTERNALS
368 *
369 *	Evalates the polynomial using recursion and the form:
370 *
371 *		P(x) = P0 + x(P1 + x(P2 +...x(Pn)))
372 *
373 */
374
375static double _XcmsPolynomial(
376    register int order,
377    double const *coeffs,
378    double x)
379{
380    auto double rtn_value;
381
382    coeffs += order;
383    rtn_value = *coeffs--;
384    while(order-- > 0)
385	rtn_value = *coeffs-- + (x * rtn_value);
386
387    return(rtn_value);
388}
389
390
391/*
392 *  FUNCTION
393 *
394 *	_XcmsSine	double precision sine
395 *
396 *  KEY WORDS
397 *
398 *	sin
399 *	machine independent routines
400 *	trigonometric functions
401 *	math libraries
402 *
403 *  DESCRIPTION
404 *
405 *	Returns double precision sine of double precision
406 *	floating point argument.
407 *
408 *  USAGE
409 *
410 *	double _XcmsSine (x)
411 *	double x;
412 *
413 *  REFERENCES
414 *
415 *	Computer Approximations, J.F. Hart et al, John Wiley & Sons,
416 *	1968, pp. 112-120.
417 *
418 *  RESTRICTIONS
419 *
420 *	The sin and cos routines are interactive in the sense that
421 *	in the process of reducing the argument to the range -PI/4
422 *	to PI/4, each may call the other.  Ultimately one or the
423 *	other uses a polynomial approximation on the reduced
424 *	argument.  The sin approximation has a maximum relative error
425 *	of 10**(-17.59) and the cos approximation has a maximum
426 *	relative error of 10**(-16.18).
427 *
428 *	These error bounds assume exact arithmetic
429 *	in the polynomial evaluation.  Additional rounding and
430 *	truncation errors may occur as the argument is reduced
431 *	to the range over which the polynomial approximation
432 *	is valid, and as the polynomial is evaluated using
433 *	finite-precision arithmetic.
434 *
435 *  PROGRAMMER
436 *
437 *	Fred Fish
438 *
439 *  INTERNALS
440 *
441 *	Computes sin(x) from:
442 *
443 *	  (1)	Reduce argument x to range -PI to PI.
444 *
445 *	  (2)	If x > PI/2 then call sin recursively
446 *		using relation sin(x) = -sin(x - PI).
447 *
448 *	  (3)	If x < -PI/2 then call sin recursively
449 *		using relation sin(x) = -sin(x + PI).
450 *
451 *	  (4)	If x > PI/4 then call cos using
452 *		relation sin(x) = cos(PI/2 - x).
453 *
454 *	  (5)	If x < -PI/4 then call cos using
455 *		relation sin(x) = -cos(PI/2 + x).
456 *
457 *	  (6)	If x is small enough that polynomial
458 *		evaluation would cause underflow
459 *		then return x, since sin(x)
460 *		approaches x as x approaches zero.
461 *
462 *	  (7)	By now x has been reduced to range
463 *		-PI/4 to PI/4 and the approximation
464 *		from HART pg. 118 can be used:
465 *
466 *		sin(x) = y * ( p(y) / q(y) )
467 *		Where:
468 *
469 *		y    =  x * (4/PI)
470 *
471 *		p(y) =  SUM [ Pj * (y**(2*j)) ]
472 *		over j = {0,1,2,3}
473 *
474 *		q(y) =  SUM [ Qj * (y**(2*j)) ]
475 *		over j = {0,1,2,3}
476 *
477 *		P0   =  0.206643433369958582409167054e+7
478 *		P1   =  -0.18160398797407332550219213e+6
479 *		P2   =  0.359993069496361883172836e+4
480 *		P3   =  -0.2010748329458861571949e+2
481 *		Q0   =  0.263106591026476989637710307e+7
482 *		Q1   =  0.3927024277464900030883986e+5
483 *		Q2   =  0.27811919481083844087953e+3
484 *		Q3   =  1.0000...
485 *		(coefficients from HART table #3063 pg 234)
486 *
487 *
488 *	**** NOTE ****	  The range reduction relations used in
489 *	this routine depend on the final approximation being valid
490 *	over the negative argument range in addition to the positive
491 *	argument range.  The particular approximation chosen from
492 *	HART satisfies this requirement, although not explicitly
493 *	stated in the text.  This may not be true of other
494 *	approximations given in the reference.
495 *
496 */
497
498double
499_XcmsSine (double x)
500{
501    double y;
502    double yt2;
503    double retval;
504
505    if (x < -XCMS_PI || x > XCMS_PI) {
506	x = _XcmsModulo (x, XCMS_TWOPI);
507	if (x > XCMS_PI) {
508	    x = x - XCMS_TWOPI;
509	} else if (x < -XCMS_PI) {
510	    x = x + XCMS_TWOPI;
511	}
512    }
513    if (x > XCMS_HALFPI) {
514	retval = -(_XcmsSine (x - XCMS_PI));
515    } else if (x < -XCMS_HALFPI) {
516	retval = -(_XcmsSine (x + XCMS_PI));
517    } else if (x > XCMS_FOURTHPI) {
518	retval = _XcmsCosine (XCMS_HALFPI - x);
519    } else if (x < -XCMS_FOURTHPI) {
520	retval = -(_XcmsCosine (XCMS_HALFPI + x));
521    } else if (x < XCMS_X6_UNDERFLOWS && x > -XCMS_X6_UNDERFLOWS) {
522	retval = x;
523    } else {
524	y = x / XCMS_FOURTHPI;
525	yt2 = y * y;
526	retval = y * (_XcmsPolynomial (3, sin_pcoeffs, yt2) / _XcmsPolynomial(3, sin_qcoeffs, yt2));
527    }
528    return(retval);
529}
530
531
532/*
533 *	NAME
534 *		_XcmsArcTangent
535 *
536 *	SYNOPSIS
537 */
538double
539_XcmsArcTangent(double x)
540/*
541 *	DESCRIPTION
542 *		Computes the arctangent.
543 *		This is an implementation of the Gauss algorithm as
544 *		described in:
545 *		    Forman S. Acton, Numerical Methods That Work,
546 *			New York, NY, Harper & Row, 1970.
547 *
548 *	RETURNS
549 *		Returns the arctangent
550 */
551{
552    double ai, a1 = 0.0, bi, b1 = 0.0, l, d;
553    double maxerror;
554    int i;
555
556    if (x == 0.0)  {
557	return (0.0);
558    }
559    if (x < 1.0) {
560	maxerror = x * XCMS_MAXERROR;
561    } else {
562	maxerror = XCMS_MAXERROR;
563    }
564    ai = _XcmsSquareRoot( 1.0 / (1.0 + (x * x)) );
565    bi = 1.0;
566    for (i = 0; i < XCMS_MAXITER; i++) {
567	a1 = (ai + bi) / 2.0;
568	b1 = _XcmsSquareRoot((a1 * bi));
569	if (a1 == b1)
570	    break;
571	d = XCMS_FABS(a1 - b1);
572	if (d < maxerror)
573	    break;
574	ai = a1;
575	bi = b1;
576    }
577
578    l = ((a1 > b1) ? b1 : a1);
579
580    a1 = _XcmsSquareRoot(1 + (x * x));
581    return (x / (a1 * l));
582}
583