1 /* $Id: utility.c,v 1.18 2009-04-20 07:17:00 pkubanek Exp $
2  **
3  * Copyright (C) 1999, 2000 Juan Carlos Remis
4  * Copyright (C) 2002 Liam Girdwood <lgirdwood@gmail.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  */
21 
22 /*------------------------------------------------------------------------*/
23 /*                                                                        */
24 /*  Module:                                                               */
25 /*                                                                        */
26 /*  Description:                                                          */
27 /*                                                                        */
28 /*                                                                        */
29 /*  "CAVEAT UTILITOR".                                                    */
30 /*                                                                        */
31 /*                   "Non sunt multiplicanda entia praeter necessitatem"  */
32 /*                                                   Guillermo de Occam.  */
33 /*------------------------------------------------------------------------*/
34 /*  Revision History:                                                     */
35 /*                                                                        */
36 /*------------------------------------------------------------------------*/
37 
38 /**/
39 module nova.utility;
40 
41 import std.stdio;
42 import std.math;
43 
44 import nova;
45 
46 /* Golden ratio */
47 enum GOLDEN = 1.61803398875;
48 
49 enum DM_PI = (2*PI);
50 enum RADIAN = (180.0 / PI);
51 
52 /* Conversion factors between degrees and radians */
53 enum D2R = (1.7453292519943295769e-2);  /* deg->radian */
54 enum R2D = (5.7295779513082320877e1);   /* radian->deg */
55 enum R2S = (2.0626480624709635516e5);   /* arc seconds per radian */
56 enum S2R = (4.8481368110953599359e-6);  /* radians per arc second */
57 
58 static string ln_version = LIBNOVA_VERSION;
59 
60 extern (C) {
61 
62 /**
63  * \fn double ln_rad_to_deg(double radians)
64  * \brief radians to degrees
65  * \ingroup conversion
66  * \param radians Angle in radians
67  * \return Angle in degrees
68  */
69 static @nogc double ln_rad_to_deg(double radians) nothrow
70 {
71     return radians * R2D;
72 }
73 
74 /**
75  * convert degrees to radians
76  * \ingroup conversion
77  * \param degrees Angle in degrees
78  * \return Angle in radians
79  */
80 static @nogc double ln_deg_to_rad(double degrees) nothrow
81 {
82     return degrees * D2R;
83 }
84 
85 /*! \fn char * ln_get_version (void)
86 * \return Null terminated version string.
87 *
88 * Return the libnova library version number string
89 * e.g. "0.4.0"
90 */
91 @nogc string ln_get_version() nothrow
92 {
93     return ln_version;
94 }
95 
96 /* convert hours:mins:secs to degrees */
97 @nogc double ln_hms_to_deg(const ref ln_hms hms) nothrow
98 {
99     double degrees;
100 
101     degrees = (cast(double)hms.hours / 24.0) * 360.0;
102     degrees += (cast(double)hms.minutes / 60.0) * 15.0;
103     degrees += (cast(double)hms.seconds / 60.0) * 0.25;
104 
105     return degrees;
106 }
107 
108 /* convert hours:mins:secs to radians */
109 @nogc double ln_hms_to_rad(const ref ln_hms hms) nothrow
110 {
111     double radians;
112 
113     radians = (cast(double)hms.hours / 24.0) * 2.0 * PI;
114     radians += (cast(double)hms.minutes / 60.0) * 2.0 * PI / 24.0;
115     radians += (cast(double)hms.seconds / 60.0) * 2.0 * PI / 1440.0;
116 
117     return radians;
118 }
119 
120 
121 /* convert degrees to hh:mm:ss */
122 @nogc void ln_deg_to_hms (double degrees, ref ln_hms hms) nothrow
123 {
124     double dtemp;
125 
126     degrees = ln_range_degrees(degrees);
127 
128 	/* divide degrees by 15 to get the hours */
129     dtemp = degrees / 15.0;
130     hms.hours = cast(ushort)dtemp;
131 
132     /* multiply remainder by 60 to get minutes */
133     dtemp = 60.0 * (dtemp - hms.hours);
134     hms.minutes = cast(ushort)dtemp;
135 
136     /* multiply remainder by 60 to get seconds */
137     hms.seconds = 60.0 * (dtemp - hms.minutes);
138 
139     /* catch any overflows */
140     if (hms.seconds > 59) {
141     	hms.seconds = 0.0;
142     	hms.minutes ++;
143     }
144     if (hms.minutes > 59) {
145     	hms.minutes = 0;
146     	hms.hours ++;
147     }
148 }
149 
150 /* convert radians to hh:mm:ss */
151 @nogc void ln_rad_to_hms (double radians, ref ln_hms hms) nothrow
152 {
153     double degrees;
154 
155     radians = ln_range_radians(radians);
156     degrees = ln_rad_to_deg(radians);
157 
158     ln_deg_to_hms(degrees, hms);
159 }
160 
161 
162 /* convert dms to degrees */
163 @nogc double ln_dms_to_deg(const ref ln_dms dms) nothrow
164 {
165     double degrees;
166 
167     degrees =  fabs(cast(double)dms.degrees);
168     degrees += fabs(cast(double)dms.minutes / 60.0);
169     degrees += fabs(cast(double)dms.seconds / 3600.0);
170 
171 	// negative ?
172 	if (dms.neg)
173 		degrees *= -1.0;
174 
175     return degrees;
176 }
177 
178 /* convert dms to radians */
179 @nogc double ln_dms_to_rad(const ref ln_dms dms) nothrow
180 {
181     double radians;
182 
183     radians =  fabs(cast(double)dms.degrees / 360.0 * 2.0 * PI);
184     radians += fabs(cast(double)dms.minutes / 21600.0 * 2.0 * PI);
185     radians += fabs(cast(double)dms.seconds / 1296000.0 * 2.0 * PI);
186 
187 	// negative ?
188 	if (dms.neg)
189 		radians *= -1.0;
190 
191     return radians;
192 }
193 
194 /* convert degrees to dms */
195 @nogc void ln_deg_to_dms (double degrees, ref ln_dms dms) nothrow
196 {
197     double dtemp;
198 
199     if (degrees >= 0.0)
200 		dms.neg = 0;
201 	else
202 		dms.neg = 1;
203 
204 	degrees = fabs(degrees);
205 	dms.degrees = cast(ushort)degrees;
206 
207     /* multiply remainder by 60 to get minutes */
208     dtemp = 60.0 * (degrees - dms.degrees);
209     dms.minutes = cast(ushort)dtemp;
210 
211     /* multiply remainder by 60 to get seconds */
212     dms.seconds = 60.0 * (dtemp - dms.minutes);
213 
214     /* catch any overflows */
215     if (dms.seconds > 59) {
216     	dms.seconds = 0.0;
217     	dms.minutes ++;
218     }
219     if (dms.minutes > 59) {
220     	dms.minutes = 0;
221     	dms.degrees ++;
222     }
223 }
224 
225 /* convert radians to dms */
226 @nogc void ln_rad_to_dms (double radians, ref ln_dms dms) nothrow
227 {
228     double degrees = ln_rad_to_deg(radians);
229 
230     ln_deg_to_dms(degrees, dms);
231 }
232 
233 
234 /* puts a large angle in the correct range 0 - 360 degrees */
235 @nogc double ln_range_degrees(double angle) nothrow
236 {
237     double temp;
238 
239     if (angle >= 0.0 && angle < 360.0)
240         return angle;
241 
242     temp = cast(int)(angle / 360);
243     if (angle < 0.0)
244         temp --;
245     temp *= 360.0;
246     return angle - temp;
247 }
248 
249 /* puts a large angle in the correct range 0 - 2PI radians */
250 @nogc double ln_range_radians(double angle) nothrow
251 {
252     double temp;
253 
254     if (angle >= 0.0 && angle < (2.0 * PI))
255         return angle;
256 
257     temp = cast(int)(angle / (PI * 2.0));
258 
259     if (angle < 0.0)
260         temp --;
261     temp *= (PI * 2.0);
262     return angle - temp;
263 }
264 
265 /* puts a large angle in the correct range -2PI - 2PI radians */
266 /* preserve sign */
267 @nogc double ln_range_radians2(double angle) nothrow
268 {
269     double temp;
270 
271     if (angle > (-2.0 * PI) && angle < (2.0 * PI))
272         return angle;
273 
274     temp = cast(int)(angle / (PI * 2.0));
275     temp *= (PI * 2.0);
276     return angle - temp;
277 }
278 
279 
280 /* add seconds to hms */
281 @nogc void ln_add_secs_hms(ref ln_hms hms, double seconds) nothrow
282 {
283     ln_hms source_hms;
284 
285     /* breaks double seconds int hms */
286     source_hms.hours = cast(ushort)(seconds / 3600);
287     seconds -= source_hms.hours * 3600;
288     source_hms.minutes = cast(ushort)(seconds / 60);
289     seconds -= source_hms.minutes * 60;
290     source_hms.seconds = seconds;
291 
292     /* add hms to hms */
293     ln_add_hms(source_hms, hms);
294 }
295 
296 
297 /* add hms to hms */
298 @nogc void ln_add_hms(const ref ln_hms source, ref ln_hms dest) nothrow
299 {
300     dest.seconds += source.seconds;
301     if (dest.seconds >= 60.0) {
302         /* carry */
303 	    dest.minutes ++;
304 	    dest.seconds -= 60.0;
305 	} else {
306 	    if (dest.seconds < 0.0) {
307 	        /* carry */
308 		    dest.minutes --;
309 		    dest.seconds += 60.0;
310 		}
311 	}
312 
313     dest.minutes += source.minutes;
314     if (dest.minutes >= 60) {
315         /* carry */
316 	    dest.hours ++;
317 	    dest.minutes -= 60;
318 	} else {
319 	    if (dest.seconds < 0.0) {
320 	        /* carry */
321 		    dest.hours --;
322 		    dest.minutes += 60;
323 		}
324 	}
325 
326     dest.hours += source.hours;
327 }
328 
329 /*! \fn void ln_hequ_to_equ(struct lnh_equ_posn *hpos, struct ln_equ_posn *pos)
330 * \brief human readable equatorial position to double equatorial position
331 * \ingroup conversion
332 */
333 @nogc void ln_hequ_to_equ(const ref lnh_equ_posn hpos, ref ln_equ_posn pos) nothrow
334 {
335 	pos.ra = ln_hms_to_deg(hpos.ra);
336 	pos.dec = ln_dms_to_deg(hpos.dec);
337 }
338 
339 /*! \fn void ln_equ_to_hequ(struct ln_equ_posn *pos, struct lnh_equ_posn *hpos)
340 * \brief human double equatorial position to human readable equatorial position
341 * \ingroup conversion
342 */
343 @nogc void ln_equ_to_hequ(const ref ln_equ_posn pos, ref lnh_equ_posn hpos) nothrow
344 {
345 	ln_deg_to_hms(pos.ra, hpos.ra);
346 	ln_deg_to_dms(pos.dec, hpos.dec);
347 }
348 
349 /*! \fn void ln_hhrz_to_hrz(struct lnh_hrz_posn *hpos, struct ln_hrz_posn *pos)
350 * \brief human readable horizontal position to double horizontal position
351 * \ingroup conversion
352 */
353 @nogc void ln_hhrz_to_hrz(const ref lnh_hrz_posn hpos, ref ln_hrz_posn pos) nothrow
354 {
355 	pos.alt = ln_dms_to_deg(hpos.alt);
356 	pos.az = ln_dms_to_deg(hpos.az);
357 }
358 
359 /*! \fn void ln_hrz_to_hhrz(struct ln_hrz_posn *pos, struct lnh_hrz_posn *hpos)
360 * \brief double horizontal position to human readable horizontal position
361 * \ingroup conversion
362 */
363 @nogc void ln_hrz_to_hhrz(const ref ln_hrz_posn pos, ref lnh_hrz_posn hpos) nothrow
364 {
365 	ln_deg_to_dms(pos.alt, hpos.alt);
366 	ln_deg_to_dms(pos.az, hpos.az);
367 }
368 
369 /*! \fn const char * ln_hrz_to_nswe(struct ln_hrz_posn *pos);
370  * \brief returns direction of given azimuth - like N,S,W,E,NSW,...
371  * \ingroup conversion
372  */
373 @nogc string ln_hrz_to_nswe(const ref ln_hrz_posn pos) nothrow
374 {
375 	static immutable directions =
376 		["S", "SSW", "SW", "SWW", "W", "NWW", "NW", "NNW",
377 		"N", "NNE", "NE", "NEE", "E", "SEE", "SE", "SSE"];
378 
379 	return directions[cast(int)(pos.az / 22.5)];
380 }
381 
382 /*! \fn void ln_hlnlat_to_lnlat(struct lnh_lnlat_posn *hpos, struct ln_lnlat_posn *pos)
383 * \brief human readable long/lat position to double long/lat position
384 * \ingroup conversion
385 */
386 @nogc void ln_hlnlat_to_lnlat(const ref lnh_lnlat_posn hpos, ref ln_lnlat_posn pos) nothrow
387 {
388 	pos.lng = ln_dms_to_deg(hpos.lng);
389 	pos.lat = ln_dms_to_deg(hpos.lat);
390 }
391 
392 /*! \fn void ln_lnlat_to_hlnlat(struct ln_lnlat_posn *pos, struct lnh_lnlat_posn *hpos)
393 * \brief double long/lat position to human readable long/lat position
394 * \ingroup conversion
395 */
396 @nogc void ln_lnlat_to_hlnlat(const ref ln_lnlat_posn pos, ref lnh_lnlat_posn hpos) nothrow
397 {
398 	ln_deg_to_dms(pos.lng, hpos.lng);
399 	ln_deg_to_dms(pos.lat, hpos.lat);
400 }
401 
402 /*
403 * \fn double ln_get_rect_distance(struct ln_rect_posn *a, struct ln_rect_posn *b)
404 * \param a First rectangular coordinate
405 * \param b Second rectangular coordinate
406 * \return Distance between a and b.
407 *
408 * Calculate the distance between rectangular points a and b.
409 */
410 @nogc double ln_get_rect_distance(const ref ln_rect_posn a, const ref ln_rect_posn b) nothrow
411 {
412 	double x,y,z;
413 
414 	x = a.X - b.X;
415 	y = a.Y - b.Y;
416 	z = a.Z - b.Z;
417 
418 	x *= x;
419 	y *= y;
420 	z *= z;
421 
422 	return sqrt(x + y + z);
423 }
424 
425 /*
426 * \fn double ln_get_light_time (double dist)
427 * \param dist Distance in AU
428 * \return Distance in light days.
429 *
430 * Convert units of AU into light days.
431 */
432 @nogc double ln_get_light_time(double dist) nothrow
433 {
434 	return dist * 0.005775183;
435 }
436 
437 
438 /* local types and macros */
439 // typedef int BOOL;
440 // #define TRUE 1
441 // #define FALSE 0
442 // #define iswhite(c)  ((c)== ' ' || (c)=='\t')
443 
444 /*
445 []------------------------------------------------------------------------[]
446 |  trim() & strip()                                                        |
447 |                                                                          |
448 |  strips trailing whitespaces from buf.                                   |
449 |                                                                          |
450 []------------------------------------------------------------------------[]
451 */
452 /*
453 static char *trim(char *x)
454 {
455     char *y;
456 
457     if(!x)
458         return(x);
459     y = x + strlen(x)-1;
460     while (y >= x && isspace(*y))
461         *y-- = 0; // skip white space
462     return x;
463 }
464 */
465 
466 
467 /*
468 []------------------------------------------------------------------------[]
469 |                                                                          |
470 |   skipwhite()                                                            |
471 |   salta espacios en blanco                                               |
472 |                                                                          |
473 []------------------------------------------------------------------------[]
474 */
475 /*
476 static void skipwhite(char **s)
477 {
478    while (iswhite(**s))
479         (*s)++;
480 }
481 */
482 
483 
484 /*! \fn double ln_get_dec_location(const char * s)
485 * \param s Location string
486 * \return angle in degrees
487 *
488 * Obtains Latitude, Longitude, RA or Declination from a string.
489 *
490 *  If the last char is N/S doesn't accept more than 90 degrees.
491 *  If it is E/W doesn't accept more than 180 degrees.
492 *  If they are hours don't accept more than 24:00
493 *
494 *  Any position can be expressed as follows:
495 *  (please use a 8 bits charset if you want
496 *  to view the degrees separator char '0xba')
497 *
498 *  42.30.35,53
499 *  90º0'0,01 W
500 *  42º30'35.53 N
501 *  42º30'35.53S
502 *  42º30'N
503 *  -42.30.35.53
504 *   42:30:35.53 S
505 *  +42.30.35.53
506 *  +42º30 35,53
507 *   23h36'45,0
508 *
509 *
510 *  42:30:35.53 S = -42º30'35.53"
511 *  + 42 30.35.53 S the same previous position, the plus (+) sign is
512 *  considered like an error, the last 'S' has precedence over the sign
513 *
514 *  90º0'0,01 N ERROR: +- 90º0'00.00" latitude limit
515 *
516 */
517 /*
518 double ln_get_dec_location(const char *s)
519 {
520 	char *ptr, *dec, *hh, *ame, *tok_ptr;
521 	BOOL negative = FALSE;
522 	char delim1[] = " :.,;DdHhMm'\n\t";
523 	char delim2[] = " NSEWnsew\"\n\t";
524 	int dghh = 0, minutes = 0;
525 	double seconds = 0.0, pos;
526 	short count;
527 	enum {
528 	HOURS, DEGREES, LAT, LONG
529 	} type;
530 
531 	if (s == NULL || !*s)
532 	return -0.0;
533 
534 	count = strlen(s) + 1;
535 	if ((ptr = (char *) alloca(count)) == NULL)
536 	return -0.0;
537 
538 	memcpy(ptr, s, count);
539 	trim(ptr);
540 	skipwhite(&ptr);
541 	if (*ptr == '+' || *ptr == '-')
542 	negative = (char) (*ptr++ == '-' ? TRUE : negative);
543 
544 	// the last letter has precedence over the sign
545 	if (strpbrk(ptr,"SsWw") != NULL)
546 	negative = TRUE;
547 
548 	skipwhite(&ptr);
549 	if ((hh = strpbrk(ptr,"Hh")) != NULL && hh < ptr + 3) {
550 	type = HOURS;
551 	if (negative) // if RA no negative numbers
552 		negative = FALSE;
553 	} else if ((ame = strpbrk(ptr,"SsNn")) != NULL) {
554 		type = LAT;
555 		if (ame == ptr) // the North/South found before data
556 			ptr++;
557 		} else
558 			type = DEGREES; // unspecified, the caller must control it
559 	if ((ptr = strtok_r(ptr,delim1, &tok_ptr)) != NULL)
560 		dghh = atoi (ptr);
561 	else
562 		return (-0.0);
563 	if ((ptr = strtok_r(NULL,delim1, &tok_ptr)) != NULL) {
564 		minutes = atoi (ptr);
565 		if (minutes > 59)
566 			return -0.0;
567 	} else
568 		return -0.0;
569 
570 	if ((ptr = strtok_r(NULL,delim2,&tok_ptr)) != NULL) {
571 		if ((dec = strchr(ptr,',')) != NULL)
572 			*dec = '.';
573 		seconds = strtod (ptr, NULL);
574 		if (seconds >= 60.0)
575 			return -0.0;
576 	}
577 
578 	if ((ptr = strtok(NULL," \n\t")) != NULL) {
579 		skipwhite(&ptr);
580 		if (*ptr == 'S' || *ptr == 'W' || *ptr == 's' || *ptr == 'w')
581 			negative = TRUE;
582 	}
583 
584 	pos = dghh + minutes /60.0 + seconds / 3600.0;
585 	if (type == HOURS && pos > 24.0)
586 		return -0.0;
587 	if (type == LAT && pos > 90.0)
588 		return -0.0;
589 	if (negative == TRUE)
590 		pos = 0.0 - pos;
591 
592 	return pos;
593 }
594 */
595 
596 
597 /*! \fn const char * ln_get_humanr_location(double location)
598 * \param location Location angle in degress
599 * \return Angle string
600 *
601 * Obtains a human readable location in the form: ddºmm'ss.ss"
602 * String must be freed after use.
603 */
604 @nogc string ln_get_humanr_location(real location) nothrow
605 {
606     import std.format;
607 
608 	real deg, min, sec;
609 
610 	sec = fabs(60.0 * (modf(location, deg)));
611 	sec = 60.0 * (modf(sec, min));
612 	// TODO // return format("%+dº%d'%.2f\"",cast(int)deg, cast(int) min, sec);
613     return "TODO";
614 }
615 
616 @nogc double ln_interpolate3(double n, double y1, double y2, double y3) nothrow
617 {
618 	double y, a, b, c;
619 
620 	/* equ 3.2 */
621 	a = y2 - y1;
622 	b = y3 - y2;
623 	c = b - a;
624 
625 	/* equ 3.3 */
626 	y = y2 + n / 2.0 * (a + b + n * c);
627 
628 	return y;
629 }
630 
631 
632 /*! \fn double ln_interpolate5 (double n, double y1, double y2, double y3, double y4, double y5)
633 * \return interpolation value
634 * \param n Interpolation factor
635 * \param y1 Argument 1
636 * \param y2 Argument 2
637 * \param y3 Argument 3
638 * \param y4 Argument 4
639 * \param y5 Argument 5
640 *
641 * Calculate an intermediate value of the 5 arguments for the given interpolation
642 * factor.
643 */
644 @nogc double ln_interpolate5(double n, double y1, double y2, double y3,
645 	double y4, double y5) nothrow
646 {
647 	double y, A, B, C, D, E, F, G, H, J, K;
648 	double n2, n3, n4;
649 
650 	/* equ 3.8 */
651 	A = y2 - y1;
652 	B = y3 - y2;
653 	C = y4 - y3;
654 	D = y5 - y4;
655 	E = B - A;
656 	F = C - B;
657 	G = D - C;
658 	H = F - E;
659 	J = G - F;
660 	K = J - H;
661 
662 	y = 0.0;
663 	n2 = n* n;
664 	n3 = n2 * n;
665 	n4 = n3 * n;
666 
667 	y += y3;
668 	y += n * ((B + C ) / 2.0 - (H + J) / 12.0);
669 	y += n2 * (F / 2.0 - K / 24.0);
670 	y += n3 * ((H + J) / 12.0);
671 	y += n4 * (K / 24.0);
672 
673 	return y;
674 }
675 
676 
677 /*! \fn double ln_find_zero(double (*f) (double, double *), double from, double to, double *arg)
678 * \param f Function to find zero (root place)
679 * \param from Lower bound of search interval
680 * \param to Upper bound of search interval
681 * \param arg Pointer to the other parameters of the function f
682 *
683 * Find zero of function f() at given interval by Newton method.
684 *
685 */
686 @nogc alias find_zero_t = double function(double, ref double) nothrow;
687 
688 @nogc double ln_find_zero(find_zero_t func,
689 	double from, double to, ref double arg) nothrow
690 {
691 	double x, x1, x2, f;
692 	int i = 0;
693 
694 	x1 = to;
695 	x = from;
696 
697 	do {
698 		f = func(x1, arg);
699 		x2 = x1 - f * (x1 - x) / (f - func(x, arg));
700 		x = x1;
701 		x1 = x2;
702 	} while ((fabs(x - x1) > 1e-6) && (i++ < 1000));
703 
704 	return x2;
705 }
706 
707 /*! \fn double ln_find_max(double (*f) (double, double *), double from, double to, double *arg)
708 * \param f Function to find maximum
709 * \param from Lower bound of search interval
710 * \param to Upper bound of search interval
711 * \param arg Pointer to the other parameters of the function f
712 *
713 * Find local maximum of function f() at given interval by Golden Section method.
714 *
715 */
716 @nogc double ln_find_max(find_zero_t func,
717 	double from, double to, ref double arg) nothrow
718 {
719 	double	a, b, xl, xu, eps;
720 
721 	a = from;
722 	b = to;
723 	xl = b - (b - a) / GOLDEN;
724 	xu = a + (b - a) / GOLDEN;
725 	eps = fabs(b - a);
726 
727 	do {
728 		if (func(xl, arg) > func(xu, arg)) {
729 			b = xu;
730 			xu = xl;
731 			xl = b - (b - a) / GOLDEN;
732 		} else {
733 			a = xl;
734 			xl = xu;
735 			xu = a + (b - a) / GOLDEN;
736 		}
737 		eps = fabs(b - a);
738 
739 	} while (eps > 1e-6);
740 
741 	return (xu + xl) * 0.5;
742 }
743 
744 }