

Function("Contains",{list,element})
[
  Local(result);
  result:=False;
  While(Not(result) And list != {})
  [
    If(Head(list) = element,
      result := True,
      list:=Tail(list)
      );
  ];
  result;
];

Function("Find",{list,element})
[
  Local(result,count);
  result:= -1;
  count:=1;
  While(result<0 And list != {})
  [
    If(Head(list) = element,
      result := count
      );
    list:=Tail(list);
    count++;
  ];
  result;
];


Function("Append",{list,element})
[
  Insert(list,Length(list)+1,element);
];
Function("DestructiveAppend",{list,element})
[
  DestructiveInsert(list,Length(list)+1,element);
];

Function("RemoveDuplicates",{list})
[
   Local(result);
   result:={};
   ForEach(item,list)
     If(Not(Contains(result,item)),DestructiveAppend(result,item));
   result;
];

Function("Union",{list1,list2}) RemoveDuplicates(Concat(list1,list2));

Function("Intersection",{list1,list2})
[
  Local(result);
  result:={};
  ForEach(item,list1)
    If(Contains(list2,item),DestructiveAppend(result,item));
  result;
];

Function("Difference",{list1,list2})
[
  Local(result);
  result:={};
  ForEach(item,list1)
    If(Not(Contains(list2,item)),DestructiveAppend(result,item));
  result;
];

Function("Push",{stack,element})
[
  DestructiveInsert(stack,1,element);
];

Function("Pop",{stack,index})
[
  Local(result);
  result:=stack[[index]];
  DestructiveDelete(stack,index);
  result;
];

Function("PopFront",{stack}) Pop(stack,1);
Function("PopBack",{stack})  Pop(stack,Length(stack));

Function("Swap",{list,index1,index2})
[
  Local(item1,item2);
  item1:=list[[index1]];
  item2:=list[[index2]];
/*TODO old
  DestructiveDelete(list,index1);
  DestructiveInsert(list,index1,item2);
  DestructiveDelete(list,index2);
  DestructiveInsert(list,index2,item1);
*/
  DestructiveReplace(list,index1,item2);
  DestructiveReplace(list,index2,item1);
];


Function("Count",{list,element})
[
   Local(result);
   result:=0;
   ForEach(item,list) If(item = element, result++);
   result;

];

Function("BubbleSort",{list,compare})
[
  Local(i,j,length,left,right);

  list:=FlatCopy(list);
  length:=Length(list);

  For (j:=length,j>1,j--)
  [
    For(i:=1,i<j,i++)
    [
      left:=list[[i]];
      right:=list[[i+1]];
      If(Not(Apply("Apply",{compare,{left,right}})),
        [
          DestructiveInsert(DestructiveDelete(list,i),i+1,left);
        ]
      );
    ];
  ];
  list;
];


/* VarList: return the variables this expression depends on. */
Function("VarList",{expr})  RemoveDuplicates(VarListAll(expr));

/*
 * RuleBase for VarListAll: recursively traverse na expression looking
 * up all variables the expression depends on.
 */
RuleBase("VarListAll",{expr});

/* Accept any variable. */
Rule("VarListAll",1,0,IsAtom(expr) And Not(IsNumber(expr))) {expr};

/* Otherwise check all leafs of a function. */
Rule("VarListAll",1,1,IsFunction(expr))
[
  Local(item,result, flatlist);
  flatlist:=Tail(Listify(expr));
  result:={};
  ForEach(item,flatlist)
    result:=Concat(result,VarListAll(item));
  result;
];

/* Else it doesn't depend on any variable. */
Rule("VarListAll",1,2,True) {};


Function("Table",{tablebody,tablevar,tablefrom,tableto,tablestep})
[
  MacroLocal(tablevar);
  Local(tableresult);
  MacroSet(tablevar,tablefrom);
  tableresult:={};
  While(Eval(tablevar) <= tableto)
  [
    DestructiveInsert(tableresult,1,Eval(tablebody));
    MacroSet(tablevar,Eval(tablevar)+tablestep);
  ];
  DestructiveReverse(tableresult);
];
HoldArg("Table",tablebody);
HoldArg("Table",tablevar);
UnFence("Table",5);

Function("MapSingle",{func,list})
[
  Local(mapsingleresult);
  mapsingleresult:={};
  ForEach(mapsingleitem,list)
  [
    DestructiveInsert(mapsingleresult,1,
    Apply("Apply",{func,{Hold(Hold(mapsingleitem))}}));
  ];
  DestructiveReverse(mapsingleresult);
];
UnFence("MapSingle",2);
HoldArg("MapSingle",func);

Function("Map",{func,lists})
[
  Local(mapsingleresult,mapsingleitem);
  mapsingleresult:={};
  lists:=Transpose(lists);
  ForEach(mapsingleitem,lists)
  [
    DestructiveInsert(mapsingleresult,1,Apply("Apply",{func,mapsingleitem}));
  ];
  DestructiveReverse(mapsingleresult);
];
UnFence("Map",2);
HoldArg("Map",func);

Function("MapArgs",{expr,oper})
[
   UnList(Concat({expr[[0]]},
     Apply("MapSingle",{oper,Tail(Listify(expr))})
   ) );
];
UnFence("MapArgs",2);
HoldArg("MapArgs",oper);

Function("FillList", {aItem, aLength})
[
  Local(i, aResult);
  aResult:={};
  For(i:=0, i<aLength, i++)
    DestructiveInsert(aResult,1,aItem);
  aResult;
];




/*  Drop  */

/* Needs to check the parameters */

/*
 * Drop( list, n ) gives 'list' with its first n elements dropped
 * Drop( list, -n ) gives 'list' with its last n elements dropped
 * Drop( list, {m,n} ) gives 'list' with elements m through n dropped
 */

RuleBase("Drop", {lst, range});

Rule("Drop", 2, 1, IsList(range))
    Concat(Take(lst,range[[1]]-1), Drop(lst, range[[2]]));

Rule("Drop", 2, 2, range >= 0)
    If( range = 0 Or lst = {}, lst, Drop( Tail(lst), range-1 ));

Rule("Drop", 2, 2, range < 0)
    Take( lst, Length(lst) + range );


/*  Take  */

/* Needs to check the parameters */

/*
 * Take( list, n ) gives the first n elements of 'list'
 * Take( list, -n ) gives the last n elements of 'list'
 * Take( list, {m,n} ) elements m through n of 'list'
 */

RuleBase("Take", {lst, range});

Rule("Take", 2, 1, IsList(range))
    Take( Drop(lst, range[[1]] -1), range[[2]] - range[[1]] + 1);

Rule("Take", 2, 2, range >= 0)
    If( Length(lst)=0 Or range=0, {},
        Concat({Head(lst)}, Take(Tail(lst), range-1)));

Rule("Take", 2, 2, range < 0)
    Drop( lst, Length(lst) + range );


/*  Partition  */

/* Partition( list, n ) partitions 'list' into non-overlapping sublists of length n */

Partition(lst, len):=
	If( Length(lst) < len Or len = 0, {},
        	Concat( {Take(lst,len)}, Partition(Drop(lst,len), len) ));








