package tarau.jinni;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Stack;import java.io.Reader;import java.io.Writer;

/**
 This class contains a dictionary of all builtins i.e.
 Java based classes callable from Prolog.
 They should provide a constructor and an exec method.
 @author Paul Tarau
*/
public class Builtins extends HashDict {
  /**
     This constructor registers builtins. Please put a header here
     if you add a builtin at the bottom of this file.
  */
  public Builtins() {
      // add a line here for each new builtin      // basics
      register(new is_builtin());
      register(Const.aTrue);
      register(Const.aFail);
      register(new halt());      register(new compute());

      // I/O and trace related
      register(new get_stdin());
      register(new get_stdout());
      register(new set_max_answers());	    register(new set_trace());
      register(new stack_dump());
      register(new consult());
      register(new reconsult());
      register(new reconsult_again());

      // database      register(new at_key());
      register(new pred_to_string());      register(new db_to_string());            register(new new_db());      register(new get_default_db());
	    register(new db_remove());
      register(new db_add());
      register(new db_collect());      register(new db_source());
       // data structure builders/converters
      register(new arg());
      register(new new_fun());
      register(new get_arity());
      register(new name_to_chars());
      register(new chars_to_name());      register(new numbervars());
            // fluent constructors 
      register(new unfolder_source());
      register(new answer_source());
      
      register(new source_list());
      register(new list_source());
           
      register(new term_source());
      register(new source_term());
      
      register(new integer_source());
      register(new source_loop());      
      // Fluent Modifiers
      
      register(new set_persistent());
      register(new get_persistent());
            // Input Sources
      register(new file_char_reader());
      register(new char_file_writer());
      
      register(new file_clause_reader());
      register(new clause_file_writer());
      
      // writable Sinks
      register(new term_string_collector());
      register(new term_collector());
      
      register(new string_char_reader());
      register(new string_clause_reader());
         
      // fluent controllers
      register(new get());
      register(new put());      register(new stop());
      register(new collect());
    
      // fluent combinators
      register(new split_source());      register(new merge_sources()); 
      // see compose_sources,append_sources,merge_sources in lib.pro            // discharges a Source to a Sink
      register(new discharge());            // multi-var operations      register(new def());      register(new set());      register(new val());               // lazy list operations        register(new source_lazy_list());      register(new lazy_head());      register(new lazy_tail());            // OS and process interface
      register(new system());
      register(new ctime());
  }

  /**
    registers a symbol as name of a builtin
  */
  public void register(Const proto) {
    String key=proto.name()+"/"+proto.getArity();
    //IO.mes("registering builtin: "+key);
    put(key,proto);
  }

  /**
    Creates a new builtin
  */
  Const newBuiltin(Const S) {
    String className=S.name();
    int arity=S.getArity();
    String key=className+"/"+arity;
    Const b=(Const)get(key);
    return b;
  }
} // end Builtins


// Code for actual kernel Builtins:
// add your own builtins in UserBuiltins.java, by cloning the closest example:-)
/**
 * checks if something is a builtin
 */
class is_builtin extends FunBuiltin {
  is_builtin() {
    super("is_builtin",1);
  }

  public int exec(Prog p) {
    return getArg(0).isBuiltin()?1:0;
  }
}

/**
  does its best to halt the program:-)
  to be thoroughly tested with Applets
*/
class halt extends ConstBuiltin {
  halt() {super("halt");}

  public int exec(Prog p) {
    if(IO.applet!=null) { //applet
      IO.peer.halt();
      p.stop();
    }
    else // application
      Runtime.getRuntime().exit(0); 
    return 1;
  }
}
/**
 * Calls an external program
 */
class system extends FunBuiltin {
  system() {
    super("system",1);
  }

  public int exec(Prog p) {
    String cmd=((Const)getArg(0)).name();
    return IO.system(cmd);
  }
}
/**
  opens a reader returning the content of a file char by char
*/
class file_char_reader extends FunBuiltin {
  file_char_reader() {
    super("file_char_reader",2);
  }

  public int exec(Prog p) {    Term I=getArg(0);
    Fluent f;    if(I instanceof CharReader)
       f=new CharReader(((CharReader)I).reader,p);    else {
       String s=((Const)I).name();
       f=new CharReader(s,p);    }
    return putArg(1,f,p);
  }
}

/**
  opens a reader returning clauses from a file
*/
class file_clause_reader extends FunBuiltin {
  file_clause_reader() {
    super("file_clause_reader",2);
  }

