/*************************************************************************
*									 *
*	 YAP Prolog 							 *
*									 *
*	Yap Prolog was developed at NCCUP - Universidade do Porto	 *
*									 *
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997	 *
*									 *
**************************************************************************
*									 *
* File:		eval.c							 *
* Last rev:								 *
* mods:									 *
* comments:	arithmetical expression evaluation			 *
*									 *
*************************************************************************/
#ifdef SCCS
static char     SccsId[] = "%W% %G%";
#endif

/**
   @file arith0.c

   @defgroup arithmetic_operators Arithmetic Functions
   @ingroup arithmetic

   YAP implements several arithmetic functions, they are defined as
   fields in three enumerations, such that there is one enumeration
   per each different arity:  

    - #arith0_op defines constants and arity 0 arithmetic functions

      @copydoc #arith0_op

    - #arith1_op defines single argument arithmetic functions

      @copydoc #arith1_op

    - #arith2_op defines binary arithmetic functions

      @copydoc #arith2_op

  Arithmetic expressions
  in YAP may use the following operators:

   - <b>pi [ISO]</b><p> @anchor pi_0

     An approximation to the value of <em>pi</em>, that is, the ratio of a circle's circumference to its diameter.

   - <b>e</b><p>    @anchor e_0

     Euler's number, the base of the natural logarithms.

   - <b>epsilon</b><p>  @anchor epsilon_0

     The difference between the float `1.0` and the next largest floating point number.

   - <b>inf</b><p>  @anchor inf_0

     Infinity according to the IEEE Floating-Point standard. Note that evaluating this term will generate a domain error in the `iso` language mode.

     Note also that YAP supports `+inf` and  `-inf`

   - <b>nan (not a number)</b><p>  @anchor nan_0

     Not-a-number according to the IEEE Floating-Point standard. Note that evaluating this term will generate a domain error in the `iso` language mode.

   - <b>random</b><p>  @anchor random_0

     A "random" floating point number between 0 and 1.

   - <b>cputime</b><p>  @anchor cputime_0

     CPU time since YAP was invoked, in seconds.

   - <b>heapused</b><p>  @anchor heapused_0

     Heap (data-base) space used, in bytes.

   - <b>local</b><p>  @anchor local_0

     Local stack in use, in bytes

   - <b>$b</b><p>  @anchor b_0

     current choicepoint

   - <b>$env</b><p>  @anchor env_0

     Environment

   - <b>$tr</b><p>  @anchor tr_0

     Trail in use

   - <b>$free_stack</b><p>  @anchor free_stack_0
     
     Amount of free stack space, that is, free space between global and local stacks.

   - <b>global</b><p>  @anchor global_0

     Global stack in use, in bytes.
 *
 */

#include "Yap.h"
#include "Yatom.h"
#include "YapHeap.h"
#include "eval.h"


#ifndef PI
#ifdef M_PI
#define PI M_PI
#else
#define PI 3.14159265358979323846
#endif
#endif

#ifndef M_E
#define M_E 2.7182818284590452354
#endif

#ifndef INFINITY
#define INFINITY (1.0/0.0)
#endif

#ifndef NAN
#define NAN      (0.0/0.0)
#endif

/* copied from SWI-Prolog */
#ifndef DBL_EPSILON /* normal for IEEE 64-bit double */
#define DBL_EPSILON 0.00000000000000022204
#endif

