/*****
 * runtime.in
 * Tom Prince 2005/4/15
 *
 * Generate the runtime functions used by the vm::stack machine.
 *
 *****/

/* Autogenerated routines are specified like this (separated by a formfeed):
   type asyname:cname(cparams)
   {
   C code
   }

*/

// Use Void f() instead of void f() to force an explicit Stack argument.


pen      => primPen()
pair     => primPair()
triple   => primTriple()
path     => primPath()
path3     => primPath3()
guide*   => primGuide()
cycleToken => primCycleToken()
tensionSpecifier => primTensionSpecifier()
curlSpecifier => primCurlSpecifier()
file*    => primFile()
picture* => primPicture()
transform => primTransform()
callable* => voidFunction()
runnable* => primCode()
boolarray* => booleanArray()
Intarray*  => IntArray()
Intarray2*  => IntArray2()
realarray* => realArray()
realarray2* => realArray2()
pairarray* => pairArray()
pairarray2* => pairArray2()
triplearray* => tripleArray()
triplearray2* => tripleArray2()
patharray* => pathArray()
patharray2* => pathArray2()
guidearray* => guideArray()
transformarray* => transformArray()
penarray* => penArray()
penarray2* => penArray2()
stringarray* => stringArray()
stringarray2* => stringArray2()

#include <cfloat>
#include <iostream>
#include <fstream>
#include <time.h>
#include <sys/times.h>
#include <locale.h>

#include "angle.h"
#include "pair.h"
#include "triple.h"
#include "transform.h"
#include "path.h"
#include "path3.h"
#include "pen.h"
#include "drawpath.h"
#include "guide.h"
#include "picture.h"
#include "fileio.h"
#include "genv.h"
#include "builtin.h"
#include "texfile.h"
#include "pipestream.h"
#include "parser.h"
#include "stack.h"
#include "util.h"
#include "locate.h"
#include "mathop.h"
#include "callable.h"
#include "stm.h"
#include "lexical.h"
#include "process.h"
#include "arrayop.h"
#include "seconds.h"

#if defined(USEGC) && defined(GC_DEBUG) && defined(GC_BACKTRACE)
  extern "C" {
    void *GC_generate_random_valid_address(void);
    void GC_debug_print_heap_obj_proc(void *);
  }
#endif

using namespace vm;
using namespace camp;
using namespace settings;
using namespace utils;

#undef OUT
#undef IN

namespace run {
using camp::pair;
using vm::array;
using vm::frame;
using vm::stack;
using camp::transform;
using absyntax::runnable;

typedef array boolarray;
typedef array Intarray;
typedef array Intarray2;
typedef array realarray;
typedef array realarray2;
typedef array pairarray;
typedef array pairarray2;
typedef array triplearray;
typedef array triplearray2;
typedef array patharray;
typedef array patharray2;
typedef array guidearray;
typedef array transformarray;
typedef array penarray;
typedef array penarray2;
typedef array stringarray;
typedef array stringarray2;
}

using vm::array;
using types::function;

#define PRIMITIVE(name,Name,asyName) using types::prim##Name;
#include <primitives.h>
#undef PRIMITIVE

using types::booleanArray;
using types::IntArray;
using types::IntArray2;
using types::realArray;
using types::realArray2;
using types::pairArray;
using types::pairArray2;
using types::tripleArray;
using types::tripleArray2;
using types::pathArray;
using types::pathArray2;
using types::guideArray;
using types::transformArray;
using types::penArray;
using types::penArray2;
using types::stringArray;
using types::stringArray2;
using types::formal;

function *realRealFunction()
{
  return new function(primReal(),primReal());
}

function *realTripleFunction()
{
  return new function(primReal(),primTriple());
}

const size_t camp::ColorComponents[]={0,0,1,3,4,0};

namespace vm {
#if COMPACT
const Int DefaultValue=0x7fffffffffffffffLL;
const Int Undefined=0x7ffffffffffffffeLL;

const Int BoolTruthValue=0xABABABABABABABACLL;
const Int BoolFalseValue=0xABABABABABABABABLL;

const item Default=DefaultValue;
#else
const item Default=item(default_t());
#endif
}