  public int exec(Prog p) {
    Term I=getArg(0);
    Fluent f;    if(I instanceof CharReader)
       f=new ClauseReader(((CharReader)I).reader,p);    else {  
       String s=((Const)getArg(0)).name();
       f=new ClauseReader(s,p);    }
    return putArg(1,f,p);
  }
}
/**
  opens a writer which puts characters to a file one by one
*/
class char_file_writer extends FunBuiltin {
  char_file_writer() {
    super("char_file_writer",2);
  }

  public int exec(Prog p) {
    String s=((Const)getArg(0)).name();
    Fluent f=new CharWriter(s,p);
    return putArg(1,f,p);
  }
}
/**
  opens a writer which puts characters to a file one by one
*/
class clause_file_writer extends FunBuiltin {
  clause_file_writer() {
    super("clause_file_writer",2);
  }

  public int exec(Prog p) {
    String s=((Const)getArg(0)).name();
    Fluent f=new ClauseWriter(s,p);
    return putArg(1,f,p);
  }
}
/**
  get the standard output (a reader)
*/
class get_stdin extends FunBuiltin {
  get_stdin() {
    super("get_stdin",1);
  }

  public int exec(Prog p) {
    return putArg(0,new ClauseReader(p),p);
  }
}

/**
  get standard output (a writer)
*/
class get_stdout extends FunBuiltin {
  get_stdout() {
    super("get_stdout",1);
  }

  public int exec(Prog p) {
    return putArg(0,new ClauseWriter(p),p);
  }
}


/**
  gets an arity for any term:
  n>0 for f(A1,...,An)
  0 for a constant like a
  -1 for a variable like X
  -2 for an integer like 13
  -3 for real like 3.14
  -4 for a wrapped JavaObject;
  @see Term#getArity
*/
class get_arity extends FunBuiltin {
  get_arity() {
    super("get_arity",2);
  }

  public int exec(Prog p) {
    Int N=new Int(getArg(0).getArity());
    return putArg(1,N,p);
  }
}
/**
 * Dumps the current Java Stack
 */
class stack_dump extends FunBuiltin {

  stack_dump() {
    super("stack_dump",1);
  }

  public int exec(Prog p) {
    String s=getArg(0).toString();
    IO.errmes("User requested dump",(new Exception(s)));
    return 1;
  }
}

/**
  returns the real time spent up to now
*/
class ctime extends FunBuiltin {

  ctime() {
    super("ctime",1);
  }

  private final static long t0=System.currentTimeMillis();

  public int exec(Prog p) {
    Term T=new Int(System.currentTimeMillis()-t0);  
    return putArg(0,T,p);
  }
}


/**
  sets max answer counter for toplevel query
  if == 0, it will prompt the user for more answers 
  if > 0 it will not print more than IO.maxAnswers
  if < 0 it will print them out all
*/
class set_max_answers extends FunBuiltin {
  set_max_answers() {
    super("set_max_answers",1);
  }

  public int exec(Prog p) {
    IO.maxAnswers=getIntArg(0);
    return 1;
  }
}

/**
  reconsults a file of clauses while overwriting old predicate
  definitions
  @see consult

*/

class reconsult extends FunBuiltin {
  reconsult() {
    super("reconsult",1);
  }

  public int exec(Prog p) {
    String f=((Const)getArg(0)).name();
    return Init.default_db.fromFile(f)?1:0;
  }
}

/**
  consults a file of clauses while adding clauses to
  existing predicate definitions
  @see reconsult
*/
class consult extends FunBuiltin {
  consult() {
    super("consult",1);
  }

  public int exec(Prog p) {
    String f=((Const)getArg(0)).name();
    IO.trace("consulting: "+f);
    return Init.default_db.fromFile(f,false)?1:0;
  }
}

/**
  shorthand for reconsulting the last file
*/
class reconsult_again extends ConstBuiltin  {
  reconsult_again() {
    super("reconsult_again");
  }

  public int exec(Prog p) {
    return Init.default_db.fromFile()?1:0;
  }
}

/**
 * gets default database
 */class get_default_db extends FunBuiltin {
   get_default_db() {     super("get_default_db",1);
   }
   
   public int exec(Prog p) {     return putArg(0,new JavaObject(Init.default_db),p);
   }}

// databse operations

