/*
 *  sliarray.cc
 *
 *  This file is part of NEST.
 *
 *  Copyright (C) 2004 The NEST Initiative
 *
 *  NEST is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  NEST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/*
    SLI's data access functions
*/
#include <cmath>
#include "numerics.h"
#include "sliarray.h"
#include "arraydatum.h"
#include "integerdatum.h"
#include "doubledatum.h"
#include "stringdatum.h"
#include "namedatum.h"
#include "booldatum.h"
#include "tokenutils.h"
#include "config.h"
#include <vector>
#include "slinames.h"
const std::string  SLIArrayModule::commandstring(void) const
{
  return std::string("/mathematica /C++ ($Revision: 10045 $) provide-component  /mathematica /SLI (1.7) require-component");
  return std::string("(mathematica.sli) run");
}

const std::string SLIArrayModule::name(void) const
{
  return std::string("Mathematica");
}

void SLIArrayModule::RangeFunction::execute(SLIInterpreter *i) const
{
//  call:  array Range -> array

  assert(i->OStack.load()>0);

  ArrayDatum   *ad = dynamic_cast<ArrayDatum *>(i->OStack.pick(0).datum());

  assert(ad !=0);
  if(ad->size() == 1) // Construct an array of elements 1 ... N
  {
    IntegerDatum *nd= dynamic_cast<IntegerDatum *>(ad->get(0).datum());
    if(nd!=0)
    {
      long n= nd->get();
      ad->erase();
      if(n >0)
      {
	ad->reserve(n);
	for(long j=1; j<=n; ++j)
	{
	  Token it(new IntegerDatum(j));
	  ad->push_back_move(it);
	}
      }
      i->EStack.pop();
    }
    else
    {
      double d=ad->get(0);
      ad->erase();
      long n= (long) std::floor(d);
      if(n >0)
      {
	ad->reserve(n);
	for(long j=1; j<=n; ++j)
	{
	  ad->push_back((double)j) ;
	}
      }
      i->EStack.pop();
    }
  }
  else if(ad->size() == 2) // [n1 n2]
  {
    IntegerDatum *n1d= dynamic_cast<IntegerDatum *>(ad->get(0).datum());
    IntegerDatum *n2d= dynamic_cast<IntegerDatum *>(ad->get(1).datum());
    if( (n1d !=0) && (n2d != 0))
    {
      long n = 1 + n2d->get() - n1d->get();

      long start = n1d->get();
      long stop  = n2d->get();

      ad->erase();
      if (n>0)
       ad->reserve(n);

      for(long j=start; j<=stop; ++j)
      {
	Token it(new IntegerDatum(j));
	ad->push_back_move(it);
      }
      i->EStack.pop();
    }
    else
    {
      DoubleDatum *n1d= dynamic_cast<DoubleDatum *>(ad->get(0).datum());
      DoubleDatum *n2d= dynamic_cast<DoubleDatum *>(ad->get(1).datum());
      if( (n1d !=0) && (n2d != 0))
      {
	long n= 1 + static_cast<long>(n2d->get()-n1d->get());

	double start = n1d->get();
	double stop  = n2d->get();

	ad->erase();
        if (n>0)
         ad->reserve(n);

	for(double j=start; j<=stop; ++j)
	{
	  Token it(new DoubleDatum(j));
	  ad->push_back_move(it);
	}
	i->EStack.pop();
      } else i->raiseerror(i->ArgumentTypeError);
    }
  }
  else if(ad->size() == 3) // [n1 n2 dn]
  {
    IntegerDatum *n1d= dynamic_cast<IntegerDatum *>(ad->get(0).datum());
    IntegerDatum *n2d= dynamic_cast<IntegerDatum *>(ad->get(1).datum());
    IntegerDatum *n3d= dynamic_cast<IntegerDatum *>(ad->get(2).datum());
    if( (n1d !=0) && (n2d != 0) && (n3d != 0))
    {
      long di = n3d->get();
      long start=n1d->get();
      long stop=n2d->get();
      if(di != 0)
      {
	long n= 1 + (stop-start)/di;
	ad->erase();
	if(n > 0)
	{
	  ad->reserve(n);
	  long s=start;
	  for(long j=0; j<n; ++j, s+=di)
	  {
	    Token it(new IntegerDatum(s));
	    ad->push_back_move(it);
	  }
	}
	i->EStack.pop();
      }
      else i->raiseerror(i->DivisionByZeroError);
    }
    else
    {
      DoubleDatum *n1d= dynamic_cast<DoubleDatum *>(ad->get(0).datum());
      DoubleDatum *n2d= dynamic_cast<DoubleDatum *>(ad->get(1).datum());
      DoubleDatum *n3d= dynamic_cast<DoubleDatum *>(ad->get(2).datum());
      if( (n1d !=0) && (n2d != 0) && (n3d != 0))
      {
	double di = n3d->get();
	double start=n1d->get();
	double stop=n2d->get();

	if(di != 0)
	{
	  long n= 1 + static_cast<long>((stop-start)/di);
	  ad->erase();
	  if(n>0)
	  {
	    ad->reserve(n);
	    for(long j=0; j<n; ++j)
	    {
	      Token it(new DoubleDatum(start+j*di));
	      ad->push_back_move(it);
	    }
	  }
	  i->EStack.pop();
	}
	else i->raiseerror(i->DivisionByZeroError);
      }
      else i->raiseerror(i->ArgumentTypeError);
    }
  } else i->raiseerror(i->ArgumentTypeError);
}


void SLIArrayModule::ArangeFunction::execute(SLIInterpreter *i) const
{
//  call:  array arange -> vector

  assert(i->OStack.load()>0);

  ArrayDatum   *ad = dynamic_cast<ArrayDatum *>(i->OStack.pick(0).datum());

  assert(ad !=0);
  if(ad->size() == 1) // Construct an array of elements 1 ... N
  {
    IntegerDatum *nd= dynamic_cast<IntegerDatum *>(ad->get(0).datum());
    if(nd!=0)
    {
      long n= nd->get();
      if(n<0)
      {
          i->raiseerror("RangeCheck");
          return;
      }
      IntVectorDatum *result=new IntVectorDatum(new std::vector<long>(n));
      for(long j=0; j< n; ++j)
      {
          (**result)[j]=j+1;
      }
      i->EStack.pop();
      i->OStack.pop();
      i->OStack.push(result);
      return;
    }
    else
    {
        double d=ad->get(0);
        long n= (long) std::floor(d);
        if(n<0)
        {
            i->raiseerror("RangeCheck");
            return;
        }
        DoubleVectorDatum *result=new DoubleVectorDatum(new std::vector<double>(n));
	for(long j=0; j< n; ++j)
	{
            (**result)[j]= 1.0+j;
	}
        i->EStack.pop();
        i->OStack.pop();
        i->OStack.push(result);
        return;
    }
  }
  else if(ad->size() == 2) // [n1 n2]
  {
      IntegerDatum *n1d= dynamic_cast<IntegerDatum *>(ad->get(0).datum());
      IntegerDatum *n2d= dynamic_cast<IntegerDatum *>(ad->get(1).datum());
      if( (n1d !=0) && (n2d != 0))
      {
          const long start = n1d->get();
          const long stop  = n2d->get();
          long n = 1 + stop-start;
          if ( n < 0)
              n=0;
          IntVectorDatum *result=new IntVectorDatum(new std::vector<long>(n));

          for(long j=0, val=start; j< n; ++j, ++val)
          {
              (**result)[j]= val;
          }
          i->EStack.pop();
          i->OStack.pop();
          i->OStack.push(result);
          return;
      }
      else
      {
          DoubleDatum *n1d= dynamic_cast<DoubleDatum *>(ad->get(0).datum());
          DoubleDatum *n2d= dynamic_cast<DoubleDatum *>(ad->get(1).datum());
          if( (n1d !=0) && (n2d != 0))
          {
              double start = n1d->get();
              double stop  = n2d->get();
              long n= 1 + static_cast<long>(stop-start);
              if(n<0)
                  n=0;
      
              DoubleVectorDatum *result=new DoubleVectorDatum(new std::vector<double>(n));
              double val=start;
              for(long j=0; j<n; ++j, ++val)
              {
                  (**result)[j]= val;
              }
              i->EStack.pop();
              i->OStack.pop();
              i->OStack.push(result);
              return;
          }
      } 
  }
  else if(ad->size() == 3) // [n1 n2 dn]
  {
    IntegerDatum *n1d= dynamic_cast<IntegerDatum *>(ad->get(0).datum());
    IntegerDatum *n2d= dynamic_cast<IntegerDatum *>(ad->get(1).datum());
    IntegerDatum *n3d= dynamic_cast<IntegerDatum *>(ad->get(2).datum());
    if( (n1d !=0) && (n2d != 0) && (n3d != 0))
    {
      long di = n3d->get();
      long start=n1d->get();
      long stop=n2d->get();
      if(di != 0)
      {
	long n= 1 + (stop-start)/di;
        if(n<0)
        {
            i->raiseerror("RangeCheck");
            return;
        }
        IntVectorDatum *result=new IntVectorDatum(new std::vector<long>(n));
        long s=start;
        for(long j=0; j<n; ++j, s+=di)
        {
            (**result)[j]= s;
        }
        i->EStack.pop();
        i->OStack.pop();
        i->OStack.push(result);
        return;
      }
      else i->raiseerror(i->DivisionByZeroError);
    }
    else
    {
      DoubleDatum *n1d= dynamic_cast<DoubleDatum *>(ad->get(0).datum());
      DoubleDatum *n2d= dynamic_cast<DoubleDatum *>(ad->get(1).datum());
      DoubleDatum *n3d= dynamic_cast<DoubleDatum *>(ad->get(2).datum());
      if( (n1d !=0) && (n2d != 0) && (n3d != 0))
      {
	double di = n3d->get();
	double start=n1d->get();
	double stop=n2d->get();

	if(di != 0)
	{
	  long n= 1 + static_cast<long>((stop-start)/di);
          if(n<0)
          {
              i->raiseerror("RangeCheck");
              return;
          }
          DoubleVectorDatum *result=new DoubleVectorDatum(new std::vector<double>(n));
          for(long j=0; j<n; ++j)
          {
	      (**result)[j] = (start+j*di);
	  }
          i->EStack.pop();
          i->OStack.pop();
          i->OStack.push(result);
          return;
	}
	else i->raiseerror(i->DivisionByZeroError);
      }
      else i->raiseerror(i->ArgumentTypeError);
    }
  } else i->raiseerror(i->ArgumentTypeError);
}





void SLIArrayModule::ReverseFunction::execute(SLIInterpreter *i) const
{
//  call:  array reverse -> t1 ... tn n
  assert(i->OStack.load()>0);

  ArrayDatum *ad = dynamic_cast<ArrayDatum *>(i->OStack.top().datum());
  assert(ad !=0);
  ad->reverse();
  i->EStack.pop();
}

void SLIArrayModule::RotateFunction::execute(SLIInterpreter *i) const
{
  i->assert_stack_load(2);

  const long n   = getValue<long>(i->OStack.pick(0));
  ArrayDatum *ad = dynamic_cast<ArrayDatum *>(i->OStack.pick(1).datum());

  ad->rotate(n);

  i->OStack.pop();
  i->EStack.pop();
}

void SLIArrayModule::FlattenFunction::execute(SLIInterpreter *i) const
{
//  call:  array Flatten -> array
  assert(i->OStack.load()>0);

  ArrayDatum *ad = dynamic_cast<ArrayDatum *>(i->OStack.top().datum());
  assert(ad !=0);
  ArrayDatum *ta = new ArrayDatum();
  Token at(ta);

  size_t size=0;

  // Estimate size of the final array, by iterating all elements
  for(Token const*t= ad->begin(); t != ad->end(); ++t)
  {
    ArrayDatum *ad1 = dynamic_cast<ArrayDatum *>(t->datum());
    if(ad1 != NULL)
      size += ad1->size();
    else
      ++size;
  }
  ta->reserve(size);

/* Optimized flattening:
   We iterate the source array and copy/move all elements to the target
   array. If the source array has only one reference, we may move the
   elements. However, nested arrays may have more than one reference,
   even if the outer array has only one. Here, we need an additional
   check to decide whether we copy or move the array.
*/
   if(ad->references() ==1)
   {
     for(Token *t= ad->begin(); t != ad->end(); ++t)
     {
       ArrayDatum *ad1 = dynamic_cast<ArrayDatum *>(t->datum());
       if(ad1 != NULL)
       {
	 if(ad1->references() > 1)
	   for(Token *t1= ad1->begin(); t1 != ad1->end(); ++t1)
	     ta->push_back(*t1);
	 else
	   for(Token *t1= ad1->begin(); t1 != ad1->end(); ++t1)
	     ta->push_back_move(*t1);
       }
       else
	   ta->push_back_move(*t);
     }
   }
  else
  {
    for(Token const*t= ad->begin(); t != ad->end(); ++t)
    {
      ArrayDatum *ad1 = dynamic_cast<ArrayDatum *>(t->datum());
      if(ad1 != NULL)
	for(Token const*t1= ad1->begin(); t1 != ad1->end(); ++t1)
	{
	  ta->push_back(*t1);
	}
      else
	ta->push_back(*t);
    }
  }

  i->OStack.pop();
  i->OStack.push_move(at);

  i->EStack.pop();
}