namespace run {

stopWatch wallClock;

const char *arrayempty="cannot take min or max of empty array";
const char *noruntime="no runtime environment for embedded eval";

void writestring(stack *s)
{
  callable *suffix=pop<callable *>(s,NULL);
  string S=pop<string>(s);
  vm::item it=pop(s);
  bool defaultfile=isdefault(it);
  camp::ofile *f=defaultfile ? &camp::Stdout : vm::get<camp::ofile*>(it);
  if(!f->isOpen() || !f->enabled()) return;
  if(S != "") f->write(S);
  if(f->text()) {
    if(suffix) {
      s->push(f);
      suffix->call(s);
    } else if(defaultfile) f->writeline();
  }
}

string toplocation()
{
  ostringstream buf;
  position& topPos=processData().topPos;
  buf << topPos.Line() << "." << topPos.Column();
  return buf.str();
}

string emptystring;
pair zero;

}

static string defaulttransparency=string("Compatible");

void unused(void *)
{
}

// Autogenerated routines:



// Initializers

Int :IntZero()
{
  return 0;
}

real :realZero()
{
  return 0.0;
}

bool :boolFalse()
{
  return false;
}

bool isnan(real x)
{
  return std::isnan(x);
}

array* :pushNullArray()
{
  return 0;
}

frame* :pushNullRecord()
{
  return 0;
}

item :pushNullFunction()
{
  return nullfunc::instance();
}


// Default operations

// Put the default value token on the stack (in place of an argument when
// making a function call).
item :pushDefault()
{
  return Default;
}


// Test if the value on the stack is the default value token.
bool :isDefault(item i)
{
  return isdefault(i);
}


// Casts

guide* :pairToGuide(pair z)
{
  return new pairguide(z);
}

guide* :pathToGuide(path p)
{
  return new pathguide(p);
}

path :guideToPath(guide *g)
{
  return g->solve();
}


// Pen operations

pen :newPen()
{
  return pen();
}

bool ==(pen a, pen b)
{
  return a == b;
}

bool !=(pen a, pen b)
{
  return a != b;
}

pen +(pen a, pen b)
{
  return a+b;
}

pen Operator *(real a, pen b)
{
  return a*b;
}

pen Operator *(pen a, real b)
{
  return b*a;
}

pair max(pen p)
{
  return p.bounds().Max();
}

pair min(pen p)
{
  return p.bounds().Min();
}

// Reset the meaning of pen default attributes.
void resetdefaultpen()
{
  processData().defaultpen=camp::pen::initialpen();
}

void defaultpen(pen p)
{
  processData().defaultpen=pen(resolvepen,p);
}

pen defaultpen()
{
  return processData().defaultpen;
}

bool invisible(pen p)
{
  return p.invisible();
}

pen invisible()
{
  return pen(invisiblepen);
}

pen gray(pen p)
{
  p.togrey();
  return p;
}

pen rgb(pen p)
{
  p.torgb();
  return p;
}

pen cmyk(pen p)
{
  p.tocmyk();
  return p;
}

pen interp(pen a, pen b, real t)
{
  return interpolate(a,b,t);
}

pen rgb(real r, real g, real b)
{
  return pen(r,g,b);
}

pen cmyk(real c, real m, real y, real k)
{
  return pen(c,m,y,k);
}

pen gray(real gray)
{
  return pen(gray);
}

realarray *colors(pen p)
{
  size_t n=ColorComponents[p.colorspace()];
  array *a=new array(n);

  switch(n) {
    case 0:
      break;
    case 1:
      (*a)[0]=p.gray();
      break;
    case 3:
      (*a)[0]=p.red();
      (*a)[1]=p.green();
      (*a)[2]=p.blue();
      break;
    case 4:
      (*a)[0]=p.cyan();
      (*a)[1]=p.magenta();
      (*a)[2]=p.yellow();
      (*a)[3]=p.black();
      break;
    default:
      break;
  }
  return a;
}

string hex(pen p)
{
  return p.hex();
}

Int byte(real x)
{
  return camp::byte(x);
}

real byteinv(Int x)
{
  return x >= 0 ? camp::byteinv(x) : 0.0;
}

string colorspace(pen p)
{
  string s=ColorDeviceSuffix[p.colorspace()];
  std::transform(s.begin(),s.end(),s.begin(),tolower);
  return s;
}