/**
 * creates new database
 */class new_db extends FunBuiltin {
   new_db() {     super("new_db",1);
   }
   
   public int exec(Prog p) {     return putArg(0,new JavaObject(new DataBase()),p);
   }}
/**
  Puts a term on the local blackboard
*/
class db_add extends FunBuiltin {

  db_add() {super("db_add",2);}

  public int exec(Prog p) {
    DataBase db=(DataBase)((JavaObject)getArg(0)).toObject();
    Term X=getArg(1);
    //IO.mes("X==>"+X);
    String key=X.getKey();
    //IO.mes("key==>"+key);
    if(null==key) return 0;
    db.out(key,X);
    //IO.mes("res==>"+R);
    return 1;
  }
}

/**
  removes a matching term if available, fails otherwise
*/
class db_remove extends FunBuiltin  {

  db_remove() {super("db_remove",3);}

  public int exec(Prog p) {    DataBase db=(DataBase)((JavaObject)getArg(0)).toObject();    Term X=getArg(1);
    Term R=db.cin(X.getKey(),X);
    return putArg(2,R,p);
  }
}


/**
  collects all matching terms in a (possibly empty) list
  @see out
  @see in
*/
class db_collect extends FunBuiltin  {

  db_collect() {super("db_collect",3);}

  public int exec(Prog p) {
    DataBase db=(DataBase)((JavaObject)getArg(0)).toObject();    Term X=getArg(1);
    Term R=db.all(X.getKey(),X);
    return putArg(2,R,p);
  }
}

/**
 * Maps a DataBase to a Source enumerating its elements
 */class db_source extends FunBuiltin  {

  db_source() {super("db_source",2);}

  public int exec(Prog p) {
    DataBase db=(DataBase)((JavaObject)getArg(0)).toObject();    Source S=new JavaSource(db.toEnumeration(),p);
    return putArg(1,S,p);
  }
}

/**
  collects all matching terms in a (possibly empty) list
*/
class at_key extends FunBuiltin  {

  at_key() {super("at_key",2);}

  public int exec(Prog p) {
    Term R=Init.default_db.all(getArg(0).getKey(),new Var());
    return putArg(1,R,p);
  }
}
/**
 * Returns a representation of predicate as a string constant
 */
class pred_to_string extends FunBuiltin {

  pred_to_string() {super("pred_to_string",2);}

  public int exec(Prog p) {
    String key=getArg(0).getKey();
    String listing=Init.default_db.pred_to_string(key);
    if(null==listing) return 0;
    Const R=new Const(listing);
    return putArg(1,R,p);
  }
}


/**
  lists all the local blackboard to a string (Linda terms + clauses)
*/
class db_to_string extends FunBuiltin  {
  db_to_string() {super("db_to_string",1);}

  public int exec(Prog p) {
    return putArg(0,new Const(Init.default_db.pprint()),p);
  }
}


/**
  arg(I,Term,X) unifies X with the I-the argument of functor T
*/
class arg extends FunBuiltin {
  arg() {
    super("arg",3);
  }

  public int exec(Prog p) {
     int i=getIntArg(0);
     Fun F=(Fun)getArg(1);
     Term A = 
      (i==0)
        ?new Const(F.name())
        :((i==-1)
           ? new Int(F.getArity())
           : F.args[i-1]
         );
     return putArg(2,A,p);
   }
}

/**
  new_fun(F,N,T) creates a term T based on functor F with arity
  N and new free varables as arguments
*/
class new_fun extends FunBuiltin {
  new_fun() {
    super("new_fun",3);
  }

  public int exec(Prog p) {
     String s=((Const)getArg(0)).name();
     int i=getIntArg(1);
     Term T;
     if(i==0) T=new Const(s).toBuiltin();
     else {
       Fun F=new Fun(s);
       F.init(i);
       T=F.toBuiltin();
     }
     return putArg(2,T,p);
  }
}

/**
  converts a name to a list of chars
*/
class name_to_chars extends FunBuiltin {
  name_to_chars() {
    super("name_to_chars",2);
  }

  public int exec(Prog p) {
     Term Cs=((Nonvar)getArg(0)).toChars();
     return putArg(1,Cs,p);
  }
}

/**
  converts a name to a list of chars
*/
class chars_to_name extends FunBuiltin {
  chars_to_name() {
    super("chars_to_name",3);
  }