/*BeginDocumentation
Name: Sort - Sorts a homogeneous array of doubles, ints, or strings.
Synopsis:
 array Sort -> array
Parameters:
 array of doubles, ints, or strings
Description:
 The present implementation is restricted to doubles, ints, and strings.
Examples:
 [8. 4. 3. 6. 9. 5.] Sort --> [3. 4. 5. 6. 8. 9.]
Author: Diesmann, Eppler
SeeAlso: Max, Min
*/
void SLIArrayModule::SortFunction::execute(SLIInterpreter *i) const
{
  assert(i->OStack.load()>0);

  TokenArray td = getValue<TokenArray>(i->OStack.top());

  try
  {
    std::vector<double> vd;
    td.toVector(vd);
    std::sort(vd.begin(),vd.end());
    i->OStack.pop();
    i->OStack.push(new ArrayDatum(vd));
    i->EStack.pop();
    return;
  }
  catch (TypeMismatch) {;}

  try
  {
    std::vector<long> vd;
    td.toVector(vd);
    std::sort(vd.begin(),vd.end());
    i->OStack.pop();
    i->OStack.push(new ArrayDatum(vd));
    i->EStack.pop();
    return;
  }
  catch (TypeMismatch) {;}

  try
  {
    std::vector<std::string> vd;
    td.toVector(vd);
    std::sort(vd.begin(),vd.end());
    i->OStack.pop();
    ArrayDatum* output = new ArrayDatum;
    for(size_t c = 0; c < vd.size(); ++c)
    {
      StringDatum *sd= new StringDatum(vd[c]);
      output->push_back(Token(sd));
    }
    i->OStack.push(output);
    i->EStack.pop();
    return;
  }
  catch (TypeMismatch) {;}

  i->message(SLIInterpreter::M_ERROR, "Sort", "argument array may only contain doubles, ints, or strings");
  i->raiseerror(i->ArgumentTypeError);
}


/*
BeginDocumentation

   Name: Transpose - Transposes the first two levels of its argument

   Synopsis:
      array Transpose -> array

   Description:
     Transpose gives the usual transpose of a matrix.
     Acting on a tensor Tijkl... Transpose gives the tensor Tjikl...

   Parameters:

   Examples:
     [ [3 4 5] [6 7 8] ] Transpose  -> [[3 6] [4 7] [5 8]]

   Bugs:
     protected for non-rectangular shapes by assert().
     Transpose should raise
       /NonRectangularShapeError error
     and message
      "The first two levels of the one-dimensional list cannot be transposed."
   Author: Markus Diesmann, July 9, 2000
   FirstVersion: June, 2000
   References:   [1] The Mathematica Book V4.0 "Transpose"
   SeeAlso: Flatten, Partition

*/
void SLIArrayModule::TransposeFunction::execute(SLIInterpreter *i) const
{
//  call:  array Transpose -> array
  assert(i->OStack.load()>0);

  ArrayDatum *sd = dynamic_cast<ArrayDatum *>(i->OStack.top().datum());
  assert(sd !=0 );

  ArrayDatum *hd = dynamic_cast<ArrayDatum *>(sd->begin()->datum());
  assert(hd != 0);

  // size of source first level
  size_t m = sd->size();

  // size of source second level
  size_t n =  hd->size();

  //   std::cerr << "Transpose:: rows:    " << m << std::endl;
  //  std::cerr << "Transpose:: columns: " << n << std::endl;


  ArrayDatum *td = new ArrayDatum();
  assert(td != 0);

  Token tt(td);

  td->reserve(n);

  // check if correct for empty arrays

  for (size_t j = 0; j< n; j++)
   {
    hd = new ArrayDatum();
    assert(td != 0);

    hd->reserve(m);

    td->push_back(Token(hd));
   }

   for(Token *sr= sd->begin(); sr != sd->end(); ++sr)
    {
     hd =  dynamic_cast<ArrayDatum *>(sr->datum());

     // raiseerror instead
     assert(hd != 0);

     Token *sc;
     Token *tr;

     for
      (
       sc= hd->begin(), tr=td->begin();
       sc != hd->end();
       ++sc, ++tr
      )
     {

      ArrayDatum *trd = dynamic_cast<ArrayDatum *>(tr->datum());

      // raiseerror instead
      assert(trd != 0);

       trd->push_back(*sc);

     }
    }



  i->OStack.pop();
  i->OStack.push_move(tt);

  i->EStack.pop();
}



void SLIArrayModule::PartitionFunction::execute(SLIInterpreter *i) const
{
//  call:  array n d Partition -> array
  assert(i->OStack.load()>2);

  IntegerDatum *dd = dynamic_cast<IntegerDatum *>(i->OStack.pick(0).datum());
  assert(dd != NULL);
  IntegerDatum *nd = dynamic_cast<IntegerDatum *>(i->OStack.pick(1).datum());
  assert(nd != NULL);
  ArrayDatum *source = dynamic_cast<ArrayDatum *>(i->OStack.pick(2).datum());
  assert(source !=0);
  ArrayDatum *target = new ArrayDatum;

  long n = nd->get();
  long d = dd->get();

  if (n>0)
  {
    if (d>0)
    {
      size_t na= source->size();
      if(na >0)
      {
	long max = (na - n + d)/d;

	target->reserve( (max > 0) ? max : 0 );
	Token *b = source->begin();
	Token *e = source->end();

	for(Token *pt=b; pt < e-n+1; pt+=d)
	{
	  ArrayDatum *ad= new ArrayDatum;
	  ad->reserve(n);
	  for(long i = 0; i < n ; ++i)
	  {
	    assert(pt+i < e);
	    ad->push_back(*(pt+i));
	  }
	  target->push_back(ad);
	}
      }
      // need to pop ourselves, arguments, push (empty) target
      // even if argument array was empty --- HEP 2001-10-22
      i->EStack.pop();
      i->OStack.pop(3);
      i->OStack.push(target);
    }
    else
      i->raiseerror("RangeError");
  }
  else
    i->raiseerror("RangeError");
}

/*
BeginDocumentation

   Name: arrayload - pushes array elements followed by number of elements

   Synopsis:
      array Transpose ->  array arrayload -> t1 ... tn n

   Description:
    The stack is invariant under the sequences
       arrayload arraystore
       arraystore arrayload  .
    arrayload is the SLI version of PostScript operator aload.
    In contrast to PostScript SLI arrays are dynamic therefore
    the syntax of aload and astore is obsolete in SLI.
    If used aload and astore issue a warning message.

   Examples:
        [ 5 4 2 ] arrayload  --> 5 4 2   3

   Author: Marc-Oliver Gewaltig, Markus Diesmann
   Remarks: There are two obsolete versions existing called aload and astore.
   SeeAlso: arraystore
*/
void SLIArrayModule::ArrayloadFunction::execute(SLIInterpreter *i) const
{
//  call:  array arrayload -> t1 ... tn n
  assert(i->OStack.load()>0);

  Token at; at.move(i->OStack.top());
  i->OStack.pop();
  ArrayDatum *ad = dynamic_cast<ArrayDatum *>(at.datum());
  assert(ad !=0);
  i->EStack.pop();
  int  arraysize=ad->size();
  i->OStack.reserve_token(arraysize);

  if(ad->references()==1)
    for(Token *ti=ad->begin(); ti != ad->end(); ++ti)
      i->OStack.push_move(*ti);
  else
    for(Token *ti=ad->begin(); ti != ad->end(); ++ti)
      i->OStack.push(*ti);

  i->OStack.push(arraysize);
}

/*
BeginDocumentation

   Name: arraystore - pops the first n elements of the stack into an array

   Synopsis:
     t1 ... tn n  arraystore -->  array

   Description:
    The stack is invariant under the sequences
       arrayload arraystore
       arraystore arrayload  .
    arraystore is the SLI version of PostScript operator astore.
    In contrast to PostScript SLI arrays are dynamic therefore
    the syntax of aload and astore is obsolete in SLI.
    If used aload and astore issue a warning message.

   Parameters:

   Examples:
      5 4 2   3  arraystore  -->   [ 5 4 2 ]

   Bugs:
   Author: Marc-Oliver Gewaltig, Markus Diesmann
   Remarks: There are two obsolete versions existing called aload and astore.
   SeeAlso: arrayload
*/
void SLIArrayModule::ArraystoreFunction::execute(SLIInterpreter *i) const
{
//  call: t1 ... tn n  arraystore -> array
//  assert(i->OStack.load()>0);

  IntegerDatum *id=dynamic_cast<IntegerDatum *>(i->OStack.top().datum());
  assert(id != NULL);

  long n=id->get();
  if(n>=0)
  {
    if(i->OStack.load()>static_cast<size_t>(n))
    {
      i->OStack.pop();
      ArrayDatum *ad= new ArrayDatum();
      ad->reserve(n);
      Token at(ad);
      for(long l=1; l<=n; ++l)
	ad->push_back_move(i->OStack.pick(n-l));
      i->OStack.pop(n);
      i->OStack.push_move(at);
      i->EStack.pop();
    } else i->raiseerror(i->StackUnderflowError);
  } else i->raiseerror(i->RangeCheckError);
}

void SLIArrayModule::ArraycreateFunction::execute(SLIInterpreter *i) const
{
//  call: mark t1 ... tn  arraycreate -> array
  if(i->OStack.load()==0)
  {
    i->message(SLIInterpreter::M_ERROR, "arraycreate","Opening bracket missing.");
    i->raiseerror("SyntaxError");
    return;
  }

  size_t depth = i->OStack.load();
  size_t n   = 0;
  const Token mark_token(new LiteralDatum(i->mark_name));
  bool found = false;

  while( (n < depth) && !found)
  {
    found = (i->OStack.pick(n) == mark_token);
    ++n;
  }

  if(found)
  {
    ArrayDatum *ad= new ArrayDatum();
    ad->reserve(n-1);
    Token at(ad);
    for(size_t l=2; l <= n; ++l)
      ad->push_back_move(i->OStack.pick(n-l));
    i->OStack.pop(n);
    i->OStack.push_move(at);
    i->EStack.pop();
  }
  else
  {
    i->message(SLIInterpreter::M_ERROR, "arraycreate","Opening bracket missing.");
    i->raiseerror("SyntaxError");
    return;
  }
}

void SLIArrayModule::IMapFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *id= static_cast<IntegerDatum *>(i->EStack.pick(p+3).datum());
  assert(id!=NULL);

  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+2).datum());
  assert(count==NULL);

  ProcedureDatum const *pd= static_cast<ProcedureDatum *>(i->EStack.pick(p+1).datum());
  assert(pd !=NULL);

  std::cerr << "During Map at iteration " << count->get() << "." << std::endl;


  pd->list(std::cerr,"   ",id->get()-1);
  std::cerr <<std::endl;

}

/**********************************************/
/* % IMap                                     */
/*  call: array mark procc count proc %map   */
/*  pick   5     4    3     2    1      0     */
/**********************************************/
void SLIArrayModule::IMapFunction::execute(SLIInterpreter *i) const
{
    ProcedureDatum *proc=static_cast<ProcedureDatum *>(i->EStack.pick(1).datum());
    size_t proclimit=proc->size();
    IntegerDatum *count= static_cast<IntegerDatum *>(i->EStack.pick(2).datum());
    size_t iterator= count->get();
    IntegerDatum *procc= static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
    size_t pos= procc->get();
    ArrayDatum *array=static_cast<ArrayDatum *>(i->EStack.pick(5).datum());
    size_t limit=array->size();

    // Do we  start a new iteration ?
    if(pos==0)
    {
      if(iterator < limit) // Is Iteration is still running
      {
	if(iterator>0)
	{
	  // In this case we have to put the result of
	  // the last procedure call into the array
	  if(i->OStack.load()==0)
	  {
	    i->dec_call_depth();
	    i->raiseerror(i->StackUnderflowError);
	    return;
	  }
	  array->assign_move(iterator-1,i->OStack.top());
	  i->OStack.pop();
	}

	i->OStack.push(array->get(iterator));  // push element to user
	if(i->step_mode())
	{
	  std::cerr << "Map:"
		    << " Limit: " << limit
		    << " Pos: " << iterator
		    << " Iterator: ";
	  i->OStack.pick(0).pprint(std::cerr);
	  std::cerr << std::endl;
	}

	++(count->get());
	// We continue after this if-branch and do the commands
      }
      else
      {
	if(iterator>0)
	{
	  // In this case we have to put the result of
	  // the last procedure call into the array
	  if(i->OStack.load()==0)
	  {
	    i->raiseerror(i->StackUnderflowError);
	    return;
	  }
	  array->assign_move(iterator-1,i->OStack.top());
	  i->OStack.pop();
	}
	i->OStack.push_move(i->EStack.pick(5)); // push array
	i->EStack.pop(6);
	i->dec_call_depth();
	return;
      }
    }

    if((size_t)procc->get() < proclimit)
    {
      /* we are still evaluating the procedure. */
      i->EStack.push(proc->get(pos));  // get next command from the procedure
      ++(procc->get());                 // increment the counter and

      if(i->step_mode())
      {
	std::cerr << std::endl;
	do{
	  char cmd=i->debug_commandline(i->EStack.top());
	  if(cmd=='l') // List the procedure
	  {
	    if(proc !=NULL)
	    {
	      proc->list(std::cerr,"   ",pos);
	      std::cerr <<std::endl;
	    }
	  }
	  else
	    break;
	} while (true);
      }

    }
    if((size_t)procc->get() >= proclimit)
      (*procc)=0;


}
void SLIArrayModule::IMap_ivFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *id= static_cast<IntegerDatum *>(i->EStack.pick(p+3).datum());
  assert(id!=NULL);

  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+2).datum());
  assert(count==NULL);

  ProcedureDatum const *pd= static_cast<ProcedureDatum *>(i->EStack.pick(p+1).datum());
  assert(pd !=NULL);

  std::cerr << "During Map at iteration " << count->get() << "." << std::endl;


  pd->list(std::cerr,"   ",id->get()-1);
  std::cerr <<std::endl;

}

