/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunPro, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice
 * is preserved.
 * ====================================================
 */

#ifndef __INTERNAL_MATH_H
#define __INTERNAL_MATH_H

#ifndef __UNIXLIB_TYPES_H
#include <unixlib/types.h>
#endif
#include <endian.h>
#include <stdint.h>

__BEGIN_DECLS

/* The original fdlibm code used statements like:
	n0 = ((*(int*)&one)>>29)^1;		* index of high word *
	ix0 = *(n0+(int*)&x);			* high word of x *
	ix1 = *((1-n0)+(int*)&x);		* low word of x *
   to dig two 32 bit words out of the 64 bit IEEE floating point
   value.  That is non-ANSI, and, moreover, the gcc instruction
   scheduler gets it wrong.  We instead use the following macros.
   Unlike the original code, we determine the endianness at compile
   time, not at run time; I don't see much benefit to selecting
   endianness at run time.  */

/* A union which permits us to convert between a double and two 32 bit
   ints.  */

#if __FLOAT_WORD_ORDER == __BIG_ENDIAN

typedef union
{
  double value;
  struct
  {
    __uint32_t msw;
    __uint32_t lsw;
  } parts;
} ieee_double_shape_type;

#endif

#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN

typedef union
{
  double value;
  struct
  {
    __uint32_t lsw;
    __uint32_t msw;
  } parts;
} ieee_double_shape_type;

#endif

/* Get two 32 bit ints from a double.  */

#define EXTRACT_WORDS(ix0,ix1,d)				\
do {								\
  ieee_double_shape_type ew_u;					\
  ew_u.value = (d);						\
  (ix0) = ew_u.parts.msw;					\
  (ix1) = ew_u.parts.lsw;					\
} while (0)

/* Get the more significant 32 bit int from a double.  */

#define GET_HIGH_WORD(i,d)					\
do {								\
  ieee_double_shape_type gh_u;					\
  gh_u.value = (d);						\
  (i) = gh_u.parts.msw;						\
} while (0)

/* Get the less significant 32 bit int from a double.  */

#define GET_LOW_WORD(i,d)					\
do {								\
  ieee_double_shape_type gl_u;					\
  gl_u.value = (d);						\
  (i) = gl_u.parts.lsw;						\
} while (0)

/* Set a double from two 32 bit ints.  */

#define INSERT_WORDS(d,ix0,ix1)					\
do {								\
  ieee_double_shape_type iw_u;					\
  iw_u.parts.msw = (ix0);					\
  iw_u.parts.lsw = (ix1);					\
  (d) = iw_u.value;						\
} while (0)

/* Set the more significant 32 bits of a double from an int.  */

#define SET_HIGH_WORD(d,v)					\
do {								\
  ieee_double_shape_type sh_u;					\
  sh_u.value = (d);						\
  sh_u.parts.msw = (v);						\
  (d) = sh_u.value;						\
} while (0)

/* Set the less significant 32 bits of a double from an int.  */

#define SET_LOW_WORD(d,v)					\
do {								\
  ieee_double_shape_type sl_u;					\
  sl_u.value = (d);						\
  sl_u.parts.lsw = (v);						\
  (d) = sl_u.value;						\
} while (0)

/* A union which permits us to convert between a float and a 32 bit
   int.  */

typedef union
{
  float value;
  __uint32_t word;
} ieee_float_shape_type;

/* Get a 32 bit int from a float.  */

#define GET_FLOAT_WORD(i,d)					\
do {								\
  ieee_float_shape_type gf_u;					\
  gf_u.value = (d);						\
  (i) = gf_u.word;						\
} while (0)

/* Set a float from a 32 bit int.  */

#define SET_FLOAT_WORD(d,i)					\
do {								\
  ieee_float_shape_type sf_u;					\
  sf_u.word = (i);						\
  (d) = sf_u.value;						\
} while (0)

/* A union which permits us to convert between a long double and
   three 32 bit ints.  */