pen pattern(string *s)
{
  return pen(setpattern,*s);
}

string pattern(pen p)
{
  return p.fillpattern();
}

pen fillrule(Int n)
{
  return pen(n >= 0 && n < nFill ? (FillRule) n : DEFFILL);
}

Int fillrule(pen p)
{
  return p.Fillrule();
}

pen opacity(real opacity=1.0, string blend=defaulttransparency)
{
  for(Int i=0; i < nBlendMode; ++i)
    if(blend == BlendMode[i]) return pen(Transparency(blend,opacity));

  ostringstream buf;
  buf << "Unknown blend mode: " << "'" << blend << "'";
  error(buf);
}

real opacity(pen p)
{
  return p.opacity();
}

string blend(pen p)
{
  return p.blend();
}

pen linetype(realarray *pattern, real offset=0, bool scale=true,
             bool adjust=true)
{
  size_t size=checkArray(pattern);
  array *a=new array(size);
  for(size_t i=0; i < size; ++i)
    (*a)[i]=::max(vm::read<double>(pattern,i),0.0);

  return pen(LineType(*a,offset,scale,adjust));
}

realarray *linetype(pen p=CURRENTPEN)
{
  array a=p.linetype()->pattern;
  return copyArray(&a);
}

real offset(pen p)
{
  return p.linetype()->offset;
}

bool scale(pen p)
{
  return p.linetype()->scale;
}

bool adjust(pen p)
{
  return p.linetype()->adjust;
}

pen adjust(pen p, real arclength, bool cyclic)
{
  return adjustdash(p,arclength,cyclic);
}

pen linecap(Int n)
{
  return pen(setlinecap,n >= 0 && n < nCap ? n : DEFCAP);
}

Int linecap(pen p=CURRENTPEN)
{
  return p.cap();
}

pen linejoin(Int n)
{
  return pen(setlinejoin,n >= 0 && n < nJoin ? n : DEFJOIN);
}

Int linejoin(pen p=CURRENTPEN)
{
  return p.join();
}

pen miterlimit(real x)
{
  return pen(setmiterlimit,x >= 1.0 ? x : DEFJOIN);
}

real miterlimit(pen p=CURRENTPEN)
{
  return p.miter();
}

pen linewidth(real x)
{
  return pen(setlinewidth,x >= 0.0 ? x : DEFWIDTH);
}

real linewidth(pen p=CURRENTPEN)
{
  return p.width();
}

pen fontcommand(string *s)
{
  return pen(setfont,*s);
}

string font(pen p=CURRENTPEN)
{
  return p.Font();
}

pen fontsize(real size, real lineskip)
{
  return pen(setfontsize,size > 0.0 ? size : 0.0,
             lineskip > 0.0 ? lineskip : 0.0);
}

real fontsize(pen p=CURRENTPEN)
{
  return p.size();
}

real lineskip(pen p=CURRENTPEN)
{
  return p.Lineskip();
}

pen overwrite(Int n)
{
  return pen(setoverwrite,n >= 0 && n < nOverwrite ? (overwrite_t) n :
             DEFWRITE);
}

Int overwrite(pen p=CURRENTPEN)
{
  return p.Overwrite();
}

pen basealign(Int n)
{
  return pen(n >= 0 && n < nBaseLine ? (BaseLine) n : DEFBASE);
}

Int basealign(pen p=CURRENTPEN)
{
  return p.Baseline();
}

transform transform(pen p)
{
  return p.getTransform();
}

path nib(pen p)
{
  return p.Path();
}

pen makepen(path p)
{
  return pen(p);
}

pen colorless(pen p)
{
  p.colorless();
  return p;
}

// Interactive mode

bool interactive()
{
  return interact::interactive;
}

bool uptodate()
{
  return interact::uptodate;
}


// System commands

Int system(stringarray *s)
{
  if(safe) error("system() call disabled; override with option -nosafe");
  size_t size=checkArray(s);
  if(size == 0) return 0;
  mem::vector<string> cmd;
  for(size_t i=0; i < size; ++i)
    cmd.push_back(read<string>(s,i));
  return System(cmd);
}