/**********************************************/
/* % IMap_iv                                     */
/*  call: intvec mark procc count proc %map   */
/*  pick   5     4    3     2    1      0     */
/**********************************************/
void SLIArrayModule::IMap_ivFunction::execute(SLIInterpreter *i) const
{
    ProcedureDatum *proc=static_cast<ProcedureDatum *>(i->EStack.pick(1).datum());
    size_t proclimit=proc->size();
    IntegerDatum *count= static_cast<IntegerDatum *>(i->EStack.pick(2).datum());
    size_t iterator= count->get();
    IntegerDatum *procc= static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
    size_t pos= procc->get();
    IntVectorDatum *array=static_cast<IntVectorDatum *>(i->EStack.pick(5).datum());
    size_t limit=(*array)->size();

    // Do we  start a new iteration ?
    if(pos==0)
    {
      if(iterator < limit) // Is Iteration is still running
      {
	if(iterator>0)
	{
	  // In this case we have to put the result of
	  // the last procedure call into the array
	  if(i->OStack.load()==0)
	  {
	    i->dec_call_depth();
	    i->raiseerror(i->StackUnderflowError);
	    return;
	  }
          IntegerDatum *result=dynamic_cast<IntegerDatum *>(i->OStack.top().datum());
          if(not result)
          {
	    i->dec_call_depth();
            i->message(SLIInterpreter::M_ERROR, "Map_iv", "Function must return an integer.");

	    i->raiseerror(i->ArgumentTypeError);
            return;
          }

	  (**array)[iterator-1]=result->get();
	  i->OStack.pop();
	}

	i->OStack.push(new IntegerDatum((**array)[iterator]));  // push element to user
	if(i->step_mode())
	{
	  std::cerr << "Map:"
		    << " Limit: " << limit
		    << " Pos: " << iterator
		    << " Iterator: ";
	  i->OStack.pick(0).pprint(std::cerr);
	  std::cerr << std::endl;
	}

	++(count->get());
	// We continue after this if-branch and do the commands
      }
      else
      {
	if(iterator>0)
	{
	  // In this case we have to put the result of
	  // the last procedure call into the array
	  if(i->OStack.load()==0)
	  {
	    i->raiseerror(i->StackUnderflowError);
	    return;
	  }
          IntegerDatum *result=dynamic_cast<IntegerDatum *>(i->OStack.top().datum());
          if(not result)
          {
              i->dec_call_depth();
              i->message(SLIInterpreter::M_ERROR, "Map_iv", "Function must return an integer.");
              i->raiseerror(i->ArgumentTypeError);
              return;
          }
	  (**array)[iterator-1]= result->get();
	  i->OStack.pop();
	}
	i->OStack.push_move(i->EStack.pick(5)); // push array
	i->EStack.pop(6);
	i->dec_call_depth();
	return;
      }
    }

    if((size_t)procc->get() < proclimit)
    {
      /* we are still evaluating the procedure. */
      i->EStack.push(proc->get(pos));  // get next command from the procedure
      ++(procc->get());                 // increment the counter and

      if(i->step_mode())
      {
	std::cerr << std::endl;
	do{
	  char cmd=i->debug_commandline(i->EStack.top());
	  if(cmd=='l') // List the procedure
	  {
	    if(proc !=NULL)
	    {
	      proc->list(std::cerr,"   ",pos);
	      std::cerr <<std::endl;
	    }
	  }
	  else
	    break;
	} while (true);
      }

    }
    if((size_t)procc->get() >= proclimit)
      (*procc)=0;


}

void SLIArrayModule::IMap_dvFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *id= static_cast<IntegerDatum *>(i->EStack.pick(p+3).datum());
  assert(id!=NULL);

  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+2).datum());
  assert(count==NULL);

  ProcedureDatum const *pd= static_cast<ProcedureDatum *>(i->EStack.pick(p+1).datum());
  assert(pd !=NULL);

  std::cerr << "During Map at iteration " << count->get() << "." << std::endl;


  pd->list(std::cerr,"   ",id->get()-1);
  std::cerr <<std::endl;

}

void SLIArrayModule::IMap_dvFunction::execute(SLIInterpreter *i) const
{
    ProcedureDatum *proc=static_cast<ProcedureDatum *>(i->EStack.pick(1).datum());
    size_t proclimit=proc->size();
    IntegerDatum *count= static_cast<IntegerDatum *>(i->EStack.pick(2).datum());
    size_t iterator= count->get();
    IntegerDatum *procc= static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
    size_t pos= procc->get();
    DoubleVectorDatum *array=static_cast<DoubleVectorDatum *>(i->EStack.pick(5).datum());
    size_t limit=(*array)->size();

    // Do we  start a new iteration ?
    if(pos==0)
    {
      if(iterator < limit) // Is Iteration is still running
      {
	if(iterator>0)
	{
	  // In this case we have to put the result of
	  // the last procedure call into the array
	  if(i->OStack.load()==0)
	  {
	    i->dec_call_depth();
	    i->raiseerror(i->StackUnderflowError);
	    return;
	  }
          DoubleDatum *result=dynamic_cast<DoubleDatum *>(i->OStack.top().datum());
          if(not result)
          {
	    i->dec_call_depth();
            i->message(SLIInterpreter::M_ERROR, "Map_dv", "Function must return a double.");

	    i->raiseerror(i->ArgumentTypeError);
            return;
          }

	  (**array)[iterator-1]=result->get();
	  i->OStack.pop();
	}

	i->OStack.push(new DoubleDatum((**array)[iterator]));  // push element to user
	if(i->step_mode())
	{
	  std::cerr << "Map_dv:"
		    << " Limit: " << limit
		    << " Pos: " << iterator
		    << " Iterator: ";
	  i->OStack.pick(0).pprint(std::cerr);
	  std::cerr << std::endl;
	}

	++(count->get());
	// We continue after this if-branch and do the commands
      }
      else
      {
	if(iterator>0)
	{
	  // In this case we have to put the result of
	  // the last procedure call into the array
	  if(i->OStack.load()==0)
	  {
	    i->raiseerror(i->StackUnderflowError);
	    return;
	  }
          DoubleDatum *result=dynamic_cast<DoubleDatum *>(i->OStack.top().datum());
          if(not result)
          {
              i->dec_call_depth();
              i->message(SLIInterpreter::M_ERROR, "Map_dv", "Function must return a double.");
              i->raiseerror(i->ArgumentTypeError);
              return;
          }
	  (**array)[iterator-1]= result->get();
	  i->OStack.pop();
	}
	i->OStack.push_move(i->EStack.pick(5)); // push array
	i->EStack.pop(6);
	i->dec_call_depth();
	return;
      }
    }

    if((size_t)procc->get() < proclimit)
    {
      /* we are still evaluating the procedure. */
      i->EStack.push(proc->get(pos));  // get next command from the procedure
      ++(procc->get());                 // increment the counter and

      if(i->step_mode())
      {
	std::cerr << std::endl;
	do{
	  char cmd=i->debug_commandline(i->EStack.top());
	  if(cmd=='l') // List the procedure
	  {
	    if(proc !=NULL)
	    {
	      proc->list(std::cerr,"   ",pos);
	      std::cerr <<std::endl;
	    }
	  }
	  else
	    break;
	} while (true);
      }

    }
    if((size_t)procc->get() >= proclimit)
      (*procc)=0;


}

/********************************/
/* Map                          */
/*  call: array proc Map -> array */
/*  pick   1    0               */
/********************************/
/*
BeginDocumentation

   Name: Map - Apply a procedure to each element of a list or string

   Synopsis:
     [v1 ... vn] {f} Map -> [ f(v1) ... f(vn) ]
     (c1 ... cn) {f} Map -> (f(c1)...f(vn))

   Parameters:
     [v1 ... vn] - list of n arbitrary objects
     (c1 ... cn) - string with n characters

     {f}         - function which can operate on the elements of [array].
                   This function must return exaclty one value.

   Description:
     Map works like the corresponding Mathematica function.
     For each element of the input array, Map calls f and replaces
     the element with the result of f.
     Note that f must return exactly one value! The result of Map
     is a list with the same number of values as the argument list.
     If f does not return a value, Map fails.
     If f returns more than one value, the result of Map is undefined.

   Examples:

   [1 2 3 4 5]  {2 mul} Map -> [2 4 6 8 10]
   (abc) {1 add} Map  -> (bcd)

   Diagnostics:

   Bugs:

   Author:
    Marc-Oliver Gewaltig

   Remarks: Map is not part of PostScript
   References: The Mathematica Book
   SeeAlso: MapAt, MapIndexed, Table, forall, forallindexed, NestList

*/

void SLIArrayModule::MapFunction::execute(SLIInterpreter *i) const
{
    i->EStack.pop();
    ProcedureDatum *proc=
        dynamic_cast<ProcedureDatum *>(i->OStack.top().datum());
    assert(proc !=NULL);

    if(proc->size()==0)
    {
      // If the procedure is empty, just leave the array as it is.
      i->OStack.pop();
      return;
    }

    i->EStack.push_move(i->OStack.pick(1));        // push array
    
    i->EStack.push(i->baselookup(i->mark_name));

    i->EStack.push_by_pointer(new IntegerDatum(0));          // push procedure counter
    i->EStack.push_by_pointer(new IntegerDatum(0));          // push initial counter
    i->EStack.push_move(i->OStack.pick(0));       // push procedure

    if(dynamic_cast<IntVectorDatum *>(i->EStack.pick(4).datum()))
        i->EStack.push(i->baselookup(sli::imap_iv));
    else if(dynamic_cast<DoubleVectorDatum *>(i->EStack.pick(4).datum()))
        i->EStack.push(i->baselookup(sli::imap_dv));
    else
        i->EStack.push(i->baselookup(sli::imap));
    i->inc_call_depth();
    i->OStack.pop(2);
}

void SLIArrayModule::ValidFunction::execute(SLIInterpreter *i) const
{
  assert(i->OStack.load() > 0);
  ArrayDatum *ad = dynamic_cast<ArrayDatum*>(i->OStack.top().datum());
  assert(ad != NULL);
  i->OStack.push( ad->valid());

  i->EStack.pop();
}

void SLIArrayModule::IMapIndexedFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *id= static_cast<IntegerDatum *>(i->EStack.pick(p+3).datum());
  assert(id !=NULL);

  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+2).datum());
  assert(count!=NULL);

  ProcedureDatum const *pd= static_cast<ProcedureDatum *>(i->EStack.pick(p+1).datum());
  assert(pd !=NULL);


  std::cerr << "During MapIndexed at iteration " << count->get() << "." << std::endl;


  pd->list(std::cerr,"   ",id->get()-1);
  std::cerr <<std::endl;

}

/**********************************************/
/* % IMapIndexed                              */
/*  call: array mark procc count proc %map    */
/*  pick   5     4    3     2    1      0     */
/**********************************************/
void SLIArrayModule::IMapIndexedFunction::execute(SLIInterpreter *i) const
{
    ProcedureDatum *proc=static_cast<ProcedureDatum *>(i->EStack.pick(1).datum());
    size_t proclimit=proc->size();
    IntegerDatum *count= static_cast<IntegerDatum *>(i->EStack.pick(2).datum());
    size_t iterator= count->get();
    IntegerDatum *procc= static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
    size_t pos= procc->get();
    ArrayDatum *array=static_cast<ArrayDatum *>(i->EStack.pick(5).datum());
    size_t limit=array->size();

    // Do we  start a new iteration ?
    if(pos==0)
    {
      if(iterator < limit) // Is Iteration is still running
      {
	if(iterator>0)
	{
	  // In this case we have to put the result of
	  // the last procedure call into the array
	  if(i->OStack.load()==0)
	  {
	    i->raiseerror(i->StackUnderflowError);
	    return;
	  }
	  array->assign_move(iterator-1,i->OStack.top());


	  i->OStack.pop();
	}

	i->OStack.push(array->get(iterator));  // push element to user
	i->OStack.push(*count);             // push iterator to user
	++(count->get());

	if(i->step_mode())
	{

	  std::cerr << "MapIndexed:"
		    << " Limit: " << limit
		    << " Pos: " << iterator
		    << " Iterator: ";
	  i->OStack.pick(1).pprint(std::cerr);
	  std::cerr << std::endl;
	}

	// We continue after this if-branch and do the commands
      }
      else
      {
	if(iterator>0)
	{
	  // In this case we have to put the result of
	  // the last procedure call into the array
	  if(i->OStack.load()==0)
	  {
	    i->raiseerror(i->StackUnderflowError);
	    return;
	  }
	  array->assign_move(iterator-1,i->OStack.top());
	  i->OStack.pop();
	}
	i->OStack.push_move(i->EStack.pick(5)); // push array
	i->EStack.pop(6);
	i->dec_call_depth();
	return;
      }
    }

    if((size_t)procc->get() < proclimit)
    {
      /* we are still evaluating the procedure. */
      i->EStack.push(proc->get(pos));  // get next command from the procedure
      ++(procc->get());                 // increment the counter and
      if(i->step_mode())
      {
	std::cerr << std::endl;
	do{
	  char cmd=i->debug_commandline(i->EStack.top());
	  if(cmd=='l') // List the procedure
	  {
	    if(proc !=NULL)
	    {
	      proc->list(std::cerr,"   ",pos);
	      std::cerr <<std::endl;
	    }
	  }
	  else
	    break;
	} while (true);
      }
    }
    if((size_t)procc->get() >= proclimit)
      (*procc)=0;


}


