/*************************************************************************
*									 *
*	 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

/*
 * This file implements arithmetic operations 
 *
 */
#include "Yap.h"
#include "Yatom.h"
#include "Heap.h"
#include "eval.h"

yap_error_number Yap_matherror = YAP_NO_ERROR;

#define E_FUNC   blob_type
#define E_ARGS   arith_retptr o
#define USE_E_ARGS   o

#define TMP_BIG()   ((o)->big)
#define RBIG(v)     return(big_int_e)
#define RINT(v)     (o)->Int = v; return(long_int_e)
#define RFLOAT(v)   (o)->dbl = v; return(double_e)
#define RERROR()    return(db_ref_e)

static Term
EvalToTerm(blob_type bt, union arith_ret *res)
{
  switch (bt) {
  case long_int_e:
    return MkIntegerTerm(res->Int);
  case double_e:
    return MkFloatTerm(res->dbl);
#ifdef USE_GMP
  case big_int_e:
    {
      Term t = Yap_MkBigIntTerm(res->big);
      mpz_clear(res->big);
      return t;
    }
#endif
  default:
    return TermNil;
  }
}

static E_FUNC
Eval(Term t, E_ARGS)
{
  if (IsVarTerm(t)) {
    Yap_Error(INSTANTIATION_ERROR,TermNil,"in arithmetic");
    P = (yamop *)FAILCODE;
    RERROR();
  }
  if (IsApplTerm(t)) {
    Functor fun = FunctorOfTerm(t);
    switch ((CELL)fun) {
    case (CELL)FunctorLongInt:
      RINT(LongIntOfTerm(t));
    case (CELL)FunctorDouble:
      RFLOAT(FloatOfTerm(t));
#ifdef USE_GMP
    case (CELL)FunctorBigInt:
      {
	MP_INT *new = TMP_BIG();
	mpz_init_set(new, Yap_BigIntOfTerm(t));
	RBIG(new);
      }
#endif
    default:
      {
	Int n = ArityOfFunctor(fun);
	Atom name  = NameOfFunctor(fun);
	ExpEntry *p;

	if (EndOfPAEntr(p = RepExpProp(Yap_GetExpProp(name, n)))) {
	  Term ti[2];

	  /* error */
	  ti[0] = t;
	  ti[1] = MkIntegerTerm(n);
	  t = Yap_MkApplTerm(Yap_MkFunctor(Yap_LookupAtom("/"),2), 2, ti);
	  Yap_Error(TYPE_ERROR_EVALUABLE, t,
		"functor %s/%d for arithmetic expression",
		RepAtom(name)->StrOfAE,n);
	  P = (yamop *)FAILCODE;
	  RERROR();
	}
	if (n == 1)
	  return(p->FOfEE.unary(ArgOfTerm(1,t), USE_E_ARGS));
	return(p->FOfEE.binary(ArgOfTerm(1,t),ArgOfTerm(2,t), USE_E_ARGS));
      }
    }
  } else if (IsPairTerm(t)) {
    if (TailOfTerm(t) != TermNil) {
      Yap_Error(TYPE_ERROR_EVALUABLE, t,
		"string must contain a single character to be evaluated as an arithmetic expression");
      P = (yamop *)FAILCODE;
      RERROR();
    }
    return(Eval(HeadOfTerm(t), USE_E_ARGS));
  } else if (IsIntTerm(t)) {
    RINT(IntOfTerm(t));
  } else {
    Atom name = AtomOfTerm(t);
    ExpEntry *p;

    if (EndOfPAEntr(p = RepExpProp(Yap_GetExpProp(name, 0)))) {
      Term ti[2], terror;
      
      /* error */
      ti[0] = t;
      ti[1] = MkIntegerTerm(0);
      /* error */
      terror = Yap_MkApplTerm(Yap_MkFunctor(Yap_LookupAtom("/"),2), 2, ti);
      Yap_Error(TYPE_ERROR_EVALUABLE, terror,
	    "atom %s for arithmetic expression",
	    RepAtom(name)->StrOfAE);
      P = (yamop *)FAILCODE;
      RERROR();
    }
    return(p->FOfEE.constant(USE_E_ARGS));
  }
}

