/*****
 * builtin.cc
 * Tom Prince 2004/08/25
 *
 * Initialize builtins.
 *****/

#include <cmath>

#include "builtin.h"
#include "entry.h"

#include "runtime.h"
#include "runpicture.h"
#include "runlabel.h"
#include "runhistory.h"
#include "runarray.h"
#include "runfile.h"
#include "runsystem.h"
#include "runstring.h"
#include "runpair.h"
#include "runtriple.h"
#include "runpath.h"
#include "runpath3d.h"
#include "runmath.h"

#include "types.h"

#include "castop.h"
#include "mathop.h"
#include "arrayop.h"
#include "vm.h"

#include "coder.h"
#include "exp.h"
#include "refaccess.h"
#include "settings.h"

#include "opsymbols.h"

#ifndef NOSYM
#include "builtin.symbols.h"
#endif

namespace vm {
// Defined in stack.cc
extern vm::vmFrame *make_dummyframe(string name);
}

using namespace types;
using namespace camp;
using namespace vm;

namespace trans {
using camp::transform;
using camp::pair;
using vm::bltin;
using run::divide;
using run::less;
using run::greater;
using run::plus;
using run::minus;

using namespace run;

void gen_runtime_venv(venv &ve);
void gen_runbacktrace_venv(venv &ve);
void gen_runpicture_venv(venv &ve);
void gen_runlabel_venv(venv &ve);
void gen_runhistory_venv(venv &ve);
void gen_runarray_venv(venv &ve);
void gen_runfile_venv(venv &ve);
void gen_runsystem_venv(venv &ve);
void gen_runstring_venv(venv &ve);
void gen_runpair_venv(venv &ve);
void gen_runtriple_venv(venv &ve);
void gen_runpath_venv(venv &ve);
void gen_runpath3d_venv(venv &ve);
void gen_runmath_venv(venv &ve);
void gen_rungsl_venv(venv &ve);

void addType(tenv &te, symbol name, ty *t)
{
  te.enter(name, new tyEntry(t,0,0,nullPos));
}

// The base environments for built-in types and functions
void base_tenv(tenv &te)
{
#define PRIMITIVE(name,Name,asyName)                    \
  addType(te, symbol::trans(#asyName), prim##Name());
#include "primitives.h"
#undef PRIMITIVE
}

const formal noformal(0);

function *functionFromFormals(ty *result,
                              formal f1=noformal, formal f2=noformal, formal f3=noformal,
                              formal f4=noformal, formal f5=noformal, formal f6=noformal,
                              formal f7=noformal, formal f8=noformal, formal f9=noformal,
                              formal fA=noformal, formal fB=noformal, formal fC=noformal,
                              formal fD=noformal, formal fE=noformal, formal fF=noformal,
                              formal fG=noformal, formal fH=noformal, formal fI=noformal)
{
  function *fun = new function(result);

  if (f1.t) fun->add(f1);
  if (f2.t) fun->add(f2);
  if (f3.t) fun->add(f3);
  if (f4.t) fun->add(f4);
  if (f5.t) fun->add(f5);
  if (f6.t) fun->add(f6);
  if (f7.t) fun->add(f7);
  if (f8.t) fun->add(f8);
  if (f9.t) fun->add(f9);
  if (fA.t) fun->add(fA);
  if (fB.t) fun->add(fB);
  if (fC.t) fun->add(fC);
  if (fD.t) fun->add(fD);
  if (fE.t) fun->add(fE);
  if (fF.t) fun->add(fF);
  if (fG.t) fun->add(fG);
  if (fH.t) fun->add(fH);
  if (fI.t) fun->add(fI);

  return fun;
}

varEntry *addFunc(venv &ve, access *a, ty *result, symbol id,
             formal f1=noformal, formal f2=noformal, formal f3=noformal,
             formal f4=noformal, formal f5=noformal, formal f6=noformal,
             formal f7=noformal, formal f8=noformal, formal f9=noformal,
             formal fA=noformal, formal fB=noformal, formal fC=noformal,
             formal fD=noformal, formal fE=noformal, formal fF=noformal,
             formal fG=noformal, formal fH=noformal, formal fI=noformal)
{
  function *fun = functionFromFormals(result,f1,f2,f3,f4,f5,f6,f7,f8,f9,
                                      fA,fB,fC,fD,fE,fF,fG,fH,fI);

  // NOTE: If the function is a field, we should encode the defining record in
  // the entry
  varEntry *ent = new varEntry(fun, a, 0, nullPos);

  ve.enter(id, ent);
  return ent;
}

// Add a function with one or more default arguments.
varEntry *addFunc(venv &ve, bltin f, ty *result, symbol name,
             formal f1, formal f2, formal f3, formal f4, formal f5, formal f6,
             formal f7, formal f8, formal f9, formal fA, formal fB, formal fC,
             formal fD, formal fE, formal fF, formal fG, formal fH, formal fI)
{
#ifdef DEBUG_BLTIN
  // If the function is an operator, print out the whole signature with the
  // types, as operators are heavily overloaded.  min and max are also heavily
  // overloaded, so we check for them too.  Many builtin functions have so
  // many arguments that it is noise to print out their full signatures.
  string s = name;
  if (s.find("operator ", 0) == 0 || s == "min" || s == "max")
    {
      function *fun = functionFromFormals(result,f1,f2,f3,f4,f5,f6,f7,f8,f9,
                                          fA,fB,fC,fD,fE,fF,fG,fH,fI);
      ostringstream out;
      fun->printVar(out, name);
      REGISTER_BLTIN(f, out.str());
    }
  else {
    REGISTER_BLTIN(f, name);
  }
#endif

  access *a = new bltinAccess(f);
  return addFunc(ve,a,result,name,f1,f2,f3,f4,f5,f6,f7,f8,f9,
                 fA,fB,fC,fD,fE,fF,fG,fH,fI);
}

void addOpenFunc(venv &ve, bltin f, ty *result, symbol name)
{
  function *fun = new function(result, signature::OPEN);

  REGISTER_BLTIN(f, name);
  access *a= new bltinAccess(f);

  varEntry *ent = new varEntry(fun, a, 0, nullPos);

  ve.enter(name, ent);
}


// Add a rest function with zero or more default/explicit arguments.
void addRestFunc(venv &ve, bltin f, ty *result, symbol name, formal frest,
                 formal f1=noformal, formal f2=noformal, formal f3=noformal,
                 formal f4=noformal, formal f5=noformal, formal f6=noformal,
                 formal f7=noformal, formal f8=noformal, formal f9=noformal)
{
  REGISTER_BLTIN(f, name);
  access *a = new bltinAccess(f);
  function *fun = new function(result);

  if (f1.t) fun->add(f1);
  if (f2.t) fun->add(f2);
  if (f3.t) fun->add(f3);
  if (f4.t) fun->add(f4);
  if (f5.t) fun->add(f5);
  if (f6.t) fun->add(f6);
  if (f7.t) fun->add(f7);
  if (f8.t) fun->add(f8);
  if (f9.t) fun->add(f9);

  if (frest.t) fun->addRest(frest);

  varEntry *ent = new varEntry(fun, a, 0, nullPos);

  ve.enter(name, ent);
}

void addRealFunc0(venv &ve, bltin fcn, symbol name)
{
  addFunc(ve, fcn, primReal(), name);
}

template<double (*fcn)(double)>
void addRealFunc(venv &ve, symbol name)
{
  addFunc(ve, realReal<fcn>, primReal(), name, formal(primReal(),SYM(x)));
  addFunc(ve, arrayFunc<double,double,fcn>, realArray(), name,
          formal(realArray(),SYM(a)));
}

#define addRealFunc(fcn, sym) addRealFunc<fcn>(ve, sym);

void addRealFunc2(venv &ve, bltin fcn, symbol name)
{
  addFunc(ve,fcn,primReal(),name,formal(primReal(),SYM(a)),
          formal(primReal(),SYM(b)));
}

template <double (*func)(double, int)>
void realRealInt(vm::stack *s) {
  Int n = pop<Int>(s);
  double x = pop<double>(s);
  s->push(func(x, intcast(n)));
}

template<double (*fcn)(double, int)>
void addRealIntFunc(venv& ve, symbol name, symbol arg1,
                    symbol arg2) {
  addFunc(ve, realRealInt<fcn>, primReal(), name, formal(primReal(), arg1),
          formal(primInt(), arg2));
}

void addInitializer(venv &ve, ty *t, access *a)
{
  addFunc(ve, a, t, symbol::initsym);
}

void addInitializer(venv &ve, ty *t, bltin f)
{
#ifdef DEBUG_BLTIN
  ostringstream s;
  s << "initializer for " << *t;
  REGISTER_BLTIN(f, s.str());
#endif
  access *a = new bltinAccess(f);
  addInitializer(ve, t, a);
}

// Specifies that source may be cast to target, but only if an explicit
// cast expression is used.
void addExplicitCast(venv &ve, ty *target, ty *source, access *a) {
  addFunc(ve, a, target, symbol::ecastsym, source);
}

// Specifies that source may be implicitly cast to target by the
// function or instruction stores at a.
void addCast(venv &ve, ty *target, ty *source, access *a) {
  //addExplicitCast(target,source,a);
  addFunc(ve, a, target, symbol::castsym, source);
}

void addExplicitCast(venv &ve, ty *target, ty *source, bltin f) {
#ifdef DEBUG_BLTIN
  ostringstream s;
  s << "explicit cast from " << *source << " to " << *target;
  REGISTER_BLTIN(f, s.str());
#endif
  addExplicitCast(ve, target, source, new bltinAccess(f));
}

void addCast(venv &ve, ty *target, ty *source, bltin f) {
#ifdef DEBUG_BLTIN
  ostringstream s;
  s << "cast from " << *source << " to " << *target;
  REGISTER_BLTIN(f, s.str());
#endif
  addCast(ve, target, source, new bltinAccess(f));
}

template<class T>
void addVariable(venv &ve, T *ref, ty *t, symbol name,
                 record *module=settings::getSettingsModule()) {
  access *a = new refAccess<T>(ref);
  varEntry *ent = new varEntry(t, a, PUBLIC, module, 0, nullPos);
  ve.enter(name, ent);
}

template<class T>
void addVariable(venv &ve, T value, ty *t, symbol name,
                 record *module=settings::getSettingsModule(),
                 permission perm=PUBLIC) {
  item* ref=new item;
  *ref=value;
  access *a = new itemRefAccess(ref);
  varEntry *ent = new varEntry(t, a, perm, module, 0, nullPos);
  ve.enter(name, ent);
}

template<class T>
void addConstant(venv &ve, T value, ty *t, symbol name,
                 record *module=settings::getSettingsModule()) {
  addVariable(ve,value,t,name,module,RESTRICTED);
}

// The identity access, i.e. no instructions are encoded for a cast or
// operation, and no functions are called.
identAccess id;

function *IntRealFunction()
{
  return new function(primInt(),primReal());
}

function *realPairFunction()
{
  return new function(primReal(),primPair());
}

function *voidFileFunction()
{
  return new function(primVoid(),primFile());
}

void addInitializers(venv &ve)
{
  addInitializer(ve, primBoolean(), boolFalse);
  addInitializer(ve, primInt(), IntZero);
  addInitializer(ve, primReal(), realZero);

  addInitializer(ve, primString(), emptyString);
  addInitializer(ve, primPair(), pairZero);
  addInitializer(ve, primTriple(), tripleZero);
  addInitializer(ve, primTransform(), transformIdentity);
  addInitializer(ve, primGuide(), nullGuide);
  addInitializer(ve, primPath(), nullPath);
  addInitializer(ve, primPath3(), nullPath3);
  addInitializer(ve, primPen(), newPen);
  addInitializer(ve, primPicture(), newPicture);
  addInitializer(ve, primFile(), nullFile);
}

void addCasts(venv &ve)
{
  addExplicitCast(ve, primString(), primInt(), stringCast<Int>);
  addExplicitCast(ve, primString(), primReal(), stringCast<double>);
  addExplicitCast(ve, primString(), primPair(), stringCast<pair>);
  addExplicitCast(ve, primString(), primTriple(), stringCast<triple>);
  addExplicitCast(ve, primInt(), primString(), castString<Int>);
  addExplicitCast(ve, primReal(), primString(), castString<double>);
  addExplicitCast(ve, primPair(), primString(), castString<pair>);
  addExplicitCast(ve, primTriple(), primString(), castString<triple>);

  addExplicitCast(ve, primInt(), primReal(), castDoubleInt);

  addCast(ve, primReal(), primInt(), cast<Int,double>);
  addCast(ve, primPair(), primInt(), cast<Int,pair>);
  addCast(ve, primPair(), primReal(), cast<double,pair>);

  addCast(ve, primPath(), primPair(), cast<pair,path>);
  addCast(ve, primGuide(), primPair(), pairToGuide);
  addCast(ve, primGuide(), primPath(), pathToGuide);
  addCast(ve, primPath(), primGuide(), guideToPath);

  addCast(ve, primFile(), primNull(), nullFile);

  // Vectorized casts.
  addExplicitCast(ve, IntArray(), realArray(), arrayToArray<double,Int>);

  addCast(ve, realArray(), IntArray(), arrayToArray<Int,double>);
  addCast(ve, pairArray(), IntArray(), arrayToArray<Int,pair>);
  addCast(ve, pairArray(), realArray(), arrayToArray<double,pair>);

  addCast(ve, realArray2(), IntArray2(), array2ToArray2<Int,double>);
  addCast(ve, pairArray2(), IntArray2(), array2ToArray2<Int,pair>);
  addCast(ve, pairArray2(), realArray2(), array2ToArray2<double,pair>);
}

void addTupleOperators(venv &ve)
{
  addFunc(ve, realRealToPair, primPair(), SYM_TUPLE,
          formal(primReal(), SYM(x)),
          formal(primReal(), SYM(y)));
  addFunc(ve, realRealRealToTriple, primTriple(), SYM_TUPLE,
          formal(primReal(), SYM(x)),
          formal(primReal(), SYM(y)),
          formal(primReal(), SYM(z)));
  addFunc(ve, real6ToTransform, primTransform(), SYM_TUPLE,
          formal(primReal(), SYM(x)),
          formal(primReal(), SYM(y)),
          formal(primReal(), SYM(xx)),
          formal(primReal(), SYM(xy)),
          formal(primReal(), SYM(yx)),
          formal(primReal(), SYM(yy)));
}

void addGuideOperators(venv &ve)
{
  // The guide operators .. and -- take an array of guides, and turn them
  // into a single guide.
  addRestFunc(ve, dotsGuide, primGuide(), SYM_DOTS, guideArray());
  addRestFunc(ve, dashesGuide, primGuide(), SYM_DASHES, guideArray());
}

/* Avoid typing the same type three times. */
void addSimpleOperator(venv &ve, bltin f, ty *t, symbol name)
{
  addFunc(ve,f,t,name,formal(t,SYM(a)),formal(t,SYM(b)));
}
void addBooleanOperator(venv &ve, bltin f, ty *t, symbol name)
{
  addFunc(ve,f,primBoolean(),name,formal(t,SYM(a)),formal(t,SYM(b)));
}

template<class T, template <class S> class op>
void addArray2Array2Op(venv &ve, ty *t3, symbol name)
{
  addFunc(ve,array2Array2Op<T,op>,t3,name,formal(t3,SYM(a)),formal(t3,SYM(b)));
}

template<class T, template <class S> class op>
void addOpArray2(venv &ve, ty *t1, symbol name, ty *t3)
{
  addFunc(ve,opArray2<T,T,op>,t3,name,formal(t1,SYM(a)),formal(t3,SYM(b)));
}

template<class T, template <class S> class op>
void addArray2Op(venv &ve, ty *t1, symbol name, ty *t3)
{
  addFunc(ve,array2Op<T,T,op>,t3,name,formal(t3,SYM(a)),formal(t1,SYM(b)));
}

template<class T, template <class S> class op>
void addOps(venv &ve, ty *t1, symbol name, ty *t2)
{
  addSimpleOperator(ve,binaryOp<T,op>,t1,name);
  addFunc(ve,opArray<T,T,op>,t2,name,formal(t1,SYM(a)),formal(t2,SYM(b)));
  addFunc(ve,arrayOp<T,T,op>,t2,name,formal(t2,SYM(a)),formal(t1,SYM(b)));
  addSimpleOperator(ve,arrayArrayOp<T,op>,t2,name);
}

template<class T, template <class S> class op>
void addBooleanOps(venv &ve, ty *t1, symbol name, ty *t2)
{
  addBooleanOperator(ve,binaryOp<T,op>,t1,name);
  addFunc(ve,opArray<T,T,op>,
          booleanArray(),name,formal(t1,SYM(a)),formal(t2,SYM(b)));
  addFunc(ve,arrayOp<T,T,op>,
          booleanArray(),name,formal(t2,SYM(a)),formal(t1,SYM(b)));
  addFunc(ve,arrayArrayOp<T,op>,booleanArray(),name,formal(t2,SYM(a)),
          formal(t2,SYM(b)));
}

void addWrite(venv &ve, bltin f, ty *t1, ty *t2)
{
  addRestFunc(ve,f,primVoid(),SYM(write),t2,
              formal(primFile(),SYM(file),true),
              formal(primString(),SYM(s),true),
              formal(t1,SYM(x)),formal(voidFileFunction(),SYM(suffix),true));
}

template<class T>
void addUnorderedOps(venv &ve, ty *t1, ty *t2, ty *t3, ty *t4)
{
  addBooleanOps<T,equals>(ve,t1,SYM_EQ,t2);
  addBooleanOps<T,notequals>(ve,t1,SYM_NEQ,t2);

  addFunc(ve, run::array2Equals<T>, primBoolean(), SYM_EQ, formal(t3, SYM(a)),
          formal(t3, SYM(b)));
  addFunc(ve, run::array2NotEquals<T>, primBoolean(),
          SYM_NEQ, formal(t3, SYM(a)),
          formal(t3, SYM(b)));

  addCast(ve,t1,primFile(),read<T>);
  addCast(ve,t2,primFile(),readArray1<T>);
  addCast(ve,t3,primFile(),readArray2<T>);
  addCast(ve,t4,primFile(),readArray3<T>);

  addWrite(ve,write<T>,t1,t2);
  addRestFunc(ve,writeArray<T>,primVoid(),SYM(write),t3,
              formal(primFile(),SYM(file),true),
              formal(primString(),SYM(s),true),
              formal(t2,SYM(a),false,true));
  addFunc(ve,writeArray2<T>,primVoid(),SYM(write),
          formal(primFile(),SYM(file),true),t3);
  addFunc(ve,writeArray3<T>,primVoid(),SYM(write),
          formal(primFile(),SYM(file),true),t4);
}

inline double abs(pair z) {
  return z.length();
}

inline double abs(triple v) {
  return v.length();
}

inline pair conjugate(pair z) {
  return conj(z);
}

template<class T>
inline T negate(T x) {
  return -x;
}

template<class T, template <class S> class op>
void addBinOps(venv &ve, ty *t1, ty *t2, ty *t3, ty *t4, symbol name)
{
  addFunc(ve,binopArray<T,op>,t1,name,formal(t2,SYM(a)));
  addFunc(ve,binopArray2<T,op>,t1,name,formal(t3,SYM(a)));
  addFunc(ve,binopArray3<T,op>,t1,name,formal(t4,SYM(a)));
}

template<class T>
void addOrderedOps(venv &ve, ty *t1, ty *t2, ty *t3, ty *t4)
{
  addBooleanOps<T,less>(ve,t1,SYM_LT,t2);
  addBooleanOps<T,lessequals>(ve,t1,SYM_LE,t2);
  addBooleanOps<T,greaterequals>(ve,t1,SYM_GE,t2);
  addBooleanOps<T,greater>(ve,t1,SYM_GT,t2);

  addOps<T,run::min>(ve,t1,SYM(min),t2);
  addOps<T,run::max>(ve,t1,SYM(max),t2);
  addBinOps<T,run::min>(ve,t1,t2,t3,t4,SYM(min));
  addBinOps<T,run::max>(ve,t1,t2,t3,t4,SYM(max));

  addFunc(ve,sortArray<T>,t2,SYM(sort),formal(t2,SYM(a)));
  addFunc(ve,sortArray2<T>,t3,SYM(sort),formal(t3,SYM(a)));

  addFunc(ve,searchArray<T>,primInt(),SYM(search),formal(t2,SYM(a)),
          formal(t1,SYM(key)));
}

template<class T>
void addBasicOps(venv &ve, ty *t1, ty *t2, ty *t3, ty *t4, bool integer=false,
                 bool Explicit=false)
{
  addOps<T,plus>(ve,t1,SYM_PLUS,t2);
  addOps<T,minus>(ve,t1,SYM_MINUS,t2);
  addFunc(ve,initialized,primBoolean(),SYM(initialized),formal(t1,SYM(a)));

  addArray2Array2Op<T,plus>(ve,t3,SYM_PLUS);
  addArray2Array2Op<T,minus>(ve,t3,SYM_MINUS);

  addFunc(ve,&id,t1,SYM_PLUS,formal(t1,SYM(a)));
  addFunc(ve,&id,t2,SYM_PLUS,formal(t2,SYM(a)));
  addFunc(ve,Negate<T>,t1,SYM_MINUS,formal(t1,SYM(a)));
  addFunc(ve,arrayFunc<T,T,negate>,t2,SYM_MINUS,formal(t2,SYM(a)));
  addFunc(ve,arrayFunc2<T,T,negate>,t3,SYM_MINUS,formal(t3,SYM(a)));
  if(!integer) addFunc(ve,interp<T>,t1,SYM(interp),
                       formal(t1,SYM(a),false,Explicit),
                       formal(t1,SYM(b),false,Explicit),
                       formal(primReal(),SYM(t)));

  addFunc(ve,sumArray<T>,t1,SYM(sum),formal(t2,SYM(a)));
  addUnorderedOps<T>(ve,t1,t2,t3,t4);
}

template<class T>
void addOps(venv &ve, ty *t1, ty *t2, ty *t3, ty *t4, bool integer=false,
            bool Explicit=false)
{
  addBasicOps<T>(ve,t1,t2,t3,t4,integer,Explicit);

  addOps<T,times>(ve,t1,SYM_TIMES,t2);
  addOpArray2<T,times>(ve,t1,SYM_TIMES,t3);
  addArray2Op<T,times>(ve,t1,SYM_TIMES,t3);

  if(!integer) {
    addOps<T,run::divide>(ve,t1,SYM_DIVIDE,t2);
    addArray2Op<T,run::divide>(ve,t1,SYM_DIVIDE,t3);
  }

  addOps<T,power>(ve,t1,SYM_CARET,t2);
}


// Adds standard functions for a newly added array type.
void addArrayOps(venv &ve, types::array *t)
{
  ty *ct = t->celltype;

  // Check for the alias function to see if these operation have already been
  // added, if they have, don't add them again.
  static types::function aliasType(primBoolean(), primVoid(), primVoid());
  aliasType.sig.formals[0].t = t;
  aliasType.sig.formals[1].t = t;

  if (ve.lookByType(SYM(alias), &aliasType))
    return;

  addFunc(ve, run::arrayAlias,
          primBoolean(), SYM(alias), formal(t, SYM(a)), formal(t, SYM(b)));

  size_t depth=(size_t) t->depth();

  // Define an array constructor.  This needs to know the depth of the array,
  // which may not be known at runtime.  Therefore, the depth, which is known
  // here at compile-time, is pushed on the stack beforehand by use of a
  // thunk.
  callable *copyValueFunc = new thunk(new vm::bfunc(run::copyArrayValue),(Int) depth-1);
  addFunc(ve, new callableAccess(copyValueFunc),
          t, SYM(array), formal(primInt(), SYM(n)),
          formal(ct, SYM(value)),
          formal(primInt(), SYM(depth), true));

  callable *copyFunc = new thunk(new vm::bfunc(run::copyArray),(Int) depth);
  addFunc(ve, new callableAccess(copyFunc),
          t, SYM(copy), formal(t, SYM(a)), formal(primInt(), SYM(depth), true));

  addFunc(ve, run::arrayFunction,
          t, SYM(map), formal(new function(ct, ct), SYM(f)), formal(t, SYM(a)));

  addFunc(ve, run::arraySequence,
          t, SYM(sequence), formal(new function(ct, primInt()), SYM(f)),
          formal(primInt(), SYM(n)));

  addFunc(ve, run::arraySort,
          t, SYM(sort), formal(t, SYM(a)),
          formal(new function(primBoolean(), ct, ct), SYM(less)),
          formal(primBoolean(), SYM(stable), true));

  switch (depth) {
    case 1:
      addRestFunc(ve, run::arrayConcat, t, SYM(concat), new types::array(t));
      addFunc(ve, run::arraySearch,
              primInt(), SYM(search), formal(t, SYM(a)), formal(ct, SYM(key)),
              formal(new function(primBoolean(), ct, ct), SYM(less)));
      break;
    case 2:
      addFunc(ve, run::array2Transpose, t, SYM(transpose), formal(t, SYM(a)));
      break;
    case 3:
      addFunc(ve, run::array3Transpose, t, SYM(transpose), formal(t, SYM(a)),
              formal(IntArray(),SYM(perm)));
      break;
    default:
      break;
  }
}

void addRecordOps(record* r)
{
  assert(r);
  trans::venv &ve = r->e.ve;
  auto addOp= [&ve, r](vm::bltin f, ty* result, symbol name, auto&&... formals) {
    varEntry* fVar=
            addFunc(ve, f, result, name, std::forward<formal>(formals)...);
    r->autounravelRegistry.registerAutoUnravel(name, fVar, AutounravelPriority::OFFER);
  };
  // alias
  addOp(run::boolMemEq, primBoolean(), SYM(alias), formal(r, SYM(a)),
        formal(r, SYM(b)));
  // operator==
  addOp(run::boolMemEq, primBoolean(), SYM_EQ, formal(r, SYM(a)),
        formal(r, SYM(b)));
  // operator!=
  addOp(run::boolMemNeq, primBoolean(), SYM_NEQ, formal(r, SYM(a)),
        formal(r, SYM(b)));
}

void addOperators(venv &ve)
{
  addSimpleOperator(ve,binaryOp<string,plus>,primString(),SYM_PLUS);

  addBooleanOps<bool,And>(ve,primBoolean(),SYM_AMPERSAND,booleanArray());
  addBooleanOps<bool,Or>(ve,primBoolean(),SYM_BAR,booleanArray());
  addBooleanOps<bool,Xor>(ve,primBoolean(),SYM_CARET,booleanArray());

  addUnorderedOps<bool>(ve,primBoolean(),booleanArray(),booleanArray2(),
                        booleanArray3());
  addOps<Int>(ve,primInt(),IntArray(),IntArray2(),IntArray3(),true);
  addOps<double>(ve,primReal(),realArray(),realArray2(),realArray3());
  addOps<pair>(ve,primPair(),pairArray(),pairArray2(),pairArray3(),false,true);
  addBasicOps<triple>(ve,primTriple(),tripleArray(),tripleArray2(),
                      tripleArray3());
  addFunc(ve,opArray<double,triple,times>,tripleArray(),SYM_TIMES,
          formal(primReal(),SYM(a)),formal(tripleArray(),SYM(b)));
  addFunc(ve,opArray2<double,triple,timesR>,tripleArray2(),SYM_TIMES,
          formal(primReal(),SYM(a)),formal(tripleArray2(),SYM(b)));
  addFunc(ve,arrayOp<triple,double,timesR>,tripleArray(),SYM_TIMES,
          formal(tripleArray(),SYM(a)),formal(primReal(),SYM(b)));
  addFunc(ve,array2Op<triple,double,timesR>,tripleArray2(),SYM_TIMES,
          formal(tripleArray2(),SYM(a)),formal(primReal(),SYM(b)));
  addFunc(ve,arrayOp<triple,double,divide>,tripleArray(),SYM_DIVIDE,
          formal(tripleArray(),SYM(a)),formal(primReal(),SYM(b)));

  addUnorderedOps<string>(ve,primString(),stringArray(),stringArray2(),
                          stringArray3());

  addSimpleOperator(ve,binaryOp<pair,minbound>,primPair(),SYM(minbound));
  addSimpleOperator(ve,binaryOp<pair,maxbound>,primPair(),SYM(maxbound));
  addSimpleOperator(ve,binaryOp<triple,minbound>,primTriple(),SYM(minbound));
  addSimpleOperator(ve,binaryOp<triple,maxbound>,primTriple(),SYM(maxbound));
  addBinOps<pair,minbound>(ve,primPair(),pairArray(),pairArray2(),pairArray3(),
                           SYM(minbound));
  addBinOps<pair,maxbound>(ve,primPair(),pairArray(),pairArray2(),pairArray3(),
                           SYM(maxbound));
  addBinOps<triple,minbound>(ve,primTriple(),tripleArray(),tripleArray2(),
                             tripleArray3(),SYM(minbound));
  addBinOps<triple,maxbound>(ve,primTriple(),tripleArray(),tripleArray2(),
                             tripleArray3(),SYM(maxbound));

  addFunc(ve,arrayFunc<double,pair,abs>,realArray(),SYM(abs),
          formal(pairArray(),SYM(a)));
  addFunc(ve,arrayFunc<double,triple,abs>,realArray(),SYM(abs),
          formal(tripleArray(),SYM(a)));

  addFunc(ve,arrayFunc<pair,pair,conjugate>,pairArray(),SYM(conj),
          formal(pairArray(),SYM(a)));
  addFunc(ve,arrayFunc2<pair,pair,conjugate>,pairArray2(),SYM(conj),
          formal(pairArray2(),SYM(a)));

  addFunc(ve,binaryOp<Int,divide>,primReal(),SYM_DIVIDE,
          formal(primInt(),SYM(a)),formal(primInt(),SYM(b)));
  addFunc(ve,arrayOp<Int,Int,divide>,realArray(),SYM_DIVIDE,
          formal(IntArray(),SYM(a)),formal(primInt(),SYM(b)));
  addFunc(ve,opArray<Int,Int,divide>,realArray(),SYM_DIVIDE,
          formal(primInt(),SYM(a)),formal(IntArray(),SYM(b)));
  addFunc(ve,arrayArrayOp<Int,divide>,realArray(),SYM_DIVIDE,
          formal(IntArray(),SYM(a)),formal(IntArray(),SYM(b)));

  addOrderedOps<Int>(ve,primInt(),IntArray(),IntArray2(),IntArray3());
  addOrderedOps<double>(ve,primReal(),realArray(),realArray2(),realArray3());
  addOrderedOps<string>(ve,primString(),stringArray(),stringArray2(),
                        stringArray3());

  addOps<Int,mod>(ve,primInt(),SYM_MOD,IntArray());
  addOps<Int,quotient>(ve,primInt(),SYM_QUOTIENT,IntArray());
  addOps<double,mod>(ve,primReal(),SYM_MOD,realArray());

  addRestFunc(ve,diagonal<Int>,IntArray2(),SYM(diagonal),IntArray());
  addRestFunc(ve,diagonal<double>,realArray2(),SYM(diagonal),realArray());
  addRestFunc(ve,diagonal<pair>,pairArray2(),SYM(diagonal),pairArray());
}

dummyRecord *createDummyRecord(venv &ve, symbol name)
{
  dummyRecord *r=new dummyRecord(name);
  vm::vmFrame *f = make_dummyframe(name);
  addConstant(ve, f, r, name);
  addRecordOps(r);
  return r;
}

double identity(double x) {return x;}
double pow10(double x) {return run::pow(10.0,x);}

// An example of an open function.
#ifdef OPENFUNCEXAMPLE
void openFunc(stack *Stack)
{
  vm::array *a=vm::pop<vm::array *>(Stack);
  size_t numArgs=checkArray(a);
  for (size_t k=0; k<numArgs; ++k)
    cout << k << ": " << (*a)[k];

  Stack->push<Int>((Int)numArgs);
}
#endif

// A function accessible in asy code print the bytecode of a function.
void printBytecode(stack *Stack)
{
  // As arbitrary addresses can be sent to printBytecode, it should not be run
  // in safe mode.
  if (settings::safe) {
    cerr << "use -nosafe flag to enable printBytecode" << endl;
    return;
  }

  vm::array *a=vm::pop<vm::array *>(Stack);
  size_t numArgs=checkArray(a);
  if (numArgs != 1)
    cerr << "printBytecode takes one argument" << endl;

  // TODO: Add a reliable test for the object being a func.
  callable *c = a->read<callable *>(0);
  if (func *f = dynamic_cast<func *>(c))
    print(cout, f->body->code);
  else
    cout << "callable is not a standard function";
}




// NOTE: We should move all of these into a "builtin" module.
void base_venv(venv &ve)
{
  // Register the name of arrayDeleteHelper for debugging in "asy -s" mode.
  // This is done automatically for other function, but because
  // arrayDeleteHelper is not defined in the usual way, it must be done
  // explicitly, and here is as good a place as any.
  REGISTER_BLTIN(arrayDeleteHelper, "arrayDeleteHelper");

  addInitializers(ve);
  addCasts(ve);
  addOperators(ve);
  addTupleOperators(ve);
  addGuideOperators(ve);

  addRealFunc(sin,SYM(sin));
  addRealFunc(cos,SYM(cos));
  addRealFunc(tan,SYM(tan));
  addRealFunc(asin,SYM(asin));
  addRealFunc(acos,SYM(acos));
  addRealFunc(atan,SYM(atan));
  addRealFunc(exp,SYM(exp));
  addRealFunc(log,SYM(log));
  addRealFunc(log10,SYM(log10));
  addRealFunc(sinh,SYM(sinh));
  addRealFunc(cosh,SYM(cosh));
  addRealFunc(tanh,SYM(tanh));
  addRealFunc(asinh,SYM(asinh));
  addRealFunc(acosh,SYM(acosh));
  addRealFunc(atanh,SYM(atanh));
  addRealFunc(sqrt,SYM(sqrt));
  addRealFunc(cbrt,SYM(cbrt));
  addRealFunc(fabs,SYM(fabs));
  addRealFunc<fabs>(ve,SYM(abs));
  addRealFunc(expm1,SYM(expm1));
  addRealFunc(log1p,SYM(log1p));
  addRealIntFunc<ldexp>(ve, SYM(ldexp), SYM(x), SYM(e));

  addRealFunc(pow10,SYM(pow10));
  addRealFunc(identity,SYM(identity));

#ifdef STRUCTEXAMPLE
  dummyRecord *fun=createDummyRecord(ve, SYM(test));
  addFunc(fun->e.ve,realReal<sin>,primReal(),SYM(f),formal(primReal(),SYM(x)));
  addVariable<Int>(fun->e.ve,1,primInt(),SYM(x));
#endif

  addFunc(ve,writestring,primVoid(),SYM(write),
          formal(primFile(),SYM(file),true),
          formal(primString(),SYM(s)),
          formal(voidFileFunction(),SYM(suffix),true));

  addWrite(ve,write<transform>,primTransform(),transformArray());
  addWrite(ve,write<guide *>,primGuide(),guideArray());
  addWrite(ve,write<pen>,primPen(),penArray());
  addFunc(ve,arrayArrayOp<pen,equals>,booleanArray(),SYM_EQ,
          formal(penArray(),SYM(a)),formal(penArray(),SYM(b)));
  addFunc(ve,arrayArrayOp<pen,notequals>,booleanArray(),SYM_NEQ,
          formal(penArray(),SYM(a)),formal(penArray(),SYM(b)));

  addFunc(ve,arrayFunction,realArray(),SYM(map),
          formal(realPairFunction(),SYM(f)),
          formal(pairArray(),SYM(a)));
  addFunc(ve,arrayFunction,IntArray(),SYM(map),
          formal(IntRealFunction(),SYM(f)),
          formal(realArray(),SYM(a)));

  addConstant<Int>(ve, Int_MAX, primInt(), SYM(intMax));
  addConstant<Int>(ve, Int_MIN, primInt(), SYM(intMin));
  addConstant<double>(ve, HUGE_VAL, primReal(), SYM(inf));
  addConstant<double>(ve, run::infinity, primReal(), SYM(infinity));
  addConstant<double>(ve, nan(""), primReal(), SYM(nan));
  addConstant<double>(ve, DBL_MAX, primReal(), SYM(realMax));
  addConstant<double>(ve, DBL_MIN, primReal(), SYM(realMin));
  addConstant<double>(ve, DBL_EPSILON, primReal(), SYM(realEpsilon));
  addConstant<Int>(ve, DBL_DIG, primInt(), SYM(realDigits));
  addConstant<Int>(ve, Int_MAX, primInt(), SYM(randMax));
  addConstant<double>(ve, PI, primReal(), SYM(pi));
  addConstant<string>(ve, string(REVISION),primString(),SYM(VERSION));

  addVariable<pen>(ve, &processData().currentpen, primPen(), SYM(currentpen));

#ifdef OPENFUNCEXAMPLE
  addOpenFunc(ve, openFunc, primInt(), SYM(openFunc));
#endif

  addOpenFunc(ve, printBytecode, primVoid(), SYM(printBytecode));

  gen_runtime_venv(ve);
  gen_runbacktrace_venv(ve);
  gen_runpicture_venv(ve);
  gen_runlabel_venv(ve);
  gen_runhistory_venv(ve);
  gen_runarray_venv(ve);
  gen_runfile_venv(ve);
  gen_runsystem_venv(ve);
  gen_runstring_venv(ve);
  gen_runpair_venv(ve);
  gen_runtriple_venv(ve);
  gen_runpath_venv(ve);
  gen_runpath3d_venv(ve);
  gen_runmath_venv(ve);

#ifdef HAVE_LIBGSL
  gen_rungsl_venv(ve);
#endif
}

} //namespace trans

namespace run {

double infinity=cbrt(DBL_MAX); // Reduced for tension atleast infinity

void arrayDeleteHelper(stack *Stack)
{
  array *a=pop<array *>(Stack);
  item itj=pop(Stack);
  bool jdefault=isdefault(itj);
  item iti=pop(Stack);
  Int i,j;
  if(isdefault(iti)) {
    if(jdefault) {
      (*a).clear();
      return;
    } else i=j=get<Int>(itj);
  } else {
    i=get<Int>(iti);
    j=jdefault ? i : get<Int>(itj);
  }

  size_t asize=checkArray(a);
  if(a->cyclic() && asize > 0) {
    if(j-i+1 >= (Int) asize) {
      (*a).clear();
      return;
    }
    i=imod(i,asize);
    j=imod(j,asize);
    if(j >= i)
      (*a).erase((*a).begin()+i,(*a).begin()+j+1);
    else {
      (*a).erase((*a).begin()+i,(*a).end());
      (*a).erase((*a).begin(),(*a).begin()+j+1);
    }
    return;
  }

  if(i < 0 || i >= (Int) asize || i > j || j >= (Int) asize) {
    ostringstream buf;
    buf << "delete called on array of length " << (Int) asize
        << " with out-of-bounds index range [" << i << "," << j << "]";
    error(buf);
  }

  (*a).erase((*a).begin()+i,(*a).begin()+j+1);
}

// Used by coder to optimize conditional jumps.
const bltin intLess = binaryOp<Int,less>;
const bltin intGreater = binaryOp<Int,greater>;

}