void SLIArrayModule::MapIndexedFunction::execute(SLIInterpreter *i) const
{
    i->EStack.pop();
    ProcedureDatum *proc=
        dynamic_cast<ProcedureDatum *>(i->OStack.top().datum());
    assert(proc !=NULL);

    if(proc->size()==0)
    {
      // If the procedure is empty, just leave the array as it is.
      i->OStack.pop();
      return;
    }

    i->EStack.push_move(i->OStack.pick(1));        // push array
    i->EStack.push(i->baselookup(i->mark_name));

    i->EStack.push(new IntegerDatum(0));          // push procedure counter
    i->EStack.push(new IntegerDatum(0));          // push initial counter
    i->EStack.push_move(i->OStack.pick(0));       // push procedure

    i->EStack.push(i->baselookup(sli::imapindexed));
    i->inc_call_depth();
    i->OStack.pop(2);
}

void SLIArrayModule::IMapThreadFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *id= static_cast<IntegerDatum *>(i->EStack.pick(p+3).datum());
  assert(id !=NULL);

  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+2).datum());
  assert(count!=NULL);

  ProcedureDatum const *pd= static_cast<ProcedureDatum *>(i->EStack.pick(p+1).datum());
  assert(pd !=NULL);


  std::cerr << "During MapThread at iteration " << count->get() << "." << std::endl;


  pd->list(std::cerr,"   ",id->get()-1);
  std::cerr <<std::endl;

}

//******************************************************
// % IMapThread
//  call: mark  lim  tarray sarray procc count proc %map
//  pick   7     6      5      4      3     2   1    0
//*******************************************************
void SLIArrayModule::IMapThreadFunction::execute(SLIInterpreter *i) const
{
    ProcedureDatum *procd=static_cast<ProcedureDatum *>(i->EStack.pick(1).datum());
//    assert(procd != NULL);

    size_t proclimit=procd->size();

    IntegerDatum *argcountd= static_cast<IntegerDatum *>(i->EStack.pick(2).datum());
    //  assert(argcountd != NULL);

    size_t argcount= argcountd->get();

    IntegerDatum *proccountd= static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
    //assert(proccountd != NULL);
    size_t proccount= proccountd->get();

    ArrayDatum   *sarray= static_cast<ArrayDatum *>(i->EStack.pick(4).datum());
    //assert(sarray != NULL);
    ArrayDatum   *tarray= static_cast<ArrayDatum *>(i->EStack.pick(5).datum());
    //assert(tarray != NULL);

    IntegerDatum *limitd= static_cast<IntegerDatum *>(i->EStack.pick(6).datum());
    //assert(limitd != NULL);

    size_t args= sarray->size(); // number of argument arrays
    size_t limit= limitd->get(); // number of arguments per array

    // first we check whether we start anew with the iteration of the procedure
    // this is the case when proccount==0
    if(proccount==0)
    {
      if(argcount < limit) // Is Iteration is still running
      {
	if(argcount>0)
	{
	  // In this case we have to put the result of
	  // the last procedure call into the array
	  if(i->OStack.load()==0)
	  {
	    i->raiseerror(i->StackUnderflowError);
	    return;
	  }
	  tarray->assign_move(argcount-1,i->OStack.top());
	  i->OStack.pop();
	}

	// Make a loop over all argument arrays and push the next element
	for(size_t j=0; j< args; ++j)
	{
	  ArrayDatum *ad= static_cast<ArrayDatum *>(sarray->get(j).datum());
	  i->OStack.push(ad->get(argcount));  // push element to user
	}
	assert(i->OStack.load()>= args);
	++(argcountd->get());

	// We continue after this if-branch and do the commands
	if(i->step_mode())
	{

	  std::cerr << "MapThread:"
		    << " Limit: " << limit
		    << " Pos: " << argcount
		    << " Args: " << args
		    << std::endl;
	}
      }
      else
      {
        assert(argcount >= limit);
	if(argcount>0) // should be obsolete
	{
	  // In this case we have to put the result of
	  // the last procedure call into the array
	  if(i->OStack.load()==0)
	  {
	    i->raiseerror(i->StackUnderflowError);
	    return;
	  }
	  tarray->assign_move(argcount-1,i->OStack.top());
	  i->OStack.pop();
	}
	i->OStack.push_move(i->EStack.pick(5)); // push result array
	i->EStack.pop(8);
	i->dec_call_depth();
	return;
      }
    }

    if((size_t)proccountd->get() < proclimit)
    {
      /* we are still evaluating the procedure. */
      i->EStack.push(procd->get(proccount));  // get next command from the procedure
      ++(proccountd->get());                 // increment the counter and

      if(i->step_mode())
      {
	std::cerr << std::endl;
	do{
	  char cmd=i->debug_commandline(i->EStack.top());
	  if(cmd=='l') // List the procedure
	  {
	    if(procd !=NULL)
	    {
	      procd->list(std::cerr,"   ", proccount);
	      std::cerr <<std::endl;
	    }
	  }
	  else
	    break;
	} while (true);
      }
    }
    if((size_t)proccountd->get() >= proclimit)
      (*proccountd)=0;
}

/* BeginDocumentation
Name: MapThread - apply a procedure to corresponding elements of n arrays
Synopsis: [[a11 ... a1n]...[am1 ... amn]] {f} MapThread ->
                                  [f(a11, a21,... am1)...f(a1n, a2n,...,amn)]
Description: MapThread is like a multidimensional Map. It applies the function of
             to corresponding elements of m argument arrays.

Parameters: the first parameter is a list of m arrays of equal size n.
            The second parameter is a procedure which takes m arguments and returns
            a single value.
Examples:    [[1 2][3 4]] {add} MapThread -> [4 6]
            [[1 2 3 4] [1 1 1 1]] {add} MapThread -> [2 3 4 5]

References: This function implements the simple version of Mathematica's MapThread
SeeAlso: Map, MapIndexed, NestList, FoldList, ScanThread
*/

void SLIArrayModule::MapThreadFunction::execute(SLIInterpreter *i) const
{
  assert(i->OStack.load()>=2 );
  ProcedureDatum *proc=
    dynamic_cast<ProcedureDatum *>(i->OStack.top().datum());
  assert(proc !=NULL);

  if(proc->size()==0)
  {
    // If the procedure is empty, just leave the array as it is.
    i->OStack.pop();
    i->EStack.pop();
    return;
  }

  ArrayDatum  *ad= dynamic_cast<ArrayDatum *>(i->OStack.pick(1).datum());
  assert(ad !=NULL);

  if(ad->size() >0)
  {
    // check if the components are arrays of equal length.
    ArrayDatum *ad1= dynamic_cast<ArrayDatum *>(ad->get(0).datum());
    if (ad1 == NULL)
    {
      i->raiseerror(i->ArgumentTypeError);
      return;
    }

    for(size_t j=1; j< ad->size(); ++ j)
    {
      ArrayDatum *ad2= dynamic_cast<ArrayDatum *>(ad->get(j).datum());
      if (ad2 == NULL)
      {
	i->raiseerror(i->ArgumentTypeError);
	return;
      }

      if(ad2->size() != ad1->size())
      {
	i->raiseerror(i->RangeCheckError);
	return;
      }
    }

    i->EStack.pop(); // remove MapThread object
    i->EStack.push(i->baselookup(i->mark_name));    //  mark
    i->EStack.push(new IntegerDatum(ad1->size()));  //  limit
    i->EStack.push(new ArrayDatum(*ad1));           //  target array (copy)
    i->EStack.push_move(i->OStack.pick(1));          //  argument array
    i->EStack.push(new IntegerDatum(0));            //  procedure counter
    i->EStack.push(new IntegerDatum(0));            //  initial counter
    i->EStack.push_move(i->OStack.top());           //  procedure

    i->EStack.push(i->baselookup(Name("::MapThread")));
    i->OStack.pop(2);
    i->inc_call_depth();
  }
  else // size > 0
  {
    i->OStack.pop();
    i->EStack.pop();
  }

}


// Put a token to a nested array.
void SLIArrayModule::Put_a_a_tFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<3 )
  {
    i->message(SLIInterpreter::M_ERROR, "Put","Too few parameters supplied.");
    i->message(SLIInterpreter::M_ERROR, "Put","Usage: [array] [d1 ...dn] obj Put -> [array]");
    i->raiseerror(i->StackUnderflowError);
    return;
  }

  ArrayDatum *source = dynamic_cast<ArrayDatum *>(i->OStack.pick(2).datum());
  if(source == NULL)
  {
    i->message(SLIInterpreter::M_ERROR, "Put","First argument must be an array.");
    i->message(SLIInterpreter::M_ERROR, "Put","Usage: [array] [d1 ...dn]  obj Put -> [array]");
    i->raiseerror(i->ArgumentTypeError);
    return;
  }


  ArrayDatum *pos    = dynamic_cast<ArrayDatum *>(i->OStack.pick(1).datum());

  if(pos == NULL)
  {
    i->message(SLIInterpreter::M_ERROR, "Put","Second argument must be an array indicating the position is a nested array.");
    i->message(SLIInterpreter::M_ERROR, "Put","Usage: [array] [d1 ...dn]  obj Put -> [array]");
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  for(Token *t=pos->begin(); t != pos->end(); ++t)
  {
    assert(t != NULL);
    IntegerDatum *idx=dynamic_cast<IntegerDatum *>(t->datum());
    if(idx == NULL)
    {
      i->message(SLIInterpreter::M_ERROR, "Put","Non integer index found.");
      i->message(SLIInterpreter::M_ERROR, "Put","Source array is unchanged.");
      i->raiseerror(i->ArgumentTypeError);
      return;
    }

    int j=idx->get();

    if (j <0)
    {
      i->message(SLIInterpreter::M_ERROR, "Put","Negative index found.");
      i->message(SLIInterpreter::M_ERROR, "Put","Source array is unchanged.");
      i->raiseerror(i->RangeCheckError);
      return;
    }

    if(j >= (int)source->size())
    {
      i->message(SLIInterpreter::M_ERROR, "Put","Index out of range.");
      i->message(SLIInterpreter::M_ERROR, "Put","Source array is unchanged.");
      i->raiseerror(i->RangeCheckError);
      return;
    }

    if(t < pos->end()-1)
    {
      source= dynamic_cast<ArrayDatum *>((*source)[j].datum());
      if(source==NULL)
      {
	i->message(SLIInterpreter::M_ERROR, "Put","Dimensions of index and array do not match.");
	i->message(SLIInterpreter::M_ERROR, "Put","Source array is unchanged.");
	i->raiseerror(i->RangeCheckError);
	return;
      }
    }
    else
    {
      // Now source points to the innermost target array and we can replace the object.
      (*source)[j].swap(i->OStack.top());
    }
  }

  i->EStack.pop();
  i->OStack.pop(2);

}

/* BeginDocumentation

Name: area - Return array of indices defining a 2d subarea of a 2d array.

Synopsis:
                source_width source_anchor_y source_anchor_x
    area_height   area_width   area_anchor_y   area_anchor_x
                                                        area -> [1d-indices]

Description:
  Given a -- hypothetical -- twodimensional array,
  "area" tells you, what indices you need to
  subscript a contiguous, twodimensional subarea.

  The subarea is defined by specifying it's size
  (width and height), as well as its location in the
  source array. The location is defined by specifying
  an anchor point in the source array as well as in
  the subarea. Anchor points are matched, see
  illustration, and examples below:

  source array: height=6, width=15, anchor=(2,5)
  subarea     : height=4, width= 5, anchor=(1,3)
  ...............
  ..ooooo........
  ..oooxo........
  ..ooooo........
  ..ooooo........
  ...............


  "area" returns an array of ONEDIMENSIONAL indices.
  There is a SLI function called "area2" returning
  twodimensional indices, as well as the conversion
  functions "cv1d" and "cv2d".
  (For information on the order of subscription in NEST
  arrays, see references below.)

Parameters:
   In: "area" takes seven integer arguments (one integer
       and three pairs). These arguments describe (1) the width of the
       (hypothetical) source array, (2) the height and width of the
       subarea, as well as (3&4) an anchor point in each of the two
       arrays (see illustration above):

         source_width   : width  of the (hypothetical) source
                          array to be subscribed into
         source_anchor_y,
         source_anchor_x: position of the anchor point relative
                          to ORIGIN OF THE SOURCE ARRAY

         area_heigh  t  : height of the subarea to be subscribed
         area_width     : width  of the subarea to be subscribed
         area_anchor_y,
         area_anchor_x  : position of the anchor point relative
                          to ORIGIN OF THE SUBAREA

  Out: "area" returns an array of ONEDIMENSIONAL indices:

         [1d-indices]   : flat integer array containing the indices
                          that can be used to subscript the
                          (hypothetical) source array in order to
                          access the desired subarea.

                          Indices are onedimensional, and are returned
                          in standard NEST (monotonic) counting order.
                          (For information on the order of
                          subscription in NEST arrays, see references
                          below.)

Examples:
  (Examples are illustrated):

  Ex. 1: source array: (height=5), width=10, anchor=(0,0)
         subarea     :  height=3, width= 3, anchor=(0,0)
         xoo.......
         ooo.......
         ooo.......
         ..........
         ..........

         10 0 0  3 3 0 0 area -> [0 1 2  10 11 12  20 21 22]

  Ex. 1b:source array: (height=5), width=10, anchor=(2,2)
         subarea     :  height=3, width= 3, anchor=(2,2)
         ooo.......
         ooo.......
         oox.......
         ..........
         ..........

         10 2 2  3 3 2 2 area -> [0 1 2  10 11 12  20 21 22]

  Ex. 1c:Note that anchor point may lie outside both
         arrays' bounds:
         source array: (height=5), width=10, anchor=(1,12)
         subarea     :  height=3, width= 3, anchor=(1,12)
         ooo.......
         ooo.......  x
         ooo.......
         ..........
         ..........

         10 1 12  3 3 1 12 area -> [0 1 2  10 11 12  20 21 22]

  Ex. 2: source array: (height=6), width=15, anchor=(2,5)
         subarea     :  height=4, width= 5, anchor=(1,3)
         ...............
         ..ooooo........
         ..oooxo........
         ..ooooo........
         ..ooooo........
         ...............

         15 2 5  4 5 1 3 area -> [17 18 19 20 21
                                  32 33 34 35 36
                                  47 48 49 50 51
                                  62 63 64 65 66]


Diagnostics:
  May raise the following SLI interpreter errors:
    StackUnderflowError
    ArgumentTypeError

  NO ARGUMENT RANGE CHECK IS PERFORMED.
  The result may be useless if subarea is not
  contained in the source array. Note that THIS
  RESTRICTION DOES NOT APPLY TO FUNCTION "area2".

  However, anchor points may lie outside the array
  bounds.

  Note that the height of the source array is not used in computation,
  and does not appear in the parameter list

Author: Ruediger Kupper

References: (TO BE DONE: NEST layer indexing conventions)

SeeAlso: area2
*/
void SLIArrayModule::AreaFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<7 )
  {
    i->message(SLIInterpreter::M_ERROR, "area","Too few parameters supplied.");
    i->message(SLIInterpreter::M_ERROR, "area","Usage: sw say sax  ah aw aay aax  area");
    i->message(SLIInterpreter::M_ERROR, "area","where:  sw : source array width");
    i->message(SLIInterpreter::M_ERROR, "area","        say: source array anchor y position");
    i->message(SLIInterpreter::M_ERROR, "area","        sax: source array anchor x position");
    i->message(SLIInterpreter::M_ERROR, "area","        ah : subregion height");
    i->message(SLIInterpreter::M_ERROR, "area","        aw : subregion width");
    i->message(SLIInterpreter::M_ERROR, "area","        aay: subregion anchor y position");
    i->message(SLIInterpreter::M_ERROR, "area","        aax: subregion anchor x position");
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  //  IntegerDatum* s_h_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(7).datum());
  IntegerDatum* s_w_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(6).datum());
  IntegerDatum* s_y_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(5).datum());
  IntegerDatum* s_x_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(4).datum());

  IntegerDatum* a_h_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(3).datum());
  IntegerDatum* a_w_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(2).datum());
  IntegerDatum* a_y_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(1).datum());
  IntegerDatum* a_x_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(0).datum());