  public int exec(Prog p) {
     int convert=getIntArg(0);
     String s=charsToString((Nonvar)getArg(1));
     Nonvar T=new Const(s);     if(convert>0) {
       try {
         double r=Double.valueOf(s).doubleValue();
         if(Math.floor(r)==r) T=new Int((long)r);
         else T=new Real(r);
       }
       catch(NumberFormatException e) {
       }     }
     return putArg(2,T,p);
  }
}

/**
 * returns a copy of a Term with variables uniformly replaced with 
 * constants 
 */class numbervars extends FunBuiltin {
  numbervars() {
    super("numbervars",2);
  }

  public int exec(Prog p) {
     Term T=getArg(0).numbervars();
     return putArg(1,T,p);
  }
}

/**
 * Performs simple arithmetic operations
 * like compute('+',1,2,Result)
 */
class compute extends FunBuiltin {
  compute() {
    super("compute",4);
  }

  public int exec(Prog p) {
    
     Term o=getArg(0); Term a=getArg(1); Term b=getArg(2);
     if(!(o instanceof Const) || !(a instanceof Num) || !(b instanceof Num)) 
       IO.errmes(
         "bad arithmetic operation ("+o+"): "+
         a+","+b+"\nprog: "+p.toString());
	 String opname=((Const)o).name();
     double x=((Num)a).getValue();
     double y=((Num)b).getValue();
     double r;
	   char op=opname.charAt(0);
     switch(op) {
	   case '+':r=x+y;break;
     case '-':r=x-y;break;
	   case '*':r=x*y;break;
	   case '/':r=x/y;break;
     case ':':r=(int)(x/y);break;
     case '%':r=x%y;break;
	   case '?':r=(x<y)?(-1):(x==y?0:1);break; // compares!
     case 'p':r=Math.pow(x,y);break;
     case 'l':r=Math.log(y)/Math.log(x);break;
     case 'r':r=x*Math.random()+y;break;
     case '<':r=(long)x<<(long)y;break;
     case '>':r=(long)x>>(long)y;break;

	   default: 
       IO.errmes("bad arithmetic operation <"+op+
       "> on "+x+" and "+y);
       return 0;
	 }
	 Num R=((Math.floor(r)==r))?(Num)(new Int((long)r)):(Num)(new Real(r));
     return putArg(3,R,p);
  }
}

/**
 * controls trace levels for debugging
 */
class set_trace extends FunBuiltin {
  set_trace() {super("set_trace",1);}

  public int exec(Prog p) {
    Prog.tracing=getIntArg(0);
    return 1;
  }
}

/**
  Explores a finite iterator and return its
  successive values as a list.
*/

class source_list extends FunBuiltin {
  source_list() {
    super("source_list",2);
  }

  public int exec(Prog p) {
    Source S=(Source)getArg(0);
    Term Xs=S.toList();
    return putArg(1,Xs,p);
  }
}

/* maps a Source to a Java Enumeration
class JinniEnumeration extends SystemObject implements Enumeration {
  JinniEnumeration(Source I) {
    this.I=I;
    this.current=this.I.getElement();
  }

  private Source I;
  private Term current;

  public boolean hasMoreElements() {
    if(null==current) {
      I=null;
      return false;
    }
    return true;
  }

  public Object nextElement() {
    Term next=current;
    current=I.getElement();
    return next;
  }
}*/

/**
 * maps a List to a Source
 */
class list_source extends FunBuiltin {

  list_source() {
    super("list_source",2);
  }

  public int exec(Prog p) {
    Source E=new ListSource((Const)getArg(0),p);
    return putArg(1,E,p);
  }
}

/**
 * maps a Term to a Source
 */
class term_source extends FunBuiltin {

  term_source() {
    super("term_source",2);
  }

  public int exec(Prog p) {
    TermSource E=new TermSource((Nonvar)getArg(0),p);
    return putArg(1,E,p);
  }
}

/**
 * Creates an Integer Source which advances at most Fuel (infinite if Fule==0) 
 * Steps computing each time x:= a*x+b. Called as: integer_source(Fuel,A,X,B,NewSource)
 */
class integer_source extends FunBuiltin {

  integer_source() {
    super("integer_source",5);
  }

  public int exec(Prog p) {
    IntegerSource E=new IntegerSource(
       ((Int)getArg(0)).longValue(),
       ((Int)getArg(1)).longValue(),
       ((Int)getArg(2)).longValue(),
       ((Int)getArg(3)).longValue(),
    p);
    return putArg(4,E,p);
  }
}
/**
  Builds a Looping Source from a Source.
*/
class source_loop extends FunBuiltin {
  source_loop() {
    super("source_loop",2);
  }  
  public int exec(Prog p) {
     Source s=(Source)getArg(0);
     return putArg(1,new SourceLoop(s,p),p);
  }
}/**
 * Builds a Source from a Term
 */