#if __FLOAT_WORD_ORDER == __BIG_ENDIAN

typedef union
{
  long double value;
  struct
  {
    unsigned int exponent:15;
    unsigned int empty:16;
    unsigned int sign:1;
    __uint32_t msw;
    __uint32_t lsw;
  } parts;
} ieee_long_double_shape_type;

#endif

#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN

typedef union
{
  long double value;
  struct
  {
    __uint32_t lsw;
    __uint32_t msw;
    unsigned int exponent:15;
    unsigned int sign:1;
    unsigned int empty:16;
  } parts;
} ieee_long_double_shape_type;

#endif

/* Get three 32 bit ints from a double. Hacked for ARM FPA.  */

#define GET_LDOUBLE_WORDS(exp,ix0,ix1,d)			\
do {								\
  ieee_long_double_shape_type ew_u;				\
  ew_u.value = (d);						\
  (exp) = ew_u.parts.exponent | (ew_u.parts.sign << 16);	\
  (ix0) = ew_u.parts.msw;					\
  (ix1) = ew_u.parts.lsw;					\
} while (0)

/* Set a double from two 32 bit ints. Hacked for ARM FPA.  */

#define SET_LDOUBLE_WORDS(d,exp,ix0,ix1)			\
do {								\
  ieee_long_double_shape_type iw_u;				\
  iw_u.parts.sign = (exp) & (1 << 16);				\
  iw_u.parts.exponent = (exp) & ((1 << 15)-1)			\
  iw_u.parts.msw = (ix0);					\
  iw_u.parts.lsw = (ix1);					\
  (d) = iw_u.value;						\
} while (0)


/* Get the more significant 32 bits of a long double mantissa.  */

#define GET_LDOUBLE_MSW(v,d)					\
do {								\
  ieee_long_double_shape_type sh_u;				\
  sh_u.value = (d);						\
  (v) = sh_u.parts.msw;						\
} while (0)

/* Set the more significant 32 bits of a long double mantissa from an int.  */

#define SET_LDOUBLE_MSW(d,v)					\
do {								\
  ieee_long_double_shape_type sh_u;				\
  sh_u.value = (d);						\
  sh_u.parts.msw = (v);						\
  (d) = sh_u.value;						\
} while (0)

/* Get int from the exponent of a long double.  */

#define GET_LDOUBLE_EXP(exp,d)					\
do {								\
  ieee_long_double_shape_type ge_u;				\
  ge_u.value = (d);						\
  (exp) = ge_u.parts.sign_exponent;				\
} while (0)

/* Set exponent of a long double from an int.  */

#define SET_LDOUBLE_EXP(d,exp)					\
do {								\
  ieee_long_double_shape_type se_u;				\
  se_u.value = (d);						\
  se_u.parts.sign_exponent = (exp);				\
  (d) = se_u.value;						\
} while (0)


/* ieee style elementary functions */
extern double __ieee754_sqrt (double);
extern double __ieee754_acos (double);
extern double __ieee754_acosh (double);
extern double __ieee754_log (double);
extern double __ieee754_atanh (double);
extern double __ieee754_asin (double);
extern double __ieee754_atan2 (double,double);
extern double __ieee754_exp (double);
extern double __ieee754_exp2 (double);
extern double __ieee754_exp10 (double);
extern double __ieee754_cosh (double);
extern double __ieee754_fmod (double,double);
extern double __ieee754_pow (double,double);
extern double __ieee754_lgamma_r (double,int *);
extern double __ieee754_gamma_r (double,int *);
extern double __ieee754_lgamma (double);
extern double __ieee754_gamma (double);
extern double __ieee754_log10 (double);
extern double __ieee754_log2 (double);
extern double __ieee754_sinh (double);
extern double __ieee754_hypot (double,double);
extern double __ieee754_j0 (double);
extern double __ieee754_j1 (double);
extern double __ieee754_y0 (double);
extern double __ieee754_y1 (double);
extern double __ieee754_jn (int,double);
extern double __ieee754_yn (int,double);
extern double __ieee754_remainder (double,double);
extern int32_t __ieee754_rem_pio2 (double,double*);
extern double __ieee754_scalb (double,double);