//   if(s_h_d == NULL)
//   {
//     i->raiseerror(i->ArgumentTypeError);
//     return;
//   }
  if(s_w_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(s_y_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(s_x_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(a_h_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(a_w_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(a_y_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(a_x_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  // not needed for computation: long const s_h = s_h_d->get();
  long const s_w = s_w_d->get();
  long const s_y = s_y_d->get();
  long const s_x = s_x_d->get();

  long const a_h = a_h_d->get();
  long const a_w = a_w_d->get();
  long const a_y = a_y_d->get();
  long const a_x = a_x_d->get();

  TokenArray indices;
  indices.reserve(a_w * a_h);

  // compute upper left corner in source array:
  long const s_0_y = s_y-a_y;
  long const s_0_x = s_x-a_x;

  for(long y=0; y<a_h; ++y)
    {
      for(long x=0; x<a_w; ++x)
        {
          indices.push_back(s_0_x + s_0_y*s_w  +  x + y*s_w);
        }
    }

  i->OStack.pop(7);
  i->OStack.push_by_pointer(new ArrayDatum(indices));
  i->EStack.pop();
}

/* BeginDocumentation

Name: area2 - Return array of indices defining a 2d subarea of a 2d array.

Synopsis:
                             source_anchor_y source_anchor_x
    area_height   area_width   area_anchor_y   area_anchor_x
                                                       area2 -> [2d-indices]

Description:
  Given a -- hypothetical -- twodimensional array,
  "area" tells you, what indices you need to
  subscript a contiguous, twodimensional subarea.

  The subarea is defined by specifying it's size
  (width and height), as well as its location in the
  source array. The location is defined by specifying
  an anchor point in the source array as well as in
  the subarea. Anchor points are matched, see
  illustration, and examples below:

  source array: height=6, width=15, anchor=(2,5)
  subarea     : height=4, width= 5, anchor=(1,3)
  ...............
  ..ooooo........
  ..oooxo........
  ..ooooo........
  ..ooooo........
  ...............


  "area2" returns an array of TWODIMENSIONAL indices.
  There is a SLI function called "area" returning
  onedimensional indices, as well as the conversion
  functions "cv1d" and "cv2d".
  (For information on the order of subscription in NEST
  arrays, see references below.)

Parameters:
   In: "area2" takes six integer arguments (three pairs).
       These arguments describe (1) the height and width of the
       subarea to be indexed in the (hypothetical) source array, as
       well as (2&3) an anchor point in each of the two arrays (see
       illustration above):

         source_anchor_y,
         source_anchor_x: position of the anchor point relative
                          to ORIGIN OF THE SOURCE ARRAY

         area_heigh  t  : height of the subarea to be subscribed
         area_width     : width  of the subarea to be subscribed
         area_anchor_y,
         area_anchor_x  : position of the anchor point relative
                          to ORIGIN OF THE SUBAREA

  Out: "area" returns an array of ONEDIMENSIONAL indices:

         [2d-indices]   : flat integer array containing the indices
                          that can be used to subscript the
                          (hypothetical) source array in order to
                          access the desired subarea.

                          Indices are twodimensional. The returned
                          array is flat and has the following order:
                          [1y 1x  2y 2x  3y 3x  ...  ny nx]
                          That is, each pair of numbers indicates the
                          y- and the x-component of a respective
                          index.

                          The indices 1..n are returned in standard
                          NEST counting order. (For information on the
                          order of subscription in NEST arrays, see
                          references below.)

Examples:
  (Examples are illustrated):

  Ex. 1: source array: (height=5), (width=10), anchor=(0,0)
         subarea     :  height=3,   width= 3,  anchor=(0,0)
         xoo.......
         ooo.......
         ooo.......
         ..........
         ..........

         0 0  3 3 0 0 area2 -> [0 0  0 1  0 2
                                1 0  1 1  1 2
                                2 0  2 1  2 2]

  Ex. 1b:source array: (height=5), (width=10), anchor=(2,2)
         subarea     :  height=3,   width= 3,  anchor=(2,2)
         ooo.......
         ooo.......
         oox.......
         ..........
         ..........

         2 2  3 3 2 2 area2 -> [0 0  0 1  0 2
                                1 0  1 1  1 2
                                2 0  2 1  2 2]

  Ex. 1c:Note that anchor point may lie outside both
         arrays' bounds:
         source array: (height=5), (width=10), anchor=(1,12)
         subarea     :  height=3,   width= 3,  anchor=(1,12)
         ooo.......
         ooo.......  x
         ooo.......
         ..........
         ..........

         1 12  3 3 1 12 area2 -> [0 0  0 1  0 2
                                  1 0  1 1  1 2
                                  2 0  2 1  2 2]

  Ex. 2: source array: (height=6), (width=15), anchor=(2,5)
         subarea     :  height=4,   width= 5,  anchor=(1,3)
         ...............
         ..ooooo........
         ..oooxo........
         ..ooooo........
         ..ooooo........
         ...............

         2 5  4 5 1 3 area2 -> [1 2  1 3  1 4  1 5  1 6
                                2 2  2 3  2 4  2 5  2 6
                                3 2  3 3  3 4  3 5  3 6
                                4 2  4 3  4 4  4 5  4 6]

  Ex. 3: Note that subarea doesn't need to lie
         inside bounds of source array:
         source array: (height=4), (width= 8), anchor=(4,-1)
         subarea     :  height=2,   width= 3,  anchor=(1, 0)
         ........
         ........
         ........
        ooo......
        xoo


         4 -1  2 3 1 0 area2 -> [3 -1  3 0  3 1
                                 4 -1  4 0  4 1]

Diagnostics:
  "area2" may raise the following SLI interpreter errors:
    StackUnderflowError
    ArgumentTypeError

  No argument range check is performed. The returned
  indices may indicate regions outside the source
  array (see Example 3). This is not a bug, it's a
  feature :-). Note that restrictions apply to the
  related function "area".

  However, anchor points may lie outside the array
  bounds.

  Note that arguments source_width and source_height are not used in
  computation, and do not appear in the argument list.

Author: Ruediger Kupper

References: (TO BE DONE: NEST layer indexing conventions)

SeeAlso: area
*/
void SLIArrayModule::Area2Function::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<6 )
  {
    i->message(SLIInterpreter::M_ERROR, "area2","Too few parameters supplied.");
    i->message(SLIInterpreter::M_ERROR, "area2","Usage: say sax  ah aw aay aax  area2");
    i->message(SLIInterpreter::M_ERROR, "area2","where:  say: source array anchor y position");
    i->message(SLIInterpreter::M_ERROR, "area2","        sax: source array anchor x position");
    i->message(SLIInterpreter::M_ERROR, "area2","        ah : subregion height");
    i->message(SLIInterpreter::M_ERROR, "area2","        aw : subregion width");
    i->message(SLIInterpreter::M_ERROR, "area2","        aay: subregion anchor y position");
    i->message(SLIInterpreter::M_ERROR, "area2","        aax: subregion anchor x position");
    i->raiseerror(i->StackUnderflowError);
    return;
  }
//   IntegerDatum* s_h_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(7).datum());
//   IntegerDatum* s_w_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(6).datum());
  IntegerDatum* s_y_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(5).datum());
  IntegerDatum* s_x_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(4).datum());

  IntegerDatum* a_h_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(3).datum());
  IntegerDatum* a_w_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(2).datum());
  IntegerDatum* a_y_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(1).datum());
  IntegerDatum* a_x_d    = dynamic_cast<IntegerDatum*>(i->OStack.pick(0).datum());

