/*****
*plain.asy
*Andy Hammerlindl and John Bowman 2004/08/19
*
*A package for general purpose drawing, with automatic sizing of pictures.
*
*****/

access settings;

if(settings.command != "") {
 string s=settings.command;
 settings.command="";
 settings.multipleView=settings.batchView=settings.interactiveView;
 _eval(s+";",false,true);
 exit();
}

include plain_constants;

access version;
if(version.VERSION != VERSION) {
 warning("version","using possibly incompatible version "+
         version.VERSION+" of plain.asy"+'\n');
 nowarn("version");
}

include plain_strings;
include plain_pens;
include plain_paths;
include plain_filldraw;
include plain_margins;
include plain_picture;
include plain_Label;
include plain_arcs;
include plain_boxes;
include plain_shipout;
include plain_markers;
include plain_arrows;
include plain_debugger;

real RELEASE=(real) split(VERSION,"-")[0];

using exitfcn=void();

void updatefunction()
{
 implicitshipout=true;
 if(!currentpicture.uptodate) shipout();
 implicitshipout=false;
}

void exitfunction()
{
 implicitshipout=true;
 if(!currentpicture.empty())
   shipout();
 implicitshipout=false;
}

atupdate(updatefunction);
atexit(exitfunction);

// A restore thunk is a function, that when called, restores the graphics state
// to what it was when the restore thunk was created.
using restoreThunk=void();
using saveFunction=restoreThunk();
saveFunction[] saveFunctions={};

// When save is called, this will be redefined to do the corresponding restore.
void restore()
{
 warning("nomatchingsave","restore called with no matching save");
}

void addSaveFunction(saveFunction s)
{
 saveFunctions.push(s);
}

restoreThunk buildRestoreThunk()
{
 // Call the save functions in reverse order, storing their restore thunks.
 restoreThunk[] thunks={};
 for(int i=saveFunctions.length-1; i >= 0; --i)
   thunks.push(saveFunctions[i]());

 return new void() {
   // Call the restore thunks in an order matching the saves.
   for(int i=thunks.length-1; i >= 0; --i)
     thunks[i]();
 };
}

// Add the default save function.
addSaveFunction(new restoreThunk() {
   pen defaultpen=defaultpen();
   pen p=currentpen;
   picture pic=currentpicture.copy();
   restoreThunk r=restore;
   return new void() {
     defaultpen(defaultpen);
     currentpen=p;
     currentpicture=pic;
     currentpicture.uptodate=false;
     restore=r;
   };
 });

// Save the current state, so that restore will put things back in that state.
restoreThunk save()
{
 return restore=buildRestoreThunk();
}

void restoredefaults()
{
 warning("nomatchingsavedefaults",
         "restoredefaults called with no matching savedefaults");
}

restoreThunk buildRestoreDefaults()
{
 pen defaultpen=defaultpen();
 exitfcn atupdate=atupdate();
 exitfcn atexit=atexit();
 restoreThunk r=restoredefaults;
 return new void() {
   defaultpen(defaultpen);
   atupdate(atupdate);
   atexit(atexit);
   restoredefaults=r;
 };
}

// Save the current state, so that restore will put things back in that state.
restoreThunk savedefaults()
{
 return restoredefaults=buildRestoreDefaults();
}

void initdefaults()
{
 savedefaults();
 resetdefaultpen();
 atupdate(null);
 atexit(null);
}

// Return the sequence n,...,m
int[] sequence(int n, int m)
{
 return n+sequence(m-n+1);
}

