/*****
 * mathop.h
 * Tom Prince 2005/3/18
 *
 * Defines some runtime functions used by the stack machine.
 *
 *****/

#ifndef MATHOP_H
#define MATHOP_H

#include <sstream>

#include "stack.h"
#include "mod.h"
#include "triple.h"

namespace run {

template <typename T>
struct less {
  bool operator() (T x, T y, size_t=0) {return x < y;}
};

template <typename T>
struct lessequals {
  bool operator() (T x, T y, size_t=0) {return x <= y;}
};

template <typename T>
struct equals {
  bool operator() (T x, T y, size_t=0) {return x == y;}
};

template <typename T>
struct greaterequals {
  bool operator() (T x, T y, size_t=0) {return x >= y;}
};

template <typename T>
struct greater {
  bool operator() (T x, T y, size_t=0) {return x > y;}
};

template <typename T>
struct notequals {
  bool operator() (T x, T y, size_t=0) {return x != y;}
};

template <typename T>
struct And {
  bool operator() (T x, T y, size_t=0) {return x && y;}
};

template <typename T>
struct Or {
  bool operator() (T x, T y, size_t=0) {return x || y;}
};

template <typename T>
struct Xor {
  bool operator() (T x, T y, size_t=0) {return x ^ y;}
};

template <typename T>
struct plus {
  T operator() (T x, T y, size_t=0) {return x+y;}
};

template <typename T>
struct minus {
  T operator() (T x, T y, size_t=0) {return x-y;}
};

template <typename T>
struct times {
  T operator() (T x, T y, size_t=0) {return x*y;}
};

template <>
struct times<camp::triple> {
  camp::triple operator() (double x, camp::triple y, size_t=0) {return x*y;}
};

template <typename T>
struct timesR {
  T operator () (T y, double x, size_t=0) {return x*y;}
};

extern void dividebyzero(size_t i=0);
extern void integeroverflow(size_t i=0);

template <typename T>
struct divide {
  T operator() (T x, T y,  size_t i=0) {
    if(y == 0) dividebyzero(i);
    return x/y;
  }
};

template <>
struct divide<camp::triple> {
  camp::triple operator() (camp::triple x, double y, size_t=0) {return x/y;}
};

inline bool validInt(double x) {
  return x > (double) Int_MIN-0.5 && x < (double) Int_MAX+0.5;
}

inline void checkInt(double x, size_t i)
{
  if(validInt(x)) return;
  integeroverflow(i);
}

inline Int Intcast(double x)
{
  if(validInt(x)) return (Int) x;
  integeroverflow(0);
  return 0;
}

template<>
struct plus<Int> {
  Int operator() (Int x, Int y, size_t i=0) {
    if((y > 0 && x > Int_MAX-y) || (y < 0 && x < Int_MIN-y))
      integeroverflow(i);
    return x+y;
  }
};

template<>
struct minus<Int> {
  Int operator() (Int x, Int y, size_t i=0) {
    if((y < 0 && x > Int_MAX+y) || (y > 0 && x < Int_MIN+y))
      integeroverflow(i);
    return x-y;
  }
};

template<>
struct times<Int> {
  Int operator() (Int x, Int y, size_t i=0) {
    if(y == 0) return 0;
    if(y < 0) {y=-y; x=-x;}
    if(y > Int_MAX || x > Int_MAX/y || x < Int_MIN/y)
      integeroverflow(i);
    return x*y;
  }
};

template<>
struct divide<Int> {
  double operator() (Int x, Int y, size_t i=0) {
    if(y == 0) dividebyzero(i);
    return ((double) x)/(double) y;
  }
};

template <class T>
void Negate(vm::stack *s)
{
  T a=vm::pop<T>(s);
  s->push(-a);
}

inline Int Negate(Int x, size_t i=0) {
  if(x < -Int_MAX) integeroverflow(i);
  return -x;
}

template<>
inline void Negate<Int>(vm::stack *s)
{
  s->push(Negate(vm::pop<Int>(s)));
}

inline double pow(double x, double y)
{
  return ::pow(x,y);
}

template<class T>
T pow(T x, Int y)
{
  if(y == 0) return 1.0;
  if(x == 0.0 && y > 0) return 0.0;
  if(y < 0) {y=-y; x=1/x;}

  T r=1.0;
  for(;;) {
    if(y & 1) r *= x;
    if((y >>= 1) == 0)  return r;
    x *= x;
  }
}

template <typename T>
struct power {
  T operator() (T x, T y, size_t=0) {return pow(x,y);}
};

template <>
struct power<Int> {
  Int operator() (Int x, Int p,  size_t i=0) {
    if(p == 0) return 1;
    Int sign=1;
    if(x < 0) {
      if(p % 2) sign=-1;
      x=-x;
    }
    if(p > 0) {
      if(x == 0) return 0;
      Int r = 1;
      for(;;) {
        if(p & 1) {
          if(r > Int_MAX/x) integeroverflow(i);
          r *= x;
        }
        if((p >>= 1) == 0)
          return sign*r;
        if(x > Int_MAX/x) integeroverflow(i);
        x *= x;
      }
    } else {
      if(x == 1) return sign;
      ostringstream buf;
      if(i > 0) buf << "array element " << i << ": ";
      buf << "Only 1 and -1 can be raised to negative exponents as integers.";
      vm::error(buf);
      return 0;
    }
  }
};

template <typename T>
struct mod {
  T operator() (T x, T y,  size_t i=0) {
    if(y == 0) dividebyzero(i);
    return portableMod(x,y);
  }
};

template <>
struct mod<Int> {
  Int operator() (Int x, Int y,  size_t i=0) {
    if(y == 0) dividebyzero(i);
    return imod(x,y);
  }
};

template <typename>
struct quotient {
  Int operator() (Int x, Int y,  size_t i=0) {
    if(y == 0) dividebyzero(i);
    if(y == -1) return Negate(x);
// Implementation-independent definition of integer division: round down
    Int q=x/y;
    if(!((x < 0)^(y < 0)) || y*q == x) return q;
    return q-1;
  }
};

template <typename T>
struct min {
  T operator() (T x, T y, size_t=0) {return x < y ? x : y;}
};

template <typename T>
struct max {
  T operator() (T x, T y, size_t=0) {return x > y ? x : y;}
};

template<class T>
inline T Min(T a, T b)
{
  return (a < b) ? a : b;
}

template<class T>
inline T Max(T a, T b)
{
  return (a > b) ? a : b;
}

template <typename T>
struct minbound {
  camp::pair operator() (camp::pair z, camp::pair w) {
    return camp::pair(Min(z.getx(),w.getx()),Min(z.gety(),w.gety()));
  }
  camp::triple operator() (camp::triple u, camp::triple v) {
    return camp::triple(Min(u.getx(),v.getx()),Min(u.gety(),v.gety()),
                        Min(u.getz(),v.getz()));
  }
};

template <typename T>
struct maxbound {
  camp::pair operator() (camp::pair z, camp::pair w) {
    return camp::pair(Max(z.getx(),w.getx()),Max(z.gety(),w.gety()));
  }
  camp::triple operator() (camp::triple u, camp::triple v) {
    return camp::triple(Max(u.getx(),v.getx()),Max(u.gety(),v.gety()),
                        Max(u.getz(),v.getz()));
  }
};

template <double (*func)(double)>
void realReal(vm::stack *s)
{
  double x=vm::pop<double>(s);
  s->push(func(x));
}

template <class T, template <class S> class op>
void binaryOp(vm::stack *s)
{
  T b=vm::pop<T>(s);
  T a=vm::pop<T>(s);
  s->push(op<T>()(a,b));
}

template <class T>
void interp(vm::stack *s)
{
  double t=vm::pop<double>(s);
  T b=vm::pop<T>(s);
  T a=vm::pop<T>(s);
  s->push((1-t)*a+t*b);
}

} // namespace run

#endif //MATHOP_H