//   if(s_h_d == NULL)
//   {
//     i->raiseerror(i->ArgumentTypeError);
//     return;
//   }
//   if(s_w_d == NULL)
//   {
//     i->raiseerror(i->ArgumentTypeError);
//     return;
//   }
  if(s_y_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(s_x_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(a_h_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(a_w_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(a_y_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(a_x_d == NULL)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  // not needed for computation: long const s_h = s_h_d->get();
  // not needed for computation: long const s_w = s_w_d->get();
  long const s_y = s_y_d->get();
  long const s_x = s_x_d->get();

  long const a_h = a_h_d->get();
  long const a_w = a_w_d->get();
  long const a_y = a_y_d->get();
  long const a_x = a_x_d->get();

  TokenArray indices;
  indices.reserve(a_w * a_h);

  // compute upper left corner in source array:
  long const s_0_y = s_y-a_y;
  long const s_0_x = s_x-a_x;

  for(long y=0; y<a_h; ++y)
    {
      for(long x=0; x<a_w; ++x)
        {
          indices.push_back(s_0_y + y);
          indices.push_back(s_0_x + x);
        }
    }

  i->OStack.pop(6);
  i->OStack.push_by_pointer(new ArrayDatum(indices));
  i->EStack.pop();
}

/* BeginDocumentation
Name: cv1d - convert 2-dimensional coordinates to 1-dim index
Synopsis: y   x   w  cv1d -> i

Description: This function converts a 2-dimensional matrix address to
the corresponding index of a linear array.  Useful if you have to handle
2-dimensional data (e.g. an image) which is stored in a linear array.
Note that by convention the origin is 0,0 and at the upper left corner.

Parameters: y : integer. the y-coordinate
x : integer. the x coordinate
w : integer. the border with of the 2-dimensional coordinate system

Example: 3 2 4 cv1d -> 14
3 is the y-coordinate (row)
2 is the x coordinate (column)
4 is the number of columns
14 is the index of the corresponding element in a 1-dimensional array

SeeAlso: cst, cva, cv2d, cvd, cvi, cvlit, cvn, cvs, cvt_a
*/
void SLIArrayModule::Cv1dFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<3 )
    {
      i->message(SLIInterpreter::M_ERROR, "cv1d","Too few parameters supplied.");
      i->message(SLIInterpreter::M_ERROR, "cv1d","Usage: y x w cv1d");
      i->raiseerror(i->StackUnderflowError);
    return;
  }

  IntegerDatum *w= dynamic_cast<IntegerDatum*>(i->OStack.pick(0).datum());
  IntegerDatum *x= dynamic_cast<IntegerDatum*>(i->OStack.pick(1).datum());
  IntegerDatum *y= dynamic_cast<IntegerDatum*>(i->OStack.pick(2).datum());

  if(w == NULL)
  {
    i->message(SLIInterpreter::M_ERROR, "cv1d","integertype expected");
    i->message(SLIInterpreter::M_ERROR, "cv1d","Usage: y x w cv1d");
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  if(x == NULL)
  {
    i->message(SLIInterpreter::M_ERROR, "cv1d","integertype expected");
    i->message(SLIInterpreter::M_ERROR, "cv1d","Usage: y x w cv1d");
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  if(y == NULL)
  {
    i->message(SLIInterpreter::M_ERROR, "cv1d","integertype expected");
    i->message(SLIInterpreter::M_ERROR, "cv1d","Usage: y x w cv1d");
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  // y= y*w + x
  y->get()*=(w->get());
  y->get()+= (x->get());
  i->OStack.pop(2);
  i->EStack.pop();
  // no i->OStack.push(), because we change the objects directly
  // on the stack. Low overhead.
}

/* BeginDocumentation
Name: cv2d - convert 1-dimensional index to 2-dim coordinate
Synopsis: i  w  cv2d -> y   x
int int        int int

Description:This function transforms an array index to y,x
coordinate pair. Useful if you have intrinsically 2-dimensional
data stored in a linear array (e.g. images).

Parameters:i : integer. the index in the array
h : integer. the number of rows in the 2-dimensional space
w : integer. the number of columns in the 2-dimensional space


Example:14 4 cv2d -> 3 2
14 is the index of the corresponding element in a 1-dimensional array
4 is the number of columns
3 is the resulting y-coordinate (row)
2 is the resulting x-coordinate (column)

SeeAlso: cst, cva, cv1d, cvd, cvi, cvlit, cvn, cvs, cvt_a
*/
void SLIArrayModule::Cv2dFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2 )
    {
      i->message(SLIInterpreter::M_ERROR, "cv2d","Too few parameters supplied.");
      i->message(SLIInterpreter::M_ERROR, "cv2d","Usage: i w cv2d");
      i->raiseerror(i->StackUnderflowError);
      return;
    }

  IntegerDatum *w  = dynamic_cast<IntegerDatum*>(i->OStack.pick(0).datum());
  IntegerDatum *in = dynamic_cast<IntegerDatum*>(i->OStack.pick(1).datum());

  if(w == NULL)
  {
    i->message(SLIInterpreter::M_ERROR, "cv2d","integertype expected");
    i->message(SLIInterpreter::M_ERROR, "cv2d","Usage: i w cv2d");
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  if(in == NULL)
  {
    i->message(SLIInterpreter::M_ERROR, "cv2d","integertype expected");
    i->message(SLIInterpreter::M_ERROR, "cv2d","Usage: i w cv2d");
    i->raiseerror(i->ArgumentTypeError);
    return;
  }


  long tmp = in->get();
  //  y = i / w
  (in->get()) /= (w->get());
  // x = i % w
  *w = tmp % w->get();
  i->EStack.pop();
  // no i->OStack.push(), because we change the objects directly
  // on the stack. Low overhead.
}


/* BeginDocumentation
Name: GetMax - get maximal element
Synopsis: array GetMax -> int
Description: returns the maximum value in an array of ints.
SeeAlso: GetMin
Remarks: works only for integer arrays.
*/
void SLIArrayModule::GetMaxFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1 )
    {
      i->message(SLIInterpreter::M_ERROR, "GetMax","Too few parameters supplied.");
      i->message(SLIInterpreter::M_ERROR, "GetMax","Usage: <array> GetMax");
      i->raiseerror(i->StackUnderflowError);
      return;
    }

  ArrayDatum *a=dynamic_cast<ArrayDatum*>(i->OStack.top().datum());
  if (a==NULL)
    {
      i->message(SLIInterpreter::M_ERROR, "GetMax","argument must be an array");
      i->raiseerror(i->ArgumentTypeError);
      return;
    }

  IntegerDatum * tmp = dynamic_cast<IntegerDatum*>(a->begin()->datum());
  if (tmp==NULL)
    {
      i->message(SLIInterpreter::M_ERROR, "GetMax","argument array may only contain integers");
      i->raiseerror(i->ArgumentTypeError);
      return;
    }

  IntegerDatum * tmp2;
  unsigned int pos=0;
  while (pos < a->size())
    {
      tmp2 = dynamic_cast<IntegerDatum*>(a->get(pos).datum());
      if (tmp2==NULL)
	{
	  i->message(SLIInterpreter::M_ERROR, "GetMax","argument array may only contain integers");
	  i->raiseerror(i->ArgumentTypeError);
	  return;
	}
      if (tmp->get() < tmp2->get()) tmp=tmp2;
      ++pos;
    }
  Token result(*tmp);

  i->OStack.pop();
  i->OStack.push(result);
  i->EStack.pop();
}

/* BeginDocumentation
Name: GetMin - get minimal element
Synopsis: array GetMin -> int
Description: returns the minimum value in an array of ints.
Remarks: works only for integer arrays.
SeeAlso: GetMax
*/
void SLIArrayModule::GetMinFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1 )
    {
      i->message(SLIInterpreter::M_ERROR, "GetMin","Too few parameters supplied.");
      i->message(SLIInterpreter::M_ERROR, "GetMin","Usage: <array> GetMin");
      i->raiseerror(i->StackUnderflowError);
      return;
    }

  ArrayDatum *a=dynamic_cast<ArrayDatum*>(i->OStack.top().datum());
  if (a==NULL)
    {
      i->message(SLIInterpreter::M_ERROR, "GetMin","argument must be an array");
      i->raiseerror(i->ArgumentTypeError);
      return;
    }


  IntegerDatum * tmp = dynamic_cast<IntegerDatum*>(a->begin()->datum());
  if (tmp==NULL)
    {
      i->message(SLIInterpreter::M_ERROR, "GetMin","argument array may only contain integers");
      i->raiseerror(i->ArgumentTypeError);
      return;
    }

  IntegerDatum * tmp2;
  unsigned int pos=0;
  while (pos < a->size())
    {
      tmp2 = dynamic_cast<IntegerDatum*>(a->get(pos).datum());
      if (tmp2==NULL)
	{
	  i->message(SLIInterpreter::M_ERROR, "GetMin","argument array may only contain integers");
	  i->raiseerror(i->ArgumentTypeError);
	  return;
	}
      if (tmp->get() > tmp2->get()) tmp=tmp2;
      ++pos;
    }
  Token result(*tmp);

  i->OStack.pop();
  i->OStack.push(result);
  i->EStack.pop();
}

/* BeginDocumentation
Name: gabor_ - Return 2D array with Gabor patch.
Synopsis:
nr nc xmin xmax ymin ymax lambda orient phase sigma el
Description:
Returns an nr by nc matrix with a Gabor patch, computed over the
argument range of [xmin,xmax] by [ymin,ymax].
This function is the low level variant of the more user-friendly
GaborPatch.
SeeAlso: arr::GaborPatch
Author: Marc-Oliver Gewaltig
References: Petkov N and Kruizinga P: Biol. Cybern. 76, 83-96 (1997)
*/
void SLIArrayModule::GaborFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load() < 11)
  {
    i->raiseerror("StackUnderflow");
    return;
  }

  long   nrow=0;
  long   ncol=0;
  double xmin=0.0;
  double xmax=0.0;
  double ymin=0.0;
  double ymax=0.0;
  double lambda=0.0;
  double phi=0.0;
  double phase=0.0;
  double sigma=0.0;
  double gamma=0.0;

  try
  {
    nrow=  getValue<long>(i->OStack.pick(10));
    ncol=  getValue<long>(i->OStack.pick(9));
    xmin=  getValue<double>(i->OStack.pick(8));
    xmax=  getValue<double>(i->OStack.pick(7));
    ymin=  getValue<double>(i->OStack.pick(6));
    ymax=  getValue<double>(i->OStack.pick(5));
    lambda=getValue<double>(i->OStack.pick(4));
    phi=   getValue<double>(i->OStack.pick(3));
    phase= getValue<double>(i->OStack.pick(2));
    sigma= getValue<double>(i->OStack.pick(1));
    gamma= getValue<double>(i->OStack.pick(0));
  }
  catch(...)
  {
    i->raiseerror("ArgumentType");
    return;
  }
  if(ymin>=ymax)
  {
    i->message(SLIInterpreter::M_ERROR, "Gabor_","y_max must be > y_min.");
    i->raiseerror("RangeCheck");
    return;
  }
  if(xmin>=xmax)
  {
    i->message(SLIInterpreter::M_ERROR, "Gabor_","x_max must be > x_min.");
    i->raiseerror("RangeCheck");
    return;
  }
  if((ncol<2) || (nrow<2))
  {
    i->message(SLIInterpreter::M_ERROR, "Gabor_","Matrix must have at least two rows and two columns.");
    i->raiseerror("RangeCheck");
    return;
  }

  assert(ymax>ymin);
  assert(xmax>xmin);
  assert(ncol>1);
  assert(nrow>1);

  const double sig_sq = 2.0*sigma*sigma;
  const double gam_sq = gamma*gamma;
  const double cos_phi = std::cos(phi);
  const double sin_phi = std::sin(phi);
  const double s_fact = 2.0*numerics::pi*std::sin(phi)/lambda;
  const double c_fact = 2.0*numerics::pi*std::cos(phi)/lambda;
  const double dx= (xmax-xmin)/(ncol-1.0);
  const double dy= (ymax-ymin)/(nrow-1.0);

  ArrayDatum result;
  result.reserve(nrow);

  std::vector<double> col(ncol);
  for(size_t r=0; r< (size_t)nrow; ++r)
  {
    const double y=ymin + r*dy;
    for(size_t c=0; c< (size_t)ncol; ++c)
    {
      const double x=xmin + c*dx;
      const double x1=x*cos_phi - y*sin_phi;
      const double y1=x*sin_phi + y*cos_phi;
      const double x2=x*c_fact - y*s_fact;

      col[c]= std::exp(-(x1*x1 + gam_sq*y1*y1)/sig_sq) * std::cos(x2 - phase);
    }
    result.push_back(new ArrayDatum(col));
  }
  i->OStack.pop(11);
  i->OStack.push(result);
  i->EStack.pop();
}

/* BeginDocumentation
Name: gauss2d_ - Return 2D array with Gauss patch.
Synopsis:
nr nc xmin xmax ymin ymax phi sigma gamma
Description:
Returns an nr by nc matrix with a Gauss patch, computed over the
argument range of [xmin,xmax] by [ymin,ymax].
This function is the low level variant of the more user-friendly
GaussPatch.
SeeAlso: arr::GaussPatch
Author: Marc-Oliver Gewaltig
*/
void SLIArrayModule::Gauss2dFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load() < 9)
  {
    i->raiseerror("StackUnderflow");
    return;
  }

  long   nrow=0;
  long   ncol=0;
  double xmin=0.0;
  double xmax=0.0;
  double ymin=0.0;
  double ymax=0.0;
  double phi=0.0;
  double sigma=0.0;
  double gamma=0.0;

  try
  {
    nrow=  getValue<long>(i->OStack.pick(8));
    ncol=  getValue<long>(i->OStack.pick(7));
    xmin=  getValue<double>(i->OStack.pick(6));
    xmax=  getValue<double>(i->OStack.pick(5));
    ymin=  getValue<double>(i->OStack.pick(4));
    ymax=  getValue<double>(i->OStack.pick(3));
    phi=   getValue<double>(i->OStack.pick(2));
    sigma= getValue<double>(i->OStack.pick(1));
    gamma= getValue<double>(i->OStack.pick(0));
  }
  catch(...)
  {
    i->raiseerror("ArgumentType");
    return;
  }
  if(ymin>=ymax)
  {
    i->message(SLIInterpreter::M_ERROR, "gauss2d_","y_max must be > y_min.");
    i->raiseerror("RangeCheck");
    return;
  }
  if(xmin>=xmax)
  {
    i->message(SLIInterpreter::M_ERROR, "gauss2s_","x_max must be > x_min.");
    i->raiseerror("RangeCheck");
    return;
  }
  if((ncol<2) || (nrow<2))
  {
    i->message(SLIInterpreter::M_ERROR, "gauss2d_","Matrix must have at least two rows and two columns.");
    i->raiseerror("RangeCheck");
    return;
  }

  assert(ymax>ymin);
  assert(xmax>xmin);
  assert(ncol>1);
  assert(nrow>1);

  const double sig_sq = 2.0*sigma*sigma;
  const double gam_sq = gamma*gamma;
  const double dx= (xmax-xmin)/(ncol-1.0);
  const double dy= (ymax-ymin)/(nrow-1.0);
  const double cos_phi = std::cos(phi);
  const double sin_phi = std::sin(phi);

  ArrayDatum result;
  result.reserve(nrow);

  std::vector<double> col(ncol);
  for(size_t r=0; r< (size_t)nrow; ++r)
  {
    const double y=ymin + r*dy;
    col.assign(ncol,0.0); // clear contents
    for(size_t c=0; c< (size_t)ncol; ++c)
    {
      const double x=xmin + c*dx;
      const double x1=x*cos_phi - y*sin_phi;
      const double y1=x*sin_phi + y*cos_phi;

      col[c]= std::exp(-(x1*x1 + gam_sq*y1*y1)/sig_sq);
    }
    result.push_back(new ArrayDatum(col));
  }
  i->OStack.pop(9);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Array2IntVectorFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  try
  {
    IntVectorDatum ivd( new std::vector<long>(getValue<std::vector<long> >(i->OStack.top())));
    i->OStack.pop();
    i->OStack.push(ivd);
  }
  catch(...)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  i->EStack.pop();
}

void SLIArrayModule::Array2DoubleVectorFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  try
  {
    DoubleVectorDatum ivd( new std::vector<double>(getValue<std::vector<double> >(i->OStack.top())));
    i->OStack.pop();
    i->OStack.push(ivd);
  }
  catch(...)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  i->EStack.pop();

}

void SLIArrayModule::IntVector2ArrayFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntVectorDatum *ivd= dynamic_cast<IntVectorDatum *>(i->OStack.top().datum());
  if(ivd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  ArrayDatum ad(**ivd);
  i->OStack.pop();
  i->OStack.push(ad);
  i->EStack.pop();
}