bool view()
{
  return view();
}

string asydir()
{
  return systemDir;
}

string locale(string s=emptystring)
{
  char *L=setlocale(LC_ALL,s.empty() ? NULL : s.c_str());
  return L != NULL ? string(L) : "";
}

void abort(string s=emptystring)
{
  if(s.empty()) throw handled_error();
  error(s.c_str());
}

void exit()
{
  throw quit();
}

void assert(bool b, string s=emptystring)
{
  flush(cout);
  if(!b) {
    ostringstream buf;
    buf << "assert FAILED";
    if(s != "") buf << ": " << s;
    error(buf);
  }
}

void sleep(Int seconds)
{
  if(seconds <= 0) return;
  sleep(seconds);
}

void usleep(Int microseconds)
{
  if(microseconds <= 0) return;
  usleep((unsigned long) microseconds);
}

void _eval(string *s, bool embedded, bool interactiveWrite=false)
{
  if(embedded) {
    trans::coenv *e=Stack->getEnvironment();
    vm::interactiveStack *is=dynamic_cast<vm::interactiveStack *>(Stack);
    if(e && is)
      runStringEmbedded(*s, *e, *is);
    else
      error(noruntime);
  } else
    runString(*s,interactiveWrite);
}

void _eval(runnable *s, bool embedded)
{
  absyntax::block *ast=new absyntax::block(s->getPos(), false);
  ast->add(s);

  if(embedded) {
    trans::coenv *e=Stack->getEnvironment();
    vm::interactiveStack *is=dynamic_cast<vm::interactiveStack *>(Stack);
    if(e && is)
      runCodeEmbedded(ast, *e, *is);
    else
      error(noruntime);
  } else
    runCode(ast);
}

string xasyKEY() {
  processDataStruct& P=processData();
  xkey_t& xkey=P.xkey;
  xkey_t::iterator p=xkey.find(P.topPos.LineColumn());
  return p != xkey.end() ? p->second+" 1" : toplocation()+" 0";
}

void xasyKEY(string *s) {
  processData().KEY=*s;
}

string location() {
  ostringstream buf;
  buf << getPos();
  return buf.str();
}

// Wrapper for the stack::load() method.
void :loadModule(string *index)
{
  Stack->load(*index);
}

string cd(string s=emptystring)
{
  if(!globalread()) readDisabled();
  if(!s.empty() && !globalwrite()) {
    string outname=settings::outname();
    string dir=stripFile(outname);
    if(dir.empty()) Setting("outname")=getPath()+dirsep+outname;
  }
  return setPath(s.c_str());
}

void list(string *s, bool imports=false)
{
  if(*s == "-") return;
  trans::genv ge;
  symbol name=symbol::trans(*s);
  record *r=ge.getModule(name,*s);
  r->e.list(imports ? 0 : r);
}


// Guide operations

guide* :nullGuide()
{
  return new pathguide(path());
}

guide* :dotsGuide(guidearray *a)
{
  guidevector v;
  size_t size=checkArray(a);
  for (size_t i=0; i < size; ++i)
    v.push_back(a->read<guide*>(i));

  return new multiguide(v);
}

guide* :dashesGuide(guidearray *a)
{
  static camp::curlSpec curly;
  static camp::specguide curlout(&curly, camp::OUT);
  static camp::specguide curlin(&curly, camp::IN);

  size_t n=checkArray(a);

  // a--b is equivalent to a{curl 1}..{curl 1}b
  guidevector v;
  if (n > 0)
    v.push_back(a->read<guide*>(0));

  if (n==1) {
    v.push_back(&curlout);
    v.push_back(&curlin);
  }
  else
    for (size_t i=1; i<n; ++i) {
      v.push_back(&curlout);
      v.push_back(&curlin);
      v.push_back(a->read<guide*>(i));
    }

  return new multiguide(v);
}

cycleToken :newCycleToken()
{
  return cycleToken();
}

guide *operator cast(cycleToken tok)
{
// Avoid unused variable warning messages.
  unused(&tok);
  return new cycletokguide();
}

guide* operator spec(pair z, Int p)
{
  camp::side d=(camp::side) p;
  camp::dirSpec *sp=new camp::dirSpec(z);

  return new specguide(sp,d);
}