E_FUNC
Yap_Eval(Term t, E_ARGS)
{
  if (IsVarTerm(t)) {
    Yap_Error(INSTANTIATION_ERROR,TermNil,"in arithmetic");
    P = (yamop *)FAILCODE;
    RERROR();
  }
  if (IsApplTerm(t)) {
    Functor fun = FunctorOfTerm(t);

    switch ((CELL)fun) {
    case (CELL)FunctorLongInt:
      RINT(LongIntOfTerm(t));
    case (CELL)FunctorDouble:
      RFLOAT(FloatOfTerm(t));
#ifdef USE_GMP
    case (CELL)FunctorBigInt:
      {
	MP_INT *new = TMP_BIG();
	mpz_init_set(new, Yap_BigIntOfTerm(t));
	RBIG(new);
      }
#endif
    default:
      {
	Int n = ArityOfFunctor(fun);
	Atom name  = NameOfFunctor(fun);
	ExpEntry *p;

	if (EndOfPAEntr(p = RepExpProp(Yap_GetExpProp(name, n)))) {
	  Term ti[2];

	  /* error */
	  ti[0] = t;
	  ti[1] = MkIntegerTerm(n);
	  t = Yap_MkApplTerm(Yap_MkFunctor(Yap_LookupAtom("/"),2), 2, ti);
	  Yap_Error(TYPE_ERROR_EVALUABLE, t,
		"functor %s/%d for arithmetic expression",
		RepAtom(name)->StrOfAE,n);
	  P = (yamop *)FAILCODE;
	  RERROR();
	}
	if (n == 1) {
	  return p->FOfEE.unary(ArgOfTerm(1,t), USE_E_ARGS);
	}
	return
	  p->FOfEE.binary(ArgOfTerm(1,t),ArgOfTerm(2,t), USE_E_ARGS);
      }
    }
  } else if (IsPairTerm(t)) {
    if (TailOfTerm(t) != TermNil) {
      Yap_Error(TYPE_ERROR_EVALUABLE, t,
		"string must contain a single character to be evaluated as an arithmetic expression");
      P = (yamop *)FAILCODE;
      RERROR();
    }
    return(Eval(HeadOfTerm(t), USE_E_ARGS));
  } else if (IsIntTerm(t)) {
    RINT(IntOfTerm(t));
  } else {
    Atom name = AtomOfTerm(t);
    ExpEntry *p;

    if (EndOfPAEntr(p = RepExpProp(Yap_GetExpProp(name, 0)))) {
      Term ti[2], terror;
      
      /* error */
      ti[0] = t;
      ti[1] = MkIntegerTerm(0);
      /* error */
      terror = Yap_MkApplTerm(Yap_MkFunctor(Yap_LookupAtom("/"),2), 2, ti);
      Yap_Error(TYPE_ERROR_EVALUABLE, terror,
	      "atom %s for arithmetic expression",
	      RepAtom(name)->StrOfAE);
      P = (yamop *)FAILCODE;
      RERROR();
    }
    return(p->FOfEE.constant(USE_E_ARGS));
  }
}

#ifdef BEAM
Int BEAM_is(void);

Int
BEAM_is(void)
{				/* X is Y	 */
  union arith_ret res;
  blob_type bt;

  bt = Eval(Deref(XREGS[2]), &res);
  if (bt==db_ref_e) return (NULL);
  return (EvalToTerm(bt,&res));
}

#endif

static Int
p_is(void)
{				/* X is Y	 */
  union arith_ret res;
  blob_type bt;
  Term out;

  bt = Eval(Deref(ARG2), &res);
  out = EvalToTerm(bt,&res);
  if (out == TermNil) {
    Yap_Error(EVALUATION_ERROR_INT_OVERFLOW, ARG2, "is/2");
    return FALSE;
  }
  return Yap_unify_constant(ARG1,out);
}

void
Yap_InitEval(void)
{
  /* here are the arithmetical predicates */
  Yap_InitConstExps();
  Yap_InitUnaryExps();
  Yap_InitBinaryExps();
  Yap_InitCPred("is", 2, p_is, TestPredFlag | SafePredFlag);
}