/**************************************************************************/
/*                                                                        */
/*  This file is part of Frama-C.                                         */
/*                                                                        */
/*  Copyright (C) 2007-2020                                               */
/*    CEA (Commissariat à l'énergie atomique et aux énergies              */
/*         alternatives)                                                  */
/*                                                                        */
/*  you can redistribute it and/or modify it under the terms of the GNU   */
/*  Lesser General Public License as published by the Free Software       */
/*  Foundation, version 2.1.                                              */
/*                                                                        */
/*  It is distributed in the hope that it will be useful,                 */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of        */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         */
/*  GNU Lesser General Public License for more details.                   */
/*                                                                        */
/*  See the GNU Lesser General Public License version 2.1                 */
/*  for more details (enclosed in the file licenses/LGPLv2.1).            */
/*                                                                        */
/**************************************************************************/

/* ISO C: 7.12 */
#ifndef __FC_MATH
#define __FC_MATH
#include "features.h"
__PUSH_FC_STDLIB

#include "__fc_string_axiomatic.h"
#include "errno.h"

__BEGIN_DECLS

typedef float float_t;
typedef double double_t;

#define MATH_ERRNO	1
#define MATH_ERREXCEPT	2

/* The constants below are not part of C99/C11 but they are defined in POSIX */
#define M_E 0x1.5bf0a8b145769p1         /* e          */
#define M_LOG2E 0x1.71547652b82fep0     /* log_2 e    */
#define M_LOG10E 0x1.bcb7b1526e50ep-2   /* log_10 e   */
#define M_LN2 0x1.62e42fefa39efp-1      /* log_e 2    */
#define M_LN10 0x1.26bb1bbb55516p1      /* log_e 10   */
#define M_PI 0x1.921fb54442d18p1        /* pi         */
#define M_PI_2 0x1.921fb54442d18p0      /* pi/2       */
#define M_PI_4 0x1.921fb54442d18p-1     /* pi/4       */
#define M_1_PI 0x1.45f306dc9c883p-2     /* 1/pi       */
#define M_2_PI 0x1.45f306dc9c883p-1     /* 2/pi       */
#define M_2_SQRTPI 0x1.20dd750429b6dp0  /* 2/sqrt(pi) */
#define M_SQRT2 0x1.6a09e667f3bcdp0     /* sqrt(2)    */
#define M_SQRT1_2 0x1.6a09e667f3bcdp-1  /* 1/sqrt(2)  */

/* The following specifications will set errno. */
#define math_errhandling	MATH_ERRNO

#define FP_NAN 0
#define FP_INFINITE 1
#define FP_ZERO 2
#define FP_SUBNORMAL 3
#define FP_NORMAL 4

#define FP_ILOGB0 __FC_INT_MIN
#define FP_ILOGBNAN __FC_INT_MIN

#include "float.h" // for DBL_MIN and FLT_MIN

/*@
  assigns \result \from x;
  behavior nan:
    assumes is_nan: \is_NaN(x);
    ensures fp_nan: \result == FP_NAN;
  behavior inf:
    assumes is_infinite: !\is_NaN(x) && !\is_finite(x);
    ensures fp_infinite: \result == FP_INFINITE;
  behavior zero:
    assumes is_a_zero: x == 0.0; // also includes -0.0
    ensures fp_zero: \result == FP_ZERO;
  behavior subnormal:
    assumes is_finite: \is_finite(x);
    assumes is_subnormal: (x > 0.0 && x < FLT_MIN) || (x < 0.0 && x > -FLT_MIN);
    ensures fp_subnormal: \result == FP_SUBNORMAL;
  behavior normal:
    assumes is_finite: \is_finite(x);
    assumes not_subnormal: (x <= -FLT_MIN || x >= FLT_MIN);
    ensures fp_normal: \result == FP_NORMAL;
  complete behaviors;
  disjoint behaviors;
 */
int __fc_fpclassifyf(float x);