curlSpecifier operator curl(real gamma, Int p)
{
  camp::side s=(camp::side) p;
  return curlSpecifier(gamma,s);
}

real :curlSpecifierValuePart(curlSpecifier spec)
{
  return spec.getValue();
}

Int :curlSpecifierSidePart(curlSpecifier spec)
{
  return spec.getSide();
}

guide *operator cast(curlSpecifier spec)
{
  return new specguide(spec);
}

tensionSpecifier operator tension(real tout, real tin, bool atleast)
{
  return tensionSpecifier(tout, tin, atleast);
}

real :tensionSpecifierOutPart(tensionSpecifier t)
{
  return t.getOut();
}

real :tensionSpecifierInPart(tensionSpecifier t)
{
  return t.getIn();
}

bool :tensionSpecifierAtleastPart(tensionSpecifier t)
{
  return t.getAtleast();
}

guide *operator cast(tensionSpecifier t)
{
  return new tensionguide(t);
}

guide* operator controls(pair zout, pair zin)
{
  return new controlguide(zout, zin);
}

Int size(guide *g)
{
  flatguide f;
  g->flatten(f,false);
  return f.size();
}

Int length(guide *g)
{
  flatguide f;
  g->flatten(f,false);
  return g->cyclic() ? f.size() : f.size()-1;
}

bool cyclic(guide *g)
{
  flatguide f;
  g->flatten(f,false);
  return g->cyclic();
}

pair point(guide *g, Int t)
{
  flatguide f;
  g->flatten(f,false);
  return f.Nodes(adjustedIndex(t,f.size(),g->cyclic())).z;
}

pairarray *dirSpecifier(guide *g, Int t)
{
  flatguide f;
  g->flatten(f,false);
  Int n=f.size();
  if(!g->cyclic() && (t < 0 || t >= n-1)) return new array(0);
  array *c=new array(2);
  (*c)[0]=f.Nodes(t).out->dir();
  (*c)[1]=f.Nodes(t+1).in->dir();
  return c;
}

pairarray *controlSpecifier(guide *g, Int t)
{
  flatguide f;
  g->flatten(f,false);
  Int n=f.size();
  if(!g->cyclic() && (t < 0 || t >= n-1)) return new array(0);
  knot curr=f.Nodes(t);
  knot next=f.Nodes(t+1);
  if(curr.out->controlled()) {
    assert(next.in->controlled());
    array *c=new array(2);
    (*c)[0]=curr.out->control();
    (*c)[1]=next.in->control();
    return c;
  } else return new array(0);
}

tensionSpecifier tensionSpecifier(guide *g, Int t)
{
  flatguide f;
  g->flatten(f,false);
  Int n=f.size();
  if(!g->cyclic() && (t < 0 || t >= n-1)) return tensionSpecifier(1.0,1.0,false);
  knot curr=f.Nodes(t);
  return tensionSpecifier(curr.tout.val,f.Nodes(t+1).tin.val,curr.tout.atleast);
}

realarray *curlSpecifier(guide *g, Int t)
{
  flatguide f;
  g->flatten(f,false);
  Int n=f.size();
  if(!g->cyclic() && (t < 0 || t >= n-1)) return new array(0);
  array *c=new array(2);
  real c0=f.Nodes(t).out->curl();
  real c1=f.Nodes(t+1).in->curl();
  (*c)[0]=c0 >= 0.0 ? c0 : 1.0;
  (*c)[1]=c1 >= 0.0 ? c1 : 1.0;
  return c;
}