/* fdlibm kernel function */
extern double __kernel_standard (double,double,int);
extern double __kernel_sin (double,double,int);
extern double __kernel_cos (double,double);
extern double __kernel_tan (double,double,int);
extern int    __kernel_rem_pio2 (double*,double*,int,int,int, const int32_t*);

/* internal functions.  */
extern double __copysign (double x, double __y);


/* ieee style elementary float functions */
extern float __ieee754_sqrtf (float);
extern float __ieee754_acosf (float);
extern float __ieee754_acoshf (float);
extern float __ieee754_logf (float);
extern float __ieee754_atanhf (float);
extern float __ieee754_asinf (float);
extern float __ieee754_atan2f (float,float);
extern float __ieee754_expf (float);
extern float __ieee754_exp2f (float);
extern float __ieee754_exp10f (float);
extern float __ieee754_coshf (float);
extern float __ieee754_fmodf (float,float);
extern float __ieee754_powf (float,float);
extern float __ieee754_lgammaf_r (float,int *);
extern float __ieee754_gammaf_r (float,int *);
extern float __ieee754_lgammaf (float);
extern float __ieee754_gammaf (float);
extern float __ieee754_log10f (float);
extern float __ieee754_log2f (float);
extern float __ieee754_sinhf (float);
extern float __ieee754_hypotf (float,float);
extern float __ieee754_j0f (float);
extern float __ieee754_j1f (float);
extern float __ieee754_y0f (float);
extern float __ieee754_y1f (float);
extern float __ieee754_jnf (int,float);
extern float __ieee754_ynf (int,float);
extern float __ieee754_remainderf (float,float);
extern int32_t __ieee754_rem_pio2f (float,float*);
extern float __ieee754_scalbf (float,float);


/* float versions of fdlibm kernel functions */
extern float __kernel_sinf (float,float,int);
extern float __kernel_cosf (float,float);
extern float __kernel_tanf (float,float,int);
extern int   __kernel_rem_pio2f (float*,float*,int,int,int, const int32_t*);

/* internal functions.  */
extern float __copysignf (float x, float __y);

/* Prototypes for functions of the IBM Accurate Mathematical Library.  */
extern double __exp1 (double __x, double __xx, double __error);
extern double __sin (double __x);
extern double __cos (double __x);
extern int __branred (double __x, double *__a, double *__aa);
extern void __doasin (double __x, double __dx, double __v[]);
extern void __dubsin (double __x, double __dx, double __v[]);
extern void __dubcos (double __x, double __dx, double __v[]);
extern double __halfulp (double __x, double __y);
extern double __sin32 (double __x, double __res, double __res1);
extern double __cos32 (double __x, double __res, double __res1);
extern double __mpsin (double __x, double __dx);
extern double __mpcos (double __x, double __dx);
extern double __mpsin1 (double __x);
extern double __mpcos1 (double __x);
extern double __slowexp (double __x);
extern double __slowpow (double __x, double __y, double __z);
extern void __docos (double __x, double __dx, double __v[]);

#if defined (__CC_NORCROFT)
/* The Norcroft compiler doesn't support the idea of symbol aliasing,
   or at least allowing us to pass such aliases to the assembler.  */