/*@
  assigns \result \from x;
  behavior nan:
    assumes is_nan: \is_NaN(x);
    ensures fp_nan: \result == FP_NAN;
  behavior inf:
    assumes is_infinite: !\is_NaN(x) && !\is_finite(x);
    ensures fp_infinite: \result == FP_INFINITE;
  behavior zero:
    assumes is_a_zero: x == 0.0; // also includes -0.0
    ensures fp_zero: \result == FP_ZERO;
  behavior subnormal:
    assumes is_finite: \is_finite(x);
    assumes is_subnormal: (x > 0.0 && x < DBL_MIN) || (x < 0.0 && x > -DBL_MIN);
    ensures fp_subnormal: \result == FP_SUBNORMAL;
  behavior normal:
    assumes is_finite: \is_finite(x);
    assumes not_subnormal: (x <= -DBL_MIN || x >= DBL_MIN);
    ensures fp_normal: \result == FP_NORMAL;
  complete behaviors;
  disjoint behaviors;
 */
int __fc_fpclassify(double x);

// Incorrect in presence of long double with >64 bits
#define fpclassify(x) \
  (sizeof(x) == sizeof(float) ? __fc_fpclassifyf(x) : __fc_fpclassify(x))

#define isinf(x) \
  (sizeof(x) == sizeof(float) ? __fc_fpclassifyf(x) == FP_INFINITE : __fc_fpclassify(x) == FP_INFINITE)

#define isnan(x) \
  (sizeof(x) == sizeof(float) ? __fc_fpclassifyf(x) == FP_NAN : __fc_fpclassify(x) == FP_NAN)

#define isnormal(x) \
  (sizeof(x) == sizeof(float) ? __fc_fpclassifyf(x) == FP_NORMAL : __fc_fpclassify(x) == FP_NORMAL)


/*@ requires in_domain: \is_finite(x) && \abs(x) <= 1;
    assigns \result \from x;
    ensures positive_result: \is_finite(\result) && \result >= 0;
*/
extern double acos(double x);

/*@ requires in_domain: \is_finite(x) && \abs(x) <= 1;
    assigns \result \from x;
    ensures positive_result: \is_finite(\result) && \result >= 0;
*/
extern float acosf(float x);

/*@ requires in_domain: \is_finite(x) && \abs(x) <= 1;
    assigns \result \from x;
    ensures positive_result: \is_finite(\result) && \result >= 0;
*/
extern long double acosl(long double x);