static Term
eval0(Int fi) {
  CACHE_REGS
  arith0_op fop = fi;
  switch (fop) {
  case op_pi:
    {
      RFLOAT(PI);
    }
  case op_e:
    {
      RFLOAT(M_E);
    }
  case op_epsilon:
    {
      RFLOAT(DBL_EPSILON);
    }
  case op_inf:
    {
#ifdef _MSC_VER /* Microsoft's Visual C++ Compiler */
      Yap_ArithError(TYPE_ERROR_EVALUABLE, TermNil, "evaluating infinity");
      P = (yamop *)FAILCODE;
      RERROR();
#else
      if (yap_flags[LANGUAGE_MODE_FLAG] == 1) {/* iso */
	Yap_ArithError(TYPE_ERROR_EVALUABLE, TermNil, "evaluating infinity");
	P = (yamop *)FAILCODE;
	RERROR();
      } else {
	RFLOAT(INFINITY);
      }
#endif
    }
  case op_nan:
    {
#ifdef _MSC_VER /* Microsoft's Visual C++ Compi<ler */
      Yap_ArithError(TYPE_ERROR_EVALUABLE, TermNil, "evaluating infinity");
      RERROR();
#else
      if (yap_flags[LANGUAGE_MODE_FLAG] == 1) {/* iso */
	Yap_ArithError(TYPE_ERROR_EVALUABLE, TermNil, "evaluating not-a-number");
	RERROR();
      } else {
	RFLOAT(NAN);
      }
#endif
    }
  case op_random:
    {
      RFLOAT(Yap_random());
    }
  case op_cputime:
    {
      RFLOAT((Float)Yap_cputime()/1000.0);
    }
  case op_heapused:
    /// - heapused
    ///   Heap (data-base) space used, in bytes.
    ///
    RINT(HeapUsed);
  case op_localsp:
    /// - local
    ///   Local stack in use, in bytes
    ///
#if YAPOR_SBA
    RINT((Int)ASP);
#else
    RINT(LCL0 - ASP);
#endif
  case op_b:
    /// - $b
    ///   current choicepoint
    ///
#if YAPOR_SBA
    RINT((Int)B);
#else
    RINT(LCL0 - (CELL *)B);
#endif
  case op_env:
    /// - $env
    ///   Environment
    ///
#if YAPOR_SBA
    RINT((Int)YENV);
#else
    RINT(LCL0 - YENV);
#endif
  case op_tr:
    /// - $tr
    ///   Trail in use
    ///
#if YAPOR_SBA
    RINT(TR);
#else
    RINT(((CELL *)TR)-LCL0);
#endif
  case op_stackfree:
    /// - $free_stack
    ///   
    /// Not-a-number according to the IEEE Floating-Point standard. Note that evaluating this term will generate a domain error in the `iso` language mode.
    RINT(Unsigned(ASP) - Unsigned(HR));
  case op_globalsp:
    /// - global
    ///   Global stack in use, in bytes.
    ///
#if YAPOR_SBA
    RINT((Int)HR);
#else
    RINT(HR - H0);
#endif
  }
  /// end of switch
  RERROR();
}

Term Yap_eval_atom(Int f)
{
  return eval0(f);
}

typedef struct init_const_eval {
  char          *OpName;
  arith0_op      f;
} InitConstEntry;

static InitConstEntry InitConstTab[] = {
  {"pi", op_pi},
  {"e", op_e},
  {"epsilon", op_epsilon},
  {"inf", op_inf},
  {"nan", op_nan},
  {"random", op_random},
  {"cputime", op_cputime},
  {"heapused", op_heapused},
  {"local_sp", op_localsp},
  {"global_sp", op_globalsp},
  {"$last_choice_pt", op_b},
  {"$env", op_env},
  {"$tr", op_tr},
  {"stackfree", op_stackfree},
};

void
Yap_InitConstExps(void)
{
  unsigned int    i;
  ExpEntry       *p;

  for (i = 0; i < sizeof(InitConstTab)/sizeof(InitConstEntry); ++i) {
    AtomEntry *ae = RepAtom(Yap_LookupAtom(InitConstTab[i].OpName));
    if (ae == NULL) {
      Yap_EvalError(OUT_OF_HEAP_ERROR,TermNil,"at InitConstExps");
      return;
    }
    WRITE_LOCK(ae->ARWLock);
    if (Yap_GetExpPropHavingLock(ae, 0)) {
      WRITE_UNLOCK(ae->ARWLock);
      break;
    }
    p = (ExpEntry *) Yap_AllocAtomSpace(sizeof(ExpEntry));
    p->KindOfPE = ExpProperty;
    p->ArityOfEE = 0;
    p->ENoOfEE = 0;
    p->FOfEE = InitConstTab[i].f;
    AddPropToAtom(ae, (PropEntry *)p);
    WRITE_UNLOCK(ae->ARWLock);
  }
}

/* This routine is called from Restore to make sure we have the same arithmetic operators */
int
Yap_ReInitConstExps(void)
{
  return TRUE;
}

/// @}