#define __acos acos
#define __asin asin
#define __atan atan
#define __atan2 atan2
#define __cos cos
#define __sin sin
#define __tan tan
#define __cosh cosh
#define __sinh sinh
#define __tanh tanh
#define __sincos sincos
#define __acosh acosh
#define __asinh asinh
#define __atanh atanh
#define __exp exp
#define __frexp frexp
#define __ldexp ldexp
#define __log log
#define __log10 log10
#define __modf modf
#define __exp10 exp10
#define __pow10 pow10
#define __expm1 expm1
#define __log1p log1p
#define __logb logb
#define __exp2 exp2
#define __log2 log2
#define __pow pow
#define __sqrt sqrt
#define __hypot hypot
#define __cbrt cbrt
#define __ceil ceil
#define __fabs fabs
#define __floor floor
#define __fmod fmod
#define __isinf isinf
#define __finite finite
#define __drem drem
#define __significand significand
#define __copysign copysign
#define __nan nan
#define __isnan isnan
#define __j0 j0
#define __j1 j1
#define __jn jn
#define __y0 y0
#define __y1 y1
#define __yn yn
#define __erf erf
#define __erfc erfc
#define __lgamma lgamma
#define __tgamma tgamma
#define __gamma gamma
#define __lgamma_r lgamma_r
#define __rint rint
#define __nextafter nextafter
#define __nexttoward nexttoward
#define __remainder remainder
#define __scalbn scalbn
#define __ilogb ilogb
#define __scalbln scalbln
#define __nearbyint nearbyint
#define __round round
#define __trunc trunc
#define __remquo remquo
#define __lrint lrint
#define __lround lround
#define __fdim fdim
#define __fmax fmax
#define __fmin fmin
#define __fma fma
#define __scalb scalb

#define __acosf acosf
#define __asinf asinf
#define __atanf atanf
#define __atan2f atan2f
#define __cosf cosf
#define __sinf sinf
#define __tanf tanf
#define __coshf coshf
#define __sinhf sinhf
#define __tanhf tanhf
#define __sincosf sincosf
#define __acoshf acoshf
#define __asinhf asinhf
#define __atanhf atanhf
#define __expf expf
#define __frexpf frexpf
#define __ldexpf ldexpf
#define __logf logf
#define __log10f log10f
#define __modff modff
#define __exp10f exp10f
#define __pow10f pow10f
#define __expm1f expm1f
#define __log1pf log1pf
#define __logbf logbf
#define __exp2f exp2f
#define __log2f log2f
#define __powf powf
#define __sqrtf sqrtf
#define __hypotf hypotf
#define __cbrtf cbrtf
#define __ceilf ceilf
#define __fabsf fabsf
#define __floorf floorf
#define __fmodf fmodf
#define __isinff isinff
#define __finitef finitef
#define __dremf dremf
#define __significandf significandf
#define __copysignf copysignf
#define __nanf nanf
#define __isnanf isnanf
#define __j0f j0f
#define __j1f j1f
#define __jnf jnf
#define __y0f y0f
#define __y1f y1f
#define __ynf ynf
#define __erff erff
#define __erfcf erfcf
#define __lgammaf lgammaf
#define __tgammaf tgammaf
#define __gammaf gammaf
#define __lgammaf_r lgammaf_r
#define __rintf rintf
#define __nextafterf nextafterf
#define __nexttowardf nexttowardf
#define __remainderf remainderf
#define __scalbnf scalbnf
#define __ilogbf ilogbf
#define __scalblnf scalblnf
#define __nearbyintf nearbyintf
#define __roundf roundf
#define __truncf truncf
#define __remquof remquof
#define __lrintf lrintf
#define __llrintf llrintf
#define __lroundf lroundf
#define __llroundf llroundf
#define __fdimf fdimf
#define __fmaxf fmaxf
#define __fminf fminf
#define __fmaf fmaf


#define __cacos cacos
#define __casin casin
#define __catan catan
#define __ccos ccos
#define __csin csin
#define __ctan ctan
#define __cacosh cacosh
#define __casinh casinh
#define __catanh catanh
#define __ccosh ccosh
#define __csinh csinh
#define __ctanh ctanh
#define __cexp cexp
#define __clog clog
#define __clog10 clog10
#define __cpow cpow
#define __csqrt csqrt
#define __cabs cabs
#define __carg carg
#define __conj conj
#define __cproj cproj
#define __cimag cimag
#define __creal creal

#define __matherr matherr

#undef isnan

#endif /* __CC_NORCROFT */

__END_DECLS

#endif