// Return the sequence n,...,m skipping by skip
int[] sequence(int n, int m, int skip)
{
 return n+skip*sequence((m-n)#skip+1);
}

int[] reverse(int n) {return sequence(new int(int x){return n-1-x;},n);}
bool[] reverse(bool[] a) {return a[reverse(a.length)];}
int[] reverse(int[] a) {return a[reverse(a.length)];}
real[] reverse(real[] a) {return a[reverse(a.length)];}
pair[] reverse(pair[] a) {return a[reverse(a.length)];}
triple[] reverse(triple[] a) {return a[reverse(a.length)];}
string[] reverse(string[] a) {return a[reverse(a.length)];}

// Return a uniform partition dividing [a,b] into n subintervals.
real[] uniform(real a, real b, int n)
{
 if(n <= 0) return new real[];
 return a+sequence(n+1)/n*(b-a);
}

void eval(string s, bool embedded=false)
{
 if(!embedded) initdefaults();
 _eval(s+";",embedded);
 if(!embedded) restoredefaults();
}

void eval(code s, bool embedded=false)
{
 if(!embedded) initdefaults();
 _eval(s,embedded);
 if(!embedded) restoredefaults();
}

string mapArrayString(string From, string To)
{
 return "typedef "+From+" From;
 typedef "+To+" To;
 To[] map(To f(From), From[] a) {
   return sequence(new To(int i) {return f(a[i]);},a.length);
 }";
}

// This function is deprecated: use
// from mapArray(Src=T1, Dst=T2) access map;
void mapArray(string From, string To)
{
 eval(mapArrayString(From,To),true);
}

// Evaluate user command line option.
void usersetting()
{
 eval(settings.user,true);
}

string stripsuffix(string f, string suffix=".asy")
{
 int n=rfind(f,suffix);
 if(n != -1) f=erase(f,n,-1);
 return f;
}

string outdirectory()
{
 return stripfile(outprefix());
}

// Conditionally process each file name in array s in a new environment.
void asy(string format, bool overwrite=false ... string[] s)
{
 for(string f : s) {
   f=stripsuffix(f);
   string suffix="."+format;
   string fsuffix=stripdirectory(f+suffix);
   if(overwrite || error(input(outdirectory()+fsuffix,check=false))) {
     string outformat=settings.outformat;
     bool interactiveView=settings.interactiveView;
     bool batchView=settings.batchView;
     settings.outformat=format;
     settings.interactiveView=false;
     settings.batchView=false;
     string outname=outname();
     delete(outname+"_"+".aux");
     eval("import \""+f+"\" as dummy");
     rename(stripsuffix(outname)+suffix,fsuffix);
     settings.outformat=outformat;
     settings.interactiveView=interactiveView;
     settings.batchView=batchView;
  }
}
}

void beep()
{
 write('\7',flush);
}

struct processtime {
 real user;
 real system;
 real clock;
}

struct cputime {
 processtime parent;
 processtime child;
 processtime change;
}

cputime cputime()
{
 static processtime last;
 real[] a=_cputime();
 cputime cputime;
 real clock=a[4];
 cputime.parent.user=a[0]; // Includes system time
 cputime.parent.system=0;
 cputime.parent.clock=clock;
 cputime.child.user=a[2];
 cputime.child.system=a[3];
 cputime.child.clock=0;
 real user=cputime.parent.user+cputime.child.user;
 real system=cputime.parent.system+cputime.child.system;
 cputime.change.user=user-last.user;
 cputime.change.system=system-last.system;
 cputime.change.clock=clock-last.clock;
 last.user=user;
 last.system=system;
 last.clock=clock;
 return cputime;
}

string cputimeformat="%#.2f";

void write(file file, string s="", cputime c, string format=cputimeformat,
          suffix suffix=none)
{
 write(file,s,
       format(format,c.change.user)+"u "+
       format(format,c.change.system)+"s "+
       format(format,c.parent.user+c.child.user)+"U "+
       format(format,c.parent.system+c.child.system)+"S ",suffix);
}

void write(string s="", cputime c, string format=cputimeformat,
          suffix suffix=endl)
{
 write(stdout,s,c,format,suffix);
}

struct realschur {
 real[][] U;
 real[][] T;
}

realschur schur(real[][] a)
{
 real[][][] S=_schur(a);
 realschur schur;
 schur.U=S[0];
 schur.T=S[1];
 return schur;
}

struct schur {
 pair[][] U;
 pair[][] T;
}

schur schur(pair[][] a)
{
 pair[][][] S=_schur(a);
 schur schur;
 schur.U=S[0];
 schur.T=S[1];
 return schur;
}

if(settings.autoimport != "") {
 string s=settings.autoimport;
 settings.autoimport="";
 eval("import \""+s+"\" as dummy",true);
 atupdate(updatefunction);
 atexit(exitfunction);
 settings.autoimport=s;
}

cputime();