void SLIArrayModule::Add_iv_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntVectorDatum *ivd1= dynamic_cast<IntVectorDatum *>(i->OStack.top().datum());
  if(ivd1 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntVectorDatum *ivd2= dynamic_cast<IntVectorDatum *>(i->OStack.pick(1).datum());
  if(ivd2 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if((*ivd1)->size() != (*ivd2)->size())
  {
      i->message(SLIInterpreter::M_ERROR, "add_iv_iv","You can only add vectors of the same length.");
      i->raiseerror("RangeCheck");
  }

  IntVectorDatum *result=new IntVectorDatum( new std::vector<long>(**ivd1));
  const size_t length=(**ivd1).size();
  for( size_t j=0; j< length; ++j)
      (**result)[j] += (**ivd2)[j];

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Add_i_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntegerDatum *id= dynamic_cast<IntegerDatum *>(i->OStack.pick(1).datum());
  if(id == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntVectorDatum *ivd= dynamic_cast<IntVectorDatum *>(i->OStack.pick(0).datum());
  if(ivd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  IntVectorDatum *result=new IntVectorDatum( new std::vector<long>(**ivd));
  const size_t length=(**ivd).size();
  const long value=id->get();
  for( size_t j=0; j< length; ++j)
      (**result)[j] += value;

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Neg_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntVectorDatum *ivd= dynamic_cast<IntVectorDatum *>(i->OStack.pick(1).datum());
  if(ivd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t length=(**ivd).size();
  IntVectorDatum *result=new IntVectorDatum( new std::vector<long>(length));
  for( size_t j=0; j< length; ++j)
      (**result)[j] = - (**ivd)[j];

  i->OStack.pop();
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Sub_iv_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntVectorDatum *ivd1= dynamic_cast<IntVectorDatum *>(i->OStack.top().datum());
  if(ivd1 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntVectorDatum *ivd2= dynamic_cast<IntVectorDatum *>(i->OStack.pick(1).datum());
  if(ivd2 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if((**ivd1).size() != (**ivd2).size())
  {
      i->message(SLIInterpreter::M_ERROR, "sub_iv_iv","You can only subtract vectors of the same length.");
      i->raiseerror("RangeCheck");
  }

  IntVectorDatum *result=new IntVectorDatum( new std::vector<long>(**ivd1));
  const size_t length=(**ivd1).size();
  for( size_t j=0; j< length; ++j)
      (**result)[j] -= (**ivd2)[j];

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Mul_iv_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntVectorDatum *ivd1= dynamic_cast<IntVectorDatum *>(i->OStack.top().datum());
  if(ivd1 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntVectorDatum *ivd2= dynamic_cast<IntVectorDatum *>(i->OStack.pick(1).datum());
  if(ivd2 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if((**ivd1).size() != (**ivd2).size())
  {
      i->message(SLIInterpreter::M_ERROR, "mul_iv_iv","You can only multiply vectors of the same length.");
      i->raiseerror("RangeCheck");
  }

  IntVectorDatum *result=new IntVectorDatum( new std::vector<long>(**ivd1));
  const size_t length=(**ivd1).size();
  for( size_t j=0; j< length; ++j)
      (**result)[j]*= (**ivd2)[j];

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}


void SLIArrayModule::Mul_i_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntegerDatum *id= dynamic_cast<IntegerDatum *>(i->OStack.pick(1).datum());
  if(id == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntVectorDatum *ivd= dynamic_cast<IntVectorDatum *>(i->OStack.pick(0).datum());
  if(ivd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  IntVectorDatum *result=new IntVectorDatum( new std::vector<long>(**ivd));
  const size_t length=(**ivd).size();
  const long factor=id->get();
  for( size_t j=0; j< length; ++j)
      (**result)[j]*= factor;

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Mul_d_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleDatum *dd= dynamic_cast<DoubleDatum *>(i->OStack.pick(1).datum());
  if(dd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntVectorDatum *ivd= dynamic_cast<IntVectorDatum *>(i->OStack.pick(0).datum());
  if(ivd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t length=(**ivd).size();
  DoubleVectorDatum *result=new DoubleVectorDatum( new std::vector<double>(length));
  const double factor=dd->get();
  for( size_t j=0; j< length; ++j)
      (**result)[j] = factor*static_cast<double>((**ivd)[j]);

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Div_iv_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntVectorDatum *ivd1= dynamic_cast<IntVectorDatum *>(i->OStack.top().datum());
  if(ivd1 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntVectorDatum *ivd2= dynamic_cast<IntVectorDatum *>(i->OStack.pick(1).datum());
  if(ivd2 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if((**ivd1).size() != (**ivd2).size())
  {
      i->message(SLIInterpreter::M_ERROR, "div_iv_iv","You can only divide vectors of the same length.");
      i->raiseerror("RangeCheck");
  }

  IntVectorDatum *result=new IntVectorDatum( new std::vector<long>(**ivd1));
  const size_t length=(**ivd1).size();
  for( size_t j=0; j< length; ++j)
  {
      const long quotient=(**ivd2)[j];
      if(quotient ==0)
      {
          delete result;
          i->message(SLIInterpreter::M_ERROR, "div_iv","Vector element zero encountered.");
          i->raiseerror("DivisionByZero");
          return;
      }
      (**result)[j] /=quotient;
  } 

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Length_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntVectorDatum *ivd= dynamic_cast<IntVectorDatum *>(i->OStack.top().datum());
  if(ivd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t length=(**ivd).size();

  i->OStack.pop();
  i->OStack.push(new IntegerDatum(length));
  i->EStack.pop();
}


void SLIArrayModule::Add_dv_dvFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleVectorDatum *dvd1= dynamic_cast<DoubleVectorDatum *>(i->OStack.top().datum());
  if(dvd1 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  DoubleVectorDatum *dvd2= dynamic_cast<DoubleVectorDatum *>(i->OStack.pick(1).datum());
  if(dvd2 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if((**dvd1).size() != (**dvd2).size())
  {
      i->message(SLIInterpreter::M_ERROR, "add_dv_dv","You can only add vectors of the same length.");
      i->raiseerror("RangeCheck");
  }

  DoubleVectorDatum *result=new DoubleVectorDatum( new std::vector<double>(**dvd1));
  const size_t length=(**dvd1).size();
  for( size_t j=0; j< length; ++j)
      (**result)[j]+= (**dvd2)[j];

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Sub_dv_dvFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleVectorDatum *dvd1= dynamic_cast<DoubleVectorDatum *>(i->OStack.top().datum());
  if(dvd1 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  DoubleVectorDatum *dvd2= dynamic_cast<DoubleVectorDatum *>(i->OStack.pick(1).datum());
  if(dvd2 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if((**dvd1).size() != (**dvd2).size())
  {
      i->message(SLIInterpreter::M_ERROR, "sub_dv_dv","You can only subtract vectors of the same length.");
      i->raiseerror("RangeCheck");
  }

  DoubleVectorDatum *result=new DoubleVectorDatum( new std::vector<double>(**dvd1));
  const size_t length=(**dvd1).size();
  for( size_t j=0; j< length; ++j)
      (**result)[j]-= (**dvd2)[j];

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Add_d_dvFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleDatum *dd= dynamic_cast<DoubleDatum *>(i->OStack.pick(1).datum());
  if(dd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  DoubleVectorDatum *dvd= dynamic_cast<DoubleVectorDatum *>(i->OStack.pick(0).datum());
  if(dvd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  DoubleVectorDatum *result=new DoubleVectorDatum( new std::vector<double>(**dvd));
  const size_t length=(**dvd).size();
  const double value=(*dd).get();

  for( size_t j=0; j< length; ++j)
      (**result)[j]+= value;

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}


void SLIArrayModule::Mul_d_dvFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleDatum *dd= dynamic_cast<DoubleDatum *>(i->OStack.pick(1).datum());
  if(dd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  DoubleVectorDatum *dvd= dynamic_cast<DoubleVectorDatum *>(i->OStack.pick(0).datum());
  if(dvd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  DoubleVectorDatum *result=new DoubleVectorDatum( new std::vector<double>(**dvd));
  const size_t length=(**dvd).size();
  const double value=(*dd).get();

  for( size_t j=0; j< length; ++j)
      (**result)[j]*= value;

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}


void SLIArrayModule::Neg_dvFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleVectorDatum *dvd= dynamic_cast<DoubleVectorDatum *>(i->OStack.top().datum());
  if(dvd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t length=(**dvd).size();
  DoubleVectorDatum *result=new DoubleVectorDatum( new std::vector<double>(length));
  for( size_t j=0; j< length; ++j)
      (**result)[j] = - (**dvd)[j];

  i->OStack.pop();
  i->OStack.push(result);
  i->EStack.pop();
}


void SLIArrayModule::Inv_dvFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleVectorDatum *dvd= dynamic_cast<DoubleVectorDatum *>(i->OStack.top().datum());
  if(dvd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t length=(**dvd).size();
  DoubleVectorDatum *result=new DoubleVectorDatum( new std::vector<double>(length));
  for( size_t j=0; j< length; ++j)
  {
      const double &val=(**dvd)[j];
      if(val*val < 1.0e-100)
      {
          delete result;
          i->message(SLIInterpreter::M_ERROR, "inv_dv","Vector element (near) zero encountered.");
          i->raiseerror("DivisionByZero");
          return;
      }
      (**result)[j] = 1.0/val;
  }
  i->OStack.pop();
  i->OStack.push(result);
  i->EStack.pop();
}


void SLIArrayModule::Mul_dv_dvFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleVectorDatum *dvd1= dynamic_cast<DoubleVectorDatum *>(i->OStack.top().datum());
  if(dvd1 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  DoubleVectorDatum *dvd2= dynamic_cast<DoubleVectorDatum *>(i->OStack.pick(1).datum());
  if(dvd2 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if((**dvd1).size() != (**dvd2).size())
  {
      i->message(SLIInterpreter::M_ERROR, "mul_dv_dv","You can only multiply vectors of the same length.");
      i->raiseerror("RangeCheck");
  }

  DoubleVectorDatum *result=new DoubleVectorDatum( new std::vector<double>(**dvd1));
  const size_t length=(**dvd1).size();
  for( size_t j=0; j< length; ++j)
      (**result)[j] *= (**dvd2)[j];

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Div_dv_dvFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleVectorDatum *dvd1= dynamic_cast<DoubleVectorDatum *>(i->OStack.top().datum());
  if(dvd1 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  DoubleVectorDatum *dvd2= dynamic_cast<DoubleVectorDatum *>(i->OStack.pick(1).datum());
  if(dvd2 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if((**dvd1).size() != (**dvd2).size())
  {
      i->message(SLIInterpreter::M_ERROR, "div_iv_iv","You can only divide vectors of the same length.");
      i->raiseerror("RangeCheck");
  }

  DoubleVectorDatum *result=new DoubleVectorDatum( new std::vector<double>(**dvd1));
  const size_t length=(**dvd1).size();
  for( size_t j=0; j< length; ++j)
  {
      const double quotient=(**dvd2)[j];
      if(quotient*quotient < 1e-100)
      {
          delete result;
          i->message(SLIInterpreter::M_ERROR, "div_dv","Vector element (near) zero encountered.");
          i->raiseerror("DivisionByZero");
          return;
      }
      (**result)[j] /=quotient;
  } 

  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Length_dvFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleVectorDatum *dvd1= dynamic_cast<DoubleVectorDatum *>(i->OStack.top().datum());
  if(dvd1 == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t length=(**dvd1).size();

  i->OStack.pop();
  i->OStack.push(new IntegerDatum(length));
  i->EStack.pop();
}

void SLIArrayModule::Get_dv_iFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntegerDatum *id= dynamic_cast<IntegerDatum *>(i->OStack.pick(0).datum());
  if(id == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  DoubleVectorDatum *dvd= dynamic_cast<DoubleVectorDatum *>(i->OStack.pick(1).datum());
  if(dvd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t idx=id->get();
  if(idx >= (**dvd).size())
  {
      i->raiseerror("RangeCheck");
      return;
  }
  DoubleDatum *result=new DoubleDatum((**dvd)[idx]);
  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Get_iv_iFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntegerDatum *id= dynamic_cast<IntegerDatum *>(i->OStack.pick(0).datum());
  if(id == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntVectorDatum *vd= dynamic_cast<IntVectorDatum *>(i->OStack.pick(1).datum());
  if(vd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t idx=id->get();
  if(idx >= (**vd).size())
  {
      i->raiseerror("RangeCheck");
      return;
  }
  IntegerDatum *result=new IntegerDatum((**vd)[idx]);
  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Get_iv_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntVectorDatum *id= dynamic_cast<IntVectorDatum *>(i->OStack.pick(0).datum());
  if(id == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntVectorDatum *vd= dynamic_cast<IntVectorDatum *>(i->OStack.pick(1).datum());
  if(vd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t length= (**id).size();
  const size_t max_idx= (**vd).size();
  IntVectorDatum *result= new IntVectorDatum(new std::vector<long>(length));
  for(size_t j=0; j< length; ++j)
  {
      const size_t idx=(**id)[j];
      if(idx >= max_idx)
      {
          delete result;
          i->raiseerror("RangeCheck");
          return;
      }
      (**result)[j]=(**vd)[idx];
  }
  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Get_dv_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<2)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntVectorDatum *id= dynamic_cast<IntVectorDatum *>(i->OStack.pick(0).datum());
  if(id == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  DoubleVectorDatum *vd= dynamic_cast<DoubleVectorDatum *>(i->OStack.pick(1).datum());
  if(vd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t length= (**id).size();
  const size_t max_idx= (**vd).size();
  DoubleVectorDatum *result= new DoubleVectorDatum(new std::vector<double>(length));
  for(size_t j=0; j< length; ++j)
  {
      const size_t idx=(**id)[j];
      if(idx >= max_idx)
      {
          delete result;
          i->raiseerror("RangeCheck");
          return;
      }
      (**result)[j]=(**vd)[idx];
  }
  i->OStack.pop(2);
  i->OStack.push(result);
  i->EStack.pop();
}

// vector idx val put -> vector
void SLIArrayModule::Put_dv_i_dFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<3)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleDatum *val= dynamic_cast<DoubleDatum *>(i->OStack.pick(0).datum());
  if(val == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntegerDatum *idxd= dynamic_cast<IntegerDatum *>(i->OStack.pick(1).datum());
  if(idxd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  DoubleVectorDatum *vecd= dynamic_cast<DoubleVectorDatum *>(i->OStack.pick(2).datum());
  if(vecd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t idx= idxd->get();
  if(idx >= (**vecd).size())
  {
      i->raiseerror("RangeCheck");
      return;
  }
  (**vecd)[idx]= val->get();

  i->OStack.pop(2);
  i->EStack.pop();
}

void SLIArrayModule::Put_iv_i_iFunction::execute(SLIInterpreter *i) const
{

  IntegerDatum *val= dynamic_cast<IntegerDatum *>(i->OStack.pick(0).datum());
  if(val == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntegerDatum *idxd= dynamic_cast<IntegerDatum *>(i->OStack.pick(1).datum());
  if(idxd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  IntVectorDatum *vecd= dynamic_cast<IntVectorDatum *>(i->OStack.pick(2).datum());
  if(vecd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }

  const size_t idx= idxd->get();
  if(idx >= (**vecd).size())
  {
      i->raiseerror("RangeCheck");
      return;
  }
  (**vecd)[idx]= val->get();

  i->OStack.pop(2);
  i->EStack.pop();

}

void SLIArrayModule::DoubleVector2ArrayFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  DoubleVectorDatum *ivd= dynamic_cast<DoubleVectorDatum *>(i->OStack.top().datum());
  if(ivd == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  ArrayDatum ad(**ivd);
  i->OStack.pop();
  i->OStack.push(ad);
  i->EStack.pop();

}

void SLIArrayModule::Zeros_dvFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntegerDatum *num= dynamic_cast<IntegerDatum *>(i->OStack.top().datum());
  if(num == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(num->get() <0)
  {
      i->raiseerror("RangeCheck");
      return;
  }
  DoubleVectorDatum *result=new DoubleVectorDatum( new std::vector<double>(num->get(), 0.0));
  i->OStack.pop();
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Ones_dvFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntegerDatum *num= dynamic_cast<IntegerDatum *>(i->OStack.top().datum());
  if(num == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(num->get() <0)
  {
      i->raiseerror("RangeCheck");
      return;
  }
  DoubleVectorDatum *result=new DoubleVectorDatum( new std::vector<double>(num->get(), 1.0));
  i->OStack.pop();
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Zeros_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntegerDatum *num= dynamic_cast<IntegerDatum *>(i->OStack.top().datum());
  if(num == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(num->get() <0)
  {
      i->raiseerror("RangeCheck");
      return;
  }
  IntVectorDatum *result=new IntVectorDatum( new std::vector<long>(num->get(), 0L));
  i->OStack.pop();
  i->OStack.push(result);
  i->EStack.pop();
}

void SLIArrayModule::Ones_ivFunction::execute(SLIInterpreter *i) const
{
  if(i->OStack.load()<1)
  {
    i->raiseerror(i->StackUnderflowError);
    return;
  }
  IntegerDatum *num= dynamic_cast<IntegerDatum *>(i->OStack.top().datum());
  if(num == 0)
  {
    i->raiseerror(i->ArgumentTypeError);
    return;
  }
  if(num->get() <0)
  {
      i->raiseerror("RangeCheck");
      return;
  }
  IntVectorDatum *result=new IntVectorDatum( new std::vector<long>(num->get(), 1L));
   i->OStack.pop();
   i->OStack.push(result);
   i->EStack.pop();
}

void SLIArrayModule::FiniteQ_dFunction::execute(SLIInterpreter *i) const
{
  i->assert_stack_load(1);
  const double x = getValue<double>(i->OStack.pick(0));

  BoolDatum res(  -std::numeric_limits<double>::max() <= x
                && x <= std::numeric_limits<double>::max() );
  i->OStack.push(res);
  i->EStack.pop();
}



void SLIArrayModule::Forall_ivFunction::execute(SLIInterpreter *i) const
{
    static Token mark(i->baselookup(i->mark_name));
    static Token forall(i->baselookup(sli::iforall_iv));

    ProcedureDatum *proc=
        static_cast<ProcedureDatum *>(i->OStack.top().datum());
    
    i->EStack.pop();
    i->EStack.push_by_ref(mark);
    i->EStack.push_move(i->OStack.pick(1));        // push object
    i->EStack.push_by_pointer(new IntegerDatum(0));          // push array counter
    i->EStack.push_by_ref(i->OStack.pick(0));       // push procedure
    i->EStack.push_by_pointer(new IntegerDatum(proc->size()));          // push procedure counter
    i->EStack.push_by_ref(forall);
    i->OStack.pop(2);
    i->inc_call_depth();
}
/*********************************************************/
/* %forall_iv                                          */
/*  call: mark object count proc n %forall_iv      */
/*  pick    5     4    3     2    1    0               */        
/*********************************************************/
void SLIArrayModule::Iforall_ivFunction::execute(SLIInterpreter *i) const
{
    
    IntegerDatum *proccount=
        static_cast<IntegerDatum *>(i->EStack.pick(1).datum());
    
    ProcedureDatum
        const *proc= static_cast<ProcedureDatum *>(i->EStack.pick(2).datum());
    
    long &pos=proccount->get();

    while( proc->index_is_valid(pos))
    {
	const Token &t= proc->get(pos);
	++pos;
	if( t->is_executable())
	{
	    i->EStack.push(t);
	    return;
	}
	i->OStack.push(t);
    }
 
    IntegerDatum *count=
	static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
    IntVectorDatum *ad  =
	static_cast<IntVectorDatum *>(i->EStack.pick(4).datum());
        
    size_t idx=count->get();
    
    if(idx < (**ad).size())
    {
	pos=0; // reset procedure interator
        
	i->OStack.push(new IntegerDatum((**ad)[idx])); // push counter to user
	++(count->get());
    }
    else
    {
	i->EStack.pop(6);
	i->dec_call_depth();
    }
}


void SLIArrayModule::Iforall_ivFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+3).datum());
  assert(count!=NULL);

  std::cerr << "During forall (IntVector) at iteration " << count->get() << "." << std::endl;

}

void SLIArrayModule::Forall_dvFunction::execute(SLIInterpreter *i) const
{
    static Token mark(i->baselookup(i->mark_name));
    static Token forall(i->baselookup(sli::iforall_dv));

    ProcedureDatum *proc=
        static_cast<ProcedureDatum *>(i->OStack.top().datum());
    
    i->EStack.pop();
    i->EStack.push_by_ref(mark);
    i->EStack.push_move(i->OStack.pick(1));        // push object
    i->EStack.push_by_pointer(new IntegerDatum(0));          // push array counter
    i->EStack.push_by_ref(i->OStack.pick(0));       // push procedure
    i->EStack.push_by_pointer(new IntegerDatum(proc->size()));          // push procedure counter
    i->EStack.push_by_ref(forall);
    i->OStack.pop(2);
    i->inc_call_depth();
}

/*********************************************************/
/* %forall_iv                                          */
/*  call: mark object count proc n %forall_iv      */
/*  pick    5     4    3     2    1    0               */        
/*********************************************************/
void SLIArrayModule::Iforall_dvFunction::execute(SLIInterpreter *i) const
{
    
    IntegerDatum *proccount=
        static_cast<IntegerDatum *>(i->EStack.pick(1).datum());
    
    ProcedureDatum
        const *proc= static_cast<ProcedureDatum *>(i->EStack.pick(2).datum());
    
    long &pos=proccount->get();

    while( proc->index_is_valid(pos))
    {
	const Token &t= proc->get(pos);
	++pos;
	if( t->is_executable())
	{
	    i->EStack.push(t);
	    return;
	}
	i->OStack.push(t);
    }
 
    IntegerDatum *count=
	static_cast<IntegerDatum *>(i->EStack.pick(3).datum());
    DoubleVectorDatum *ad  =
	static_cast<DoubleVectorDatum *>(i->EStack.pick(4).datum());
        
    size_t idx=count->get();
    
    if(idx < (**ad).size())
    {
	pos=0; // reset procedure interator
        
	i->OStack.push(new DoubleDatum((**ad)[idx])); // push counter to user
	++(*count);
    }
    else
    {
	i->EStack.pop(6);
	i->dec_call_depth();
    }
}


void SLIArrayModule::Iforall_dvFunction::backtrace(SLIInterpreter *i, int p) const
{
  IntegerDatum   *count= static_cast<IntegerDatum *>(i->EStack.pick(p+3).datum());
  assert(count!=NULL);

  std::cerr << "During forall (DoubleVector) at iteration " << count->get() << "." << std::endl;

}

void SLIArrayModule::init(SLIInterpreter *i)
{
  i->createcommand("MapIndexed_a", &mapindexedfunction);
  i->createcommand("Map", &mapfunction);
  i->createcommand("MapThread_a", &mapthreadfunction);
  i->createcommand("Reverse", &reversefunction);
  i->createcommand("Rotate", &rotatefunction);
  i->createcommand("Flatten", &flattenfunction);
  i->createcommand("Sort", &sortfunction);
  i->createcommand("Transpose", &transposefunction);
  i->createcommand("Partition_a_i_i", &partitionfunction);
  i->createcommand(sli::imap, &imapfunction);
  i->createcommand(sli::imap_dv, &imap_dvfunction);
  i->createcommand(sli::imap_iv, &imap_ivfunction);
  i->createcommand(sli::imapindexed, &imapindexedfunction);
  i->createcommand("forall_iv", &forall_ivfunction);
  i->createcommand("forall_dv", &forall_dvfunction);
  i->createcommand(sli::iforall_iv, &iforall_ivfunction);
  i->createcommand(sli::iforall_dv, &iforall_dvfunction);
  i->createcommand("::MapThread", &imapthreadfunction);
  i->createcommand("Range", &rangefunction);
  i->createcommand("arrayload", &arrayloadfunction);
  i->createcommand("arraystore", &arraystorefunction);
  i->createcommand("arraycreate", &arraycreatefunction);

#ifdef PS_ARRAYS
  i->createcommand("]", &arraycreatefunction);
#endif

  i->createcommand("valid_a", &validfunction);
  i->createcommand("area", &areafunction);
  i->createcommand("area2", &area2function);
  i->createcommand("cv1d", &cv1dfunction);
  i->createcommand("cv2d", &cv2dfunction);
  i->createcommand("GetMax", &getmaxfunction);
  i->createcommand("GetMin", &getminfunction);
  i->createcommand("gabor_", &gaborfunction);
  i->createcommand("gauss2d_", &gauss2dfunction);
  i->createcommand("put_a_a_t", &put_a_a_tfunction);
  i->createcommand("array2intvector", &array2intvectorfunction);
  i->createcommand("array2doublevector", &array2doublevectorfunction);
  i->createcommand("doublevector2array", &doublevector2arrayfunction);
  i->createcommand("intvector2array", &intvector2arrayfunction);
 i->createcommand("add_iv_iv", &add_iv_ivfunction);
 i->createcommand("add_i_iv", &add_i_ivfunction);
 i->createcommand("sub_iv_iv", &sub_iv_ivfunction);
 i->createcommand("neg_iv", &neg_ivfunction);
 i->createcommand("mul_iv_iv", &mul_iv_ivfunction);
 i->createcommand("mul_i_iv", &mul_i_ivfunction);
 i->createcommand("mul_d_iv", &mul_d_ivfunction);
 i->createcommand("div_iv_iv", &div_iv_ivfunction);
 i->createcommand("length_iv", &length_ivfunction);

 i->createcommand("add_dv_dv", &add_dv_dvfunction);
 i->createcommand("add_d_dv", &add_d_dvfunction);

 i->createcommand("sub_dv_dv", &sub_dv_dvfunction);
 i->createcommand("neg_dv", &neg_dvfunction);

 i->createcommand("mul_dv_dv", &mul_dv_dvfunction);
 i->createcommand("mul_d_dv", &mul_d_dvfunction);

 i->createcommand("div_dv_dv", &div_dv_dvfunction);
 i->createcommand("inv_dv",   &inv_dvfunction);
 i->createcommand("length_dv", &length_dvfunction);

 i->createcommand("get_iv_i", &get_iv_ifunction);
 i->createcommand("get_iv_iv", &get_iv_ivfunction);
 i->createcommand("get_dv_i", &get_dv_ifunction);
 i->createcommand("get_dv_iv", &get_dv_ivfunction);
 i->createcommand("put_dv_i_d", &put_dv_i_dfunction);
 i->createcommand("put_iv_i_i", &put_iv_i_ifunction);
 i->createcommand("zeros_dv", &zeros_dvfunction);
 i->createcommand("ones_dv", &ones_dvfunction);
 i->createcommand("zeros_iv", &zeros_ivfunction);
 i->createcommand("ones_iv", &ones_ivfunction);
 i->createcommand("arange", &arangefunction);

  i->createcommand("finite_q_d", &finiteq_dfunction);
}