/*
Copyright (c) 2000-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/

/**	@file dkma.h
	Mathematical operations.
	This module provides mathematical operations doing
	range checks.

	The final argument of dkma_..._ok functions is a pointer to an int
	variable receiving an error code if there were problems in the
	function. The variable is left as is if the function is successfull.

	Functions without ..._ok save error codes into an internal variable
	which can be requested and reset. These functions are not
	thread-safe.
*/

#ifndef DK_MA_INC
#define DK_MA_INC

#include <dk.h>
#include <dktypes.h>

#if defined(EXTERN)
#undef EXTERN
#endif
#ifndef DK_MA_C
#if !DK_HAVE_PROTOTYPES
#define EXTERN extern
#else
#define EXTERN /* nix */
#endif
#else
#define EXTERN /* nix */
#endif

#if defined(__cplusplus)
extern "C" {
#endif

/**	Retrieve last error code, optionally reset.
	Retrieve the error code of the last error from the module-internal
	variable.
	@param	r	Flag to indicate whether or not to reset the
	module-internal variable.
	@return	The error code of the last error occured.
*/
EXTERN int dkma_get_error DK_PR((int r));

/**	Addition of 2 unsigned long values.
	@param	u1	Value 1.
	@param	u2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Summary.
*/
EXTERN unsigned long
dkma_add_ulong_ok DK_PR((unsigned long u1, unsigned long u2, int *ok));

/**	Subtraction of 2 unsigned long values.
	@param	u1	Value 1.
	@param	u2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Difference.
*/
EXTERN unsigned long
dkma_sub_ulong_ok DK_PR((unsigned long u1, unsigned long u2, int *ok));

/**	Multiplication of 2 unsigned long values.
	@param	u1	Value 1.
	@param	u2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Product.
*/
EXTERN unsigned long
dkma_mul_ulong_ok DK_PR((unsigned long u1, unsigned long u2, int *ok));

/**	Division of 2 unsigned long values.
	@param	u1	Value 1.
	@param	u2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Result.
*/
EXTERN unsigned long
dkma_div_ulong_ok DK_PR((unsigned long u1, unsigned long u2, int *ok));

/**	Addition of 2 unsigned long values.
	@param	u1	Value 1.
	@param	u2	Value 2.
	@return	Summary.
*/
EXTERN unsigned long
dkma_add_ulong DK_PR((unsigned long u1, unsigned long u2));

/**	Subtraction of 2 unsigned long values.
	@param	u1	Value 1.
	@param	u2	Value 2.
	@return	Difference.
*/
EXTERN unsigned long
dkma_sub_ulong DK_PR((unsigned long u1, unsigned long u2));

/**	Multiplication of 2 unsigned long values.
	@param	u1	Value 1.
	@param	u2	Value 2.
	@return	Product.
*/
EXTERN unsigned long
dkma_mul_ulong DK_PR((unsigned long u1, unsigned long u2));

/**	Division of 2 unsigned long values.
	@param	u1	Value 1.
	@param	u2	Value 2.
	@return	Result.
*/
EXTERN unsigned long
dkma_div_ulong DK_PR((unsigned long u1, unsigned long u2));

/**	Addition of 2 long values.
	@param	l1	Value 1.
	@param	l2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Summary.
*/
EXTERN long
dkma_add_long_ok DK_PR((long l1, long l2, int *ok));

/**	Subtraction of 2 long values.
	@param	l1	Value 1.
	@param	l2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Difference.
*/
EXTERN long
dkma_sub_long_ok DK_PR((long l1, long l2, int *ok));

/**	Multiplication of 2 long values.
	@param	l1	Value 1.
	@param	l2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Product.
*/
EXTERN long
dkma_mul_long_ok DK_PR((long l1, long l2, int *ok));

/**	Division of 2 long values.
	@param	l1	Value 1.
	@param	l2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Result.
*/
EXTERN long
dkma_div_long_ok DK_PR((long l1, long l2, int *ok));

/**	Addition of 2 long values.
	@param	l1	Value 1.
	@param	l2	Value 2.
	@return	Summary.
*/
EXTERN long
dkma_add_long DK_PR((long l1, long l2));

/**	Subtraction of 2 long values.
	@param	l1	Value 1.
	@param	l2	Value 2.
	@return	Difference.
*/
EXTERN long
dkma_sub_long DK_PR((long l1, long l2));

/**	Multiplication of 2 long values.
	@param	l1	Value 1.
	@param	l2	Value 2.
	@return	Product.
*/
EXTERN long
dkma_mul_long DK_PR((long l1, long l2));

/**	Division of 2 long values.
	@param	l1	Value 1.
	@param	l2	Value 2.
	@return	Result.
*/
EXTERN long
dkma_div_long DK_PR((long l1, long l2));

/**	Addition of 2 double values.
	@param	d1	Value 1.
	@param	d2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Summary.
*/
EXTERN double
dkma_add_double_ok DK_PR((double d1, double d2, int *ok));

/**	Subtraction of 2 double values.
	@param	d1	Value 1.
	@param	d2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Difference.
*/
EXTERN double
dkma_sub_double_ok DK_PR((double d1, double d2, int *ok));

/**	Multiplication of 2 double values.
	@param	d1	Value 1.
	@param	d2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Product.
*/
EXTERN double
dkma_mul_double_ok DK_PR((double d1, double d2, int *ok));

/**	Division of 2 double values.
	@param	d1	Value 1.
	@param	d2	Value 2.
	@param	ok	Pointer to variable to receive the error code (if any).
	@return	Result.
*/
EXTERN double
dkma_div_double_ok DK_PR((double d1, double d2, int *ok));

/**	Addition of 2 double values.
	@param	d1	Value 1.
	@param	d2	Value 2.
	@return	The summary.
*/
EXTERN double
dkma_add_double DK_PR((double d1, double d2));

/**	Substraction of 2 double values.
	@param	d1	Value 1.
	@param	d2	Value 2.
	@return	The difference.
*/
EXTERN double
dkma_sub_double DK_PR((double d1, double d2));

/**	Multiplication of 2 double values.
	@param	d1	Factor 1.
	@param	d2	Factor 2.
	@return	The product.
*/
EXTERN double
dkma_mul_double DK_PR((double d1, double d2));

/**	Division of 2 double values.
	@param	d1	Dividend.
	@param	d2	Divisor.
	@return	The division result.
*/
EXTERN double
dkma_div_double DK_PR((double d1, double d2));

/**	Convert unsigned long to double, save errors.
	@param	ul	The unsigned long value.
	@param  ok	Pointer to a variable to receive the error code.
	@return	The conversion result.
*/
EXTERN double
dkma_ul_to_double_ok DK_PR((unsigned long ul, int *ok));

/**	Convert unsigned long long to double, save errors.
	@param	ul	The unsigned long long value.
	@param	ok	Pointer to a variable to receive the error code.
	@return	The conversion result.
*/
EXTERN double
dkma_ull_to_double_ok DK_PR((dk_long_long_unsigned_t ul, int *ok));

/**	Convert unsigned long to double.
*/
EXTERN double
dkma_ul_to_double DK_PR((unsigned long ul));

/**	Convert unsigned long to double
	@param	ul	The unsigned long value.
	@return	The conversion result.
*/
EXTERN double
dkma_ull_to_double DK_PR((dk_long_long_unsigned_t ul));

/**	Convert long to double, save errors.
	@param	l	The long value.
	@param	ok	Pointer to a variable to receive the error code.
	@return	The conversion result.
*/
EXTERN double
dkma_l_to_double_ok DK_PR((long l, int *ok));

/**	Convert long to double.
	@param	l	The long value.
	@return	The conversion result.
*/
EXTERN double
dkma_l_to_double DK_PR((long l));

/**	Convert double to unsigned long, save range overflow errors.
	@param	d	The double value.
	@param	ok	Pointer to variable to receive the error code.
	@return	The conversion result.
*/
EXTERN unsigned long
dkma_double_to_ul_ok DK_PR((double d, int *ok));

/**	Convert double to unsigned long.
	@param	d	The double value.
	@return	The conversion result.
*/
EXTERN unsigned long
dkma_double_to_ul DK_PR((double d));

/**	Convert double to long, save range overflow errors.
	@param	d	The double value.
	@param	ok	Pointer to a variable to receive the error code.
	@return	The conversion result.
*/
EXTERN long
dkma_double_to_l_ok DK_PR((double d, int *ok));

/**	Convert double to long.
	@param	d	The double value.
	@return	The conversion result.
*/
EXTERN long
dkma_double_to_l DK_PR((double d));

/**	Restrict number of digits after decimal dot.
	@param	x	Original value.
	@param	d	Number of digits after decimal dot.
	@return	The result.
*/
EXTERN double
dkma_double_restrict_digits DK_PR((double x, unsigned d));

/**	Restrict number of digits after decimal dot,
	enforce downward rounding.
	@param	x	Original value.
	@param	d	Number of digits after decimal dot.
	@return	The result.
*/
EXTERN double
dkma_double_restrict_downwards DK_PR((double x, unsigned d));

/**	Add 2 long long unsigned values, save range check result.
	@param	o1	First value.
	@param	o2	Second value.
	@param	ok	Pointer to variable receiving error code if any.
	@return	Summary of \a o1 and \a o2.
*/
EXTERN dk_long_long_unsigned_t
dkma_add_ull_ok DK_PR((dk_long_long_unsigned_t o1, dk_long_long_unsigned_t o2, int *ok));

/**	Add 2 long long unsigned values.
	@param	o1	First value.
	@param	o2	Second value.
	@return	Summary of \a o1 and \a o2.
*/
EXTERN dk_long_long_unsigned_t
dkma_add_ull DK_PR((dk_long_long_unsigned_t o1, dk_long_long_unsigned_t o2));

/**	Substract 2 long long unsigned values, save range check result.
	@param	o1	First value.
	@param	o2	Second value.
	@param	ok	Pointer to variable receiving error code if any.
	@return	Difference of \a o1 and \a o2.
*/
EXTERN dk_long_long_unsigned_t
dkma_sub_ull_ok DK_PR((dk_long_long_unsigned_t o1, dk_long_long_unsigned_t o2, int *ok));

/**	Substract 2 long long unsigned values.
	@param	o1	First value.
	@param	o2	Second value.
	@return	Difference of \a o1 and \a o2.
*/
EXTERN dk_long_long_unsigned_t
dkma_sub_ull DK_PR((dk_long_long_unsigned_t o1, dk_long_long_unsigned_t o2));

/**	Multiplicate 2 long long unsigned values, save range check result.
	@param	o1	First value.
	@param	o2	Second value.
	@param	ok	Pointer to variable receiving error code if any.
	@return	Product of \a o1 and \a o2.
*/
EXTERN dk_long_long_unsigned_t
dkma_mul_ull_ok DK_PR((dk_long_long_unsigned_t o1, dk_long_long_unsigned_t o2, int *ok));

/**	Multiplicate 2 long long unsigned values.
	@param	o1	First value.
	@param	o2	Second value.
	@return	Product of \a o1 and \a o2.
*/
EXTERN dk_long_long_unsigned_t
dkma_mul_ull DK_PR((dk_long_long_unsigned_t o1, dk_long_long_unsigned_t o2));

/**	Divide 2 long long unsigned values, save range check result.
	@param	o1	First value.
	@param	o2	Second value.
	@param	ok	Pointer to variable receiving error code if any.
	@return	Fraction of \a o1 and \a o2.
*/
EXTERN dk_long_long_unsigned_t
dkma_div_ull_ok DK_PR((dk_long_long_unsigned_t o1, dk_long_long_unsigned_t o2, int *ok));

/**	Divide 2 long long unsigned values.
	@param	o1	First value.
	@param	o2	Second value.
	@return	Fraction of \a o1 and \a o2.
*/
EXTERN dk_long_long_unsigned_t
dkma_div_ull DK_PR((dk_long_long_unsigned_t o1, dk_long_long_unsigned_t o2));

/**	Round to nearest integer.
	This is a fallback function for systems having no rint() function.
	@param	d	Original number.
	@return	Number rounded to nearest integer.
*/
EXTERN double
dkma_rint DK_PR((double d));

/**	Calculate atan2(x,y)=atan(y/x).
	This is a fallback for systems having no atan2() function.
	@param	y	Height of the point.
	@param	x	Width of a point.
	@return	Angle between x-axis an line from (0,0) to the point
	in radians.
*/
EXTERN double
dkma_atan2 DK_PR((double y, double x));

/**	Find greatest common divisor for two numbers.
	@param	l1	First operand.
	@param	l2	Second operand.
	@return	Greatest common divisor.
*/
EXTERN dk_long_long_unsigned_t
dkma_ull_gcd DK_PR((dk_long_long_unsigned_t l1, dk_long_long_unsigned_t l2));

/**	Calculate least common multiple of two numbers,
	check for out-of-range result.
	@param	u1	Operand 1.
	@param	u2	Operand 2.
	@param	ec	Pointer to variable to receive error code.
	@return	Least common multiple.
*/
EXTERN dk_long_long_unsigned_t
dkma_ull_lcm_ok DK_PR((dk_long_long_unsigned_t u1, dk_long_long_unsigned_t u2, int *ec));

/**	Calculate least common multiple of two numbers.
	@param	l1	Operand 1.
	@param	l2	Operand 2.
	@return	Least common multiple.
*/
EXTERN dk_long_long_unsigned_t
dkma_ull_lcm DK_PR((dk_long_long_unsigned_t l1, dk_long_long_unsigned_t l2));

/**	Find greatest common divisor for two numbers.
	@param	l1	First operand.
	@param	l2	Second operand.
	@return	Greatest common divisor.
*/
EXTERN unsigned long
dkma_ul_gcd DK_PR((unsigned long l1, unsigned long l2));

/**	Calculate least common multiple of two numbers,
	check for out-of-range result.
	@param	l1	Operand 1.
	@param	l2	Operand 2.
	@param	ec	Pointer to variable to receive error code.
	@return	Least common multiple.
*/
EXTERN unsigned long
dkma_ul_lcm_ok DK_PR((unsigned long l1, unsigned long l2, int *ec));

/**	Calculate least common multiple of two numbers.
	@param	l1	Operand 1.
	@param	l2	Operand 2.
	@return	Least common multiple.
*/
EXTERN unsigned long
dkma_ul_lcm DK_PR((unsigned long l1, unsigned long l2));

/**	Find greatest common divisor for two numbers.
	@param	l1	First operand.
	@param	l2	Second operand.
	@return	Greatest common divisor.
*/
EXTERN long
dkma_l_gcd DK_PR((long l1, long l2));

/**	Calculate least common multiple of two numbers,
	check for out-of-range result.
	@param	l1	Operand 1.
	@param	l2	Operand 2.
	@param	ec	Pointer to variable to receive error code.
	@return	Least common multiple.
*/
EXTERN long
dkma_l_lcm_ok DK_PR((long l1, long l2, int *ec));

/**	Calculate least common multiple of two numbers.
	@param	l1	Operand 1.
	@param	l2	Operand 2.
	@return	Least common multiple.
*/
EXTERN long
dkma_l_lcm DK_PR((long l1, long l2));

/**	Print double to file, avoid exponential notation.
	@param	f	File to print to.
	@param	d	Value to print.
*/
EXTERN void
dkma_fputs_double_no_exp DK_PR((FILE *f, double d));

/**	Print double from text to file, avoid exponent notation.
	This function is called by dkma_fputs_double_no_exp(), it is not
	intended for direct use.
	@param	f	File to print to.
	@param	s	String containing the floating point number.
*/
EXTERN void
dkma_fputs_double_str_no_exp DK_PR((FILE *f, char *s));

#if defined(__cplusplus)
}
#endif

/* Yes, there are systems without M_PI or M_PI_2 in math.h */
#ifndef M_PI
/** Define pi if not already defined in math.h */
#define M_PI            3.14159265358979323846
#endif
#ifndef M_PI_2
/** Define 0.5 * pi if not already defined in math.h */
#define M_PI_2          1.57079632679489661923
#endif

#endif