class source_term extends FunBuiltin {

  source_term() {
    super("source_term",2);
  }

  public int exec(Prog p) {
    Source S=(Source)getArg(0);
    Term Xs=((Const)S.toFun()).toBuiltin();
    return putArg(1,Xs,p);
  }
}


// Solvers and iterators over clauses

/** * When called as answer_source(X,G,R), it
   builds a new clause and maps it to an AnswerSource    LD-resolution interpreter which will return one answer    at a time of the form "the(X)" using G as initial resolvent   and "no" when no more answers are available.
*/
class answer_source extends FunBuiltin {
  answer_source() {
    super("answer_source",3);
  }

  public int exec(Prog p) {
     Clause goal=new Clause(getArg(0),getArg(1));
     Prog U=new Prog(goal,p);
     return putArg(2,U,p);
  }
}


/**
  Builds a new clause H:-B and maps it to an iterator
*/
class unfolder_source extends FunBuiltin {
  unfolder_source() {
    super("unfolder_source",2);
  }

  public int exec(Prog p) {
     Clause goal=getArg(0).toClause();
     Prog newp=new Prog(goal,p);
     Unfolder S=new Unfolder(goal,newp);
     return putArg(1,S,p);
  }
}
/**
 generic Source advancement step, similar to an iterator's nextElement operation, gets one element from the Source
*/

class get extends FunBuiltin {
  get() {
    super("get",2);
  }

  public int exec(Prog p) {
     //IO.mes("<<"+getArg(0)+"\n"+p+p.getTrail().pprint());
     Source S=(Source)getArg(0);
     Term A=Const.the(S.getElement());
     //if(null==A) A=Const.aNo;
     //else A=new Fun("the",A);
     //IO.mes(">>"+A+"\n"+p+p.getTrail().pprint());
     return putArg(1,A,p);
  }
}

/**
 generic Sink advancement step, sends one element to the Sink 
*/

class put extends FunBuiltin {
  put() {
    super("put",2);
  }

  public int exec(Prog p) {
     Sink S=(Sink)getArg(0);     Term X=getArg(1);
     if(0==S.putElement(X)) {
       IO.errmes("error in putElement: "+X);     }     return 1;
  }
}


/**
  frees a Fluent's resources and ensures
  it cannot produce/consume any new values
*/
class stop extends FunBuiltin {
  stop() {
    super("stop",1);
  }

  public int exec(Prog p) {
     Fluent S=(Fluent)getArg(0);
     S.stop();
     return 1;
  }
}


/**
  Splits a (finite) Source in two new ones
  which inherit the current state of the parent.
*/
class split_source extends FunBuiltin {
  split_source() {
    super("split_source",3);
  }

  public int exec(Prog p) {
     Source original=(Source)getArg(0);
     Const Xs=original.toList();
     return 
       ( putArg(1,new ListSource(Xs,p),p)>0 &&
         putArg(2,new ListSource(Xs,p),p)>0
       )
       ?1
       :0;
  }
}


/**
  Merges all Sources contained in a List into one Source.
*/
class merge_sources extends FunBuiltin {
  merge_sources() {
    super("merge_sources",2);
  } 
  public int exec(Prog p) {
     Const list=(Const)getArg(0);
     return putArg(1,new SourceMerger(list,p),p);
  }
}

/**
  Flushes to a Sink the content of a Source Fluent
*/
class discharge extends FunBuiltin {
  discharge() {
    super("discharge",2);
  }

  public int exec(Prog p) {
     Source from=(Source)getArg(0);
     Sink to=(Sink)getArg(1);
     for(;;) {
       Term X=from.getElement();
       if(null==X) {
         to.stop();
         break;
       }
       else
         to.putElement(X);
     }
     return 1;
  }
}

/**
  Collects a reference to or the content of a Sink
*/

class collect extends FunBuiltin {
  collect() {
    super("collect",2);
  }

  public int exec(Prog p) {
     Sink s=(Sink)getArg(0);
     Term X=s.collect();
     if(null==X) X=Const.aNo;
     else X=new Fun("the",X);     return putArg(1,X,p);
  }
}
/**
 * Builds a StringSink which concatenates String representations
 * of Terms with put/1 and the return their concatenation with collect/1
 */