guide *reverse(guide *g)
{
  flatguide f;
  g->flatten(f,false);
  if(f.precyclic())
    return new pathguide(g->solve().reverse());

  size_t n=f.size();
  bool cyclic=g->cyclic();
  guidevector v;
  size_t start=cyclic ? n : n-1;
  knot curr=f.Nodes(start);
  knot next=curr;
  for(size_t i=start; i > 0; --i) {
    next=f.Nodes(i-1);
    v.push_back(new pairguide(curr.z));
    if(next.out->controlled()) {
      assert(curr.in->controlled());
      v.push_back(new controlguide(curr.in->control(),next.out->control()));
    } else {
      pair d=curr.in->dir();
      if(d != zero)
        v.push_back(new specguide(new dirSpec(-d),camp::OUT));
      else {
        real C=curr.in->curl();
        if(C >= 0.0)
          v.push_back(new specguide(new curlSpec(C),camp::OUT));
      }
      real tout=curr.tin.val;
      real tin=next.tout.val;
      bool atleast=next.tout.atleast;
      if(tout != 1.0 || tin != 1.0 || next.tout.atleast)
        v.push_back(new tensionguide(tensionSpecifier(tout,tin,atleast)));
      d=next.out->dir();
      if(d != zero)
        v.push_back(new specguide(new dirSpec(-d),camp::IN));
      else {
        real C=next.out->curl();
        if(C >= 0.0)
          v.push_back(new specguide(new curlSpec(C),camp::IN));
      }
    }
    curr=next;
  }
  if(cyclic)
    v.push_back(new cycletokguide());
  else
    v.push_back(new pairguide(next.z));

  return new multiguide(v);
}


realarray *_cputime()
{
  static const real ticktime=1.0/sysconf(_SC_CLK_TCK);
  struct tms buf;

  ::times(&buf);
  array *t=new array(5);
  (*t)[0]=((real) buf.tms_utime)*ticktime;
  (*t)[1]=((real) buf.tms_stime)*ticktime;
  (*t)[2]=((real) buf.tms_cutime)*ticktime;
  (*t)[3]=((real) buf.tms_cstime)*ticktime;
  (*t)[4]=wallClock.seconds();
  return t;
}


// Transforms

bool ==(transform a, transform b)
{
  return a == b;
}

bool !=(transform a, transform b)
{
  return a != b;
}

transform +(transform a, transform b)
{
  return a+b;
}

transform Operator *(transform a, transform b)
{
  return a*b;
}

pair Operator *(transform t, pair z)
{
  return t*z;
}

path Operator *(transform t, path g)
{
  return transformed(t,g);
}

pen Operator *(transform t, pen p)
{
  return transformed(t,p);
}

picture * Operator *(transform t, picture *f)
{
  return transformed(t,f);
}

picture * Operator *(realarray2 *t, picture *f)
{
  return transformed(*t,f);
}

transform ^(transform t, Int n)
{
  transform T;
  if(n < 0) {
    n=-n;
    t=inverse(t);
  }
  for(Int i=0; i < n; i++) T=T*t;
  return T;
}

real :transformXPart(transform t)
{
  return t.getx();
}

real :transformYPart(transform t)
{
  return t.gety();
}

real :transformXXPart(transform t)
{
  return t.getxx();
}

real :transformXYPart(transform t)
{
  return t.getxy();
}

real :transformYXPart(transform t)
{
  return t.getyx();
}

real :transformYYPart(transform t)
{
  return t.getyy();
}

transform :real6ToTransform(real x, real y, real xx, real xy,
                            real yx, real yy)
{
  return transform(x,y,xx,xy,yx,yy);
}

transform shift(transform t)
{
  return transform(t.getx(),t.gety(),0,0,0,0);
}

transform shiftless(transform t)
{
  return transform(0,0,t.getxx(),t.getxy(),t.getyx(),t.getyy());
}

transform identity:transformIdentity()
{
  return identity;
}

transform inverse(transform t)
{
  return inverse(t);
}

transform shift(pair z)
{
  return shift(z);
}

transform shift(real x, real y)
{
  return shift(pair(x,y));
}

transform xscale(real x)
{
  return xscale(x);
}

transform yscale(real y)
{
  return yscale(y);
}

transform scale(real x)
{
  return scale(x);
}

transform scale(real x, real y)
{
  return scale(x,y);
}

transform slant(real s)
{
  return slant(s);
}

transform rotate(real angle, pair z=0)
{
  return rotatearound(z,radians(angle));
}

transform reflect(pair a, pair b)
{
  return reflectabout(a,b);
}

bool isometry(transform t)
{
  return t.isIsometry();
}

real bezier(real a, real b, real c, real d, real t)
{
  real onemt=1-t;
  real onemt2=onemt*onemt;
  return onemt2*onemt*a+t*(3.0*(onemt2*b+t*onemt*c)+t*t*d);
}