/*@ requires in_domain: \is_finite(x) && \abs(x) <= 1;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern double asin(double x);

/*@ requires in_domain: \is_finite(x) && \abs(x) <= 1;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern float asinf(float x);

/*@ requires in_domain: \is_finite(x) && \abs(x) <= 1;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern long double asinl(long double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures result_domain: -1.571 <= \result <= 1.571;
*/
extern float atanf(float x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures result_domain: -1.571 <= \result <= 1.571;
*/
extern double atan(double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures result_domain: -1.571 <= \result <= 1.571;
*/
extern long double atanl(long double x);

/*@ requires finite_args: \is_finite(x) && \is_finite(y);
    requires finite_result: \is_finite(atan2(x, y));
    assigns \result \from x, y;
    ensures finite_result: \is_finite(\result);
*/
extern double atan2(double y, double x);

/*@ requires finite_args: \is_finite(x) && \is_finite(y);
    requires finite_logic_result: \is_finite(atan2f(x, y));
    assigns \result \from x, y;
    ensures finite_result: \is_finite(\result);
*/
extern float atan2f(float y, float x);

extern long double atan2l(long double y, long double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures result_domain: -1. <= \result <= 1.;
*/
extern double cos(double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures result_domain: -1. <= \result <= 1.;
*/
extern float cosf(float x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures result_domain: -1. <= \result <= 1.;
*/
extern long double cosl(long double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures result_domain: -1. <= \result <= 1.;
*/
extern double sin(double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures result_domain: -1. <= \result <= 1.;
*/
extern float sinf(float x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures result_domain: -1. <= \result <= 1.;
*/
extern long double sinl(long double x);

extern double tan(double x);
extern float tanf(float x);
extern long double tanl(long double x);

/*@
  assigns __fc_errno, \result \from x;
  behavior normal:
    assumes in_domain: \is_finite(x) && x >= 1;
    assigns \result \from x;
    ensures positive_result: \is_finite(\result) && \result >= 0;
  behavior infinite:
    assumes is_plus_infinity: \is_plus_infinity(x);
    assigns \result \from x;
    ensures result_plus_infinity: \is_plus_infinity(\result);
  behavior domain_error:
    assumes out_of_domain: \is_minus_infinity(x) || (\is_finite(x) && x < 1);
    assigns __fc_errno, \result \from x;
    ensures errno_set: __fc_errno == 1;
  disjoint behaviors;
 */
extern double acosh(double x);

/*@
  assigns __fc_errno, \result \from x;
  behavior normal:
    assumes in_domain: \is_finite(x) && x >= 1;
    assigns \result \from x;
    ensures positive_result: \is_finite(\result) && \result >= 0;
  behavior infinite:
    assumes is_plus_infinity: \is_plus_infinity(x);
    assigns \result \from x;
    ensures result_plus_infinity: \is_plus_infinity(\result);
  behavior domain_error:
    assumes out_of_domain: \is_minus_infinity(x) || (\is_finite(x) && x < 1);
    assigns __fc_errno, \result \from x;
    ensures errno_set: __fc_errno == 1;
  disjoint behaviors;
 */
extern float acoshf(float x);

/*@
  assigns __fc_errno, \result \from x;
  behavior normal:
    assumes in_domain: \is_finite(x) && x >= 1;
    assigns \result \from x;
    ensures positive_result: \is_finite(\result) && \result >= 0;
  behavior infinite:
    assumes is_plus_infinity: \is_plus_infinity(x);
    assigns \result \from x;
    ensures result_plus_infinity: \is_plus_infinity(\result);
  behavior domain_error:
    assumes out_of_domain: \is_minus_infinity(x) || (\is_finite(x) && x < 1);
    assigns __fc_errno, \result \from x;
    ensures errno_set: __fc_errno == 1;
  disjoint behaviors;
 */
extern long double acoshl(long double x);

extern double asinh(double x);
extern float asinhf(float x);
extern long double asinhl(long double x);

extern double atanh(double x);
extern float atanhf(float x);
extern long double atanhl(long double x);

extern double cosh(double x);
extern float coshf(float x);
extern long double coshl(long double x);

extern double sinh(double x);
extern float sinhf(float x);
extern long double sinhl(long double x);

extern double tanh(double x);
extern float tanhf(float x);
extern long double tanhl(long double x);

/*@ requires finite_arg: \is_finite(x);
    requires finite_domain: x <= 0x1.62e42fefa39efp+9;
    assigns \result \from x;
    ensures res_finite: \is_finite(\result);
    ensures positive_result: \result > 0.;
*/
extern double exp(double x);

/*@ requires finite_arg: \is_finite(x);
    requires res_finite: x <= 0x1.62e42ep+6;
    assigns \result \from x;
    ensures res_finite: \is_finite(\result);
    ensures positive_result: \result > 0.;
*/
extern float expf(float x);

extern long double expl(long double x);

extern double exp2(double x);
extern float exp2f(float x);
extern long double exp2l(long double x);

extern double expm1(double x);
extern float expm1f(float x);
extern long double expm1l(long double x);

extern double frexp(double value, int *exp);
extern float frexpf(float value, int *exp);
extern long double frexpl(long double value, int *exp);

extern int ilogb(double x);
extern int ilogbf(float x);
extern int ilogbl(long double x);

extern double ldexp(double x, int exp);
extern float ldexpf(float x, int exp);
extern long double ldexpl(long double x, int exp);

/*@ requires finite_arg: \is_finite(x);
    requires arg_positive: x > 0;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern double log(double x);

/*@ requires finite_arg: \is_finite(x);
    requires arg_positive: x > 0;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern float logf(float x);

/*@ requires finite_arg: \is_finite(x);
    requires arg_pos: x > 0;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern long double logl(long double x);

/*@ requires finite_arg: \is_finite(x);
    requires arg_positive: x > 0;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern double log10(double x);

/*@ requires finite_arg: \is_finite(x);
    requires arg_positive: x > 0;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern float log10f(float x);

/*@ requires finite_arg: \is_finite(x);
    requires arg_postive: x > 0;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern long double log10l(long double x);

extern double log1p(double x);
extern float log1pf(float x);
extern long double log1pl(long double x);

/*@ requires finite_arg: \is_finite(x);
    requires arg_positive: x > 0;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern double log2(double x);

/*@ requires finite_arg: \is_finite(x);
    requires arg_positive: x > 0;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern float log2f(float x);

/*@ requires finite_arg: \is_finite(x);
    requires arg_positive: x > 0;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern long double log2l(long double x);

extern double logb(double x);
extern float logbf(float x);
extern long double logbl(long double x);

extern double modf(double value, double *iptr);
extern float modff(float value, float *iptr);
extern long double modfl(long double value, long double *iptr);

extern double scalbn(double x, int n);
extern float scalbnf(float x, int n);
extern long double scalbnl(long double x, int n);

extern double scalbln(double x, long int n);
extern float scalblnf(float x, long int n);
extern long double scalblnl(long double x, long int n);

extern double cbrt(double x);
extern float cbrtf(float x);
extern long double cbrtl(long double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures res_finite: \is_finite(\result);
    ensures positive_result: \result >= 0.;
    ensures equal_magnitude_result: \result == x || \result == -x;
*/
extern double fabs(double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures res_finite: \is_finite(\result);
    ensures positive_result: \result >= 0.;
    ensures equal_magnitude_result: \result == x || \result == -x;
*/
extern float fabsf(float x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures res_finite: \is_finite(\result);
    ensures positive_result: \result >= 0.;
    ensures equal_magnitude_result: \result == x || \result == -x;
*/
extern long double fabsl(long double x);

extern double hypot(double x, double y);
extern float hypotf(float x, float y);
extern long double hypotl(long double x, long double y);

/*@ requires finite_args: \is_finite(x) && \is_finite(y);
    requires finite_logic_res: \is_finite(pow(x, y));
    assigns \result \from x, y;
    ensures finite_result: \is_finite(\result);
*/
extern double pow(double x, double y);

/*@ requires finite_args: \is_finite(x) && \is_finite(y);
    requires finite_logic_res: \is_finite(powf(x, y));
    assigns \result \from x, y;
    ensures finite_result: \is_finite(\result);
*/
extern float powf(float x, float y);

extern long double powl(long double x, long double y);

/*@ requires finite_arg: \is_finite(x);
    requires arg_positive: x >= -0.;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures positive_result: \result >= -0.;
    ensures result_value: \result == sqrt(x);
*/
extern double sqrt(double x);

/*@ requires finite_arg: \is_finite(x);
    requires arg_positive: x >= -0.;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures positive_result: \result >= -0.;
    ensures result_value: \result == sqrtf(x);
*/
extern float sqrtf(float x);

/*@ requires finite_arg: \is_finite(x);
    requires arg_positive: x >= -0.;
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
    ensures positive_result: \result >= -0.;
*/
extern long double sqrtl(long double x);

extern double erf(double x);
extern float erff(float x);
extern long double erfl(long double x);

extern double erfc(double x);
extern float erfcf(float x);
extern long double erfcl(long double x);

extern double lgamma(double x);
extern float lgammaf(float x);
extern long double lgammal(long double x);

extern double tgamma(double x);
extern float tgammaf(float x);
extern long double tgammal(long double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern double ceil(double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/

extern float ceilf(float x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern long double ceill(long double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern double floor(double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern float floorf(float x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern long double floorl(long double x);

extern double nearbyint(double x);
extern float nearbyintf(float x);
extern long double nearbyintl(long double x);

extern double rint(double x);
extern float rintf(float x);
extern long double rintl(long double x);

extern long int lrint(double x);
extern long int lrintf(float x);
extern long int lrintl(long double x);

extern long long int llrint(double x);
extern long long int llrintf(float x);
extern long long int llrintl(long double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern double round(double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern float roundf(float x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern long double roundl(long double x);

extern long int lround(double x);
extern long int lroundf(float x);
extern long int lroundl(long double x);

extern long long int llround(double x);
extern long long int llroundf(float x);
extern long long int llroundl(long double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern double trunc(double x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern float truncf(float x);

/*@ requires finite_arg: \is_finite(x);
    assigns \result \from x;
    ensures finite_result: \is_finite(\result);
*/
extern long double truncl(long double x);

/*@ requires finite_args: \is_finite(x) && \is_finite(y);
    requires finite_logic_result: \is_finite(fmod(x, y));
    assigns \result \from x, y;
    ensures finite_result: \is_finite(\result);
*/
extern double fmod(double x, double y);

/*@ requires finite_args: \is_finite(x) && \is_finite(y);
    requires finite_logic_result: \is_finite(fmodf(x, y));
    assigns \result \from x, y;
    ensures finite_result: \is_finite(\result);
*/
extern float fmodf(float x, float y);

extern long double fmodl(long double x, long double y);

extern double remainder(double x, double y);
extern float remainderf(float x, float y);
extern long double remainderl(long double x, long double y);

extern double remquo(double x, double y, int *quo);
extern float remquof(float x, float y, int *quo);
extern long double remquol(long double x, long double y, int *quo);

extern double copysign(double x, double y);
extern float copysignf(float x, float y);
extern long double copysignl(long double x, long double y);

/*@
  requires tagp_valid_string: valid_read_string(tagp);
  assigns \result \from indirect:tagp[0..];
  ensures result_is_nan: \is_NaN(\result);
 */
extern double nan(const char *tagp);

/*@
  requires tagp_valid_string: valid_read_string(tagp);
  assigns \result \from indirect:tagp[0..];
  ensures result_is_nan: \is_NaN(\result);
 */
extern float nanf(const char *tagp);

/*@
  requires tagp_valid_string: valid_read_string(tagp);
  assigns \result \from indirect:tagp[0..];
  ensures result_is_nan: \is_NaN(\result);
 */
extern long double nanl(const char *tagp);

extern double nextafter(double x, double y);
extern float nextafterf(float x, float y);
extern long double nextafterl(long double x, long double y);

extern double nexttoward(double x, long double y);
extern float nexttowardf(float x, long double y);
extern long double nexttowardl(long double x, long double y);

extern double fdim(double x, double y);
extern float fdimf(float x, float y);
extern long double fdiml(long double x, long double y);

extern double fmax(double x, double y);
extern float fmaxf(float x, float y);
extern long double fmaxl(long double x, long double y);

extern double fmin(double x, double y);
extern float fminf(float x, float y);
extern long double fminl(long double x, long double y);

extern double fma(double x, double y, double z);
extern float fmaf(float x, float y, float z);
extern long double fmal(long double x, long double y, long double z);

extern int __finitef(float f);

extern int __finite(double d);

#  define isfinite(x) \
     (sizeof (x) == sizeof (float) ? __finitef (x) : __finite (x))

//The (integer x) argument is just here because a function without argument is
//applied differently in ACSL and C

/*@

  logic float __fc_infinity(integer x) = \plus_infinity;
  logic float __fc_nan(integer x) = \NaN;

  @*/

/*@
  ensures result_is_infinity: \is_plus_infinity(\result);
  assigns \result \from \nothing;
  @*/
extern const float __fc_infinity(int x);

/*@
  ensures result_is_nan: \is_NaN(\result);
  assigns \result \from \nothing;
  @*/
extern const float __fc_nan(int x);


#define INFINITY __fc_infinity(0)
#define NAN __fc_nan(0)

#define HUGE_VALF INFINITY
#define HUGE_VAL  ((double)INFINITY)
#define HUGE_VALL ((long double)INFINITY)


__END_DECLS

__POP_FC_STDLIB
#endif