class term_string_collector extends FunBuiltin {
  term_string_collector() {
    super("term_string_collector",1);
  }

  public int exec(Prog p) {     return putArg(0,new StringSink(p),p);
  }
}
/**
 * Builds a TermCollector Sink which accumulates
 * Terms with put/1 and the return them with collect/1
 */
class term_collector extends FunBuiltin {
  term_collector() {
    super("term_collector",1);
  }

  public int exec(Prog p) {     return putArg(0,new TermCollector(p),p);
  }
}
/**
 * Creates a char reader from a String.
 */
class string_char_reader extends FunBuiltin {
  string_char_reader() {
    super("string_char_reader",2);
  }

  public int exec(Prog p) {     return putArg(1,new CharReader(getArg(0),p),p);
  }
}
/**
 *  Creates a clause reader from a String.
 */
class string_clause_reader extends FunBuiltin {
  string_clause_reader() {
    super("string_clause_reader",2);
  }

  public int exec(Prog p) {     return putArg(1,new ClauseReader(getArg(0),p),p);
  }
}
/**
 * def(Var,Val) Initializes a Multi_Variable Var to a value Val.
 */
class def extends FunBuiltin {
  def() {
    super("def",2);
  }

  public int exec(Prog p) {     Var X=(Var)getArg(0);
     MultiVar V=new MultiVar(getArg(1),p);
     X.bind_to(V,p.getTrail());     return 1;
  }
}
/**
 * set(Var,Val) Sets a Multi_Variable Var to a value Val.
 */
class set extends FunBuiltin {
  set() {
    super("set",2);
  }

  public int exec(Prog p) {
     MultiVar V=(MultiVar)getArg(0);
     V.set(getArg(1),p);     return 1;
  }
}
/**
 * val(Var,Val) gets the value Val of Multi_Variable Var.
 */
class val extends FunBuiltin {
  val() {
    super("val",2);
  }

  public int exec(Prog p) {
     MultiVar V=(MultiVar)getArg(0);
     return putArg(1,V.val(),p);
  }
}

/**
 * set_persistent(Fluent,yes)
 * makes a Fluent persistent - i.e. likely to keep
 * its state on backtracking. This assumes that the
 * Fluent remains accessible by being saved in a Database
 * or as element of a Fluent with longer life span.
 * 
 * set_persistent(Fluent,no) makes the Fluent perish
 * on backtracking (default behavior)
 */class set_persistent extends FunBuiltin {
  set_persistent() {
    super("set_persistent",2);
  }

  public int exec(Prog p) {
     Fluent F=(Fluent)getArg(0);     Const R=(Const)getArg(1);     boolean yesno=!R.eq(Const.aNo);
     F.setPersistent(yesno);     return 1;
  }
}
/**
 * Gets the yes/no persistentcy value of a Fluent.
 */
class get_persistent extends FunBuiltin {
  get_persistent() {
    super("get_persistent",2);
  }

  public int exec(Prog p) {
     Fluent F=(Fluent)getArg(0);     Term R=F.getPersistent()?Const.aYes:Const.aNo;
     return putArg(1,R,p);
  }
}

/**
 * Converts Source into a Lazy List which will memorize
 * its elements as it grows.
 */
class source_lazy_list extends FunBuiltin {
  source_lazy_list() {
    super("source_lazy_list",2);
  }

  public int exec(Prog p) {
    Source S=(Source)getArg(0);    //S.setPersistent(true);
    Term X=S.getElement();
    Term Xs=Const.aNil;
    if(null!=X) {
      Xs=new LazyList(X,S,new Trail());
      p.getTrail().push(Xs);    }
    return putArg(1,Xs,p);
  }
}
/**
 * returns the first element of a lazy list
 */
class lazy_head extends FunBuiltin {
   lazy_head() {
    super("lazy_head",2);
   }

  public int exec(Prog p) {
     Cons L=(Cons)getArg(0);
     return putArg(1,L.getHead(),p);
  }
}
/**
 * returns the tail if a lazy list after making it
 * grow, if possible
 */
class lazy_tail extends FunBuiltin {
   lazy_tail() {
     super("lazy_tail",2);
  }

  public int exec(Prog p) {
     Cons L=(Cons)getArg(0);
     return putArg(1,L.getTail(),p);
  }
}
