/*
 * Copyright (c) 2002-2006 Samit Basu
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifndef __Array_hpp__
#define __Array_hpp__
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include "Dimensions.hpp"
#include "Types.hpp"
#include "Data.hpp"
#include "RefVec.hpp"
#include <QVector>
#include "List.hpp"
#include <QStringList>
#include <QSharedDataPointer>

class Array;
class Interpreter;

typedef QList<Array> ArrayVector;
ArrayVector singleArrayVector(Array);
typedef QVector<ArrayVector> ArrayMatrix;

class FuncPtr;

static Dimensions zeroDim(0,0);

/** Ordered data array, the base FreeMat data type.
 * The Array class is the base class of all data types.  It represents
 * an ordered collection of data, indexed using an arbitrary number of
 * dimensions.  The Array class uses a seperate data class to store the
 * data.  It can contain an N-dimensional array of any of the following
 * data types:
 *   - FM_CELL_ARRAY - a heterogenous array - essentially an array of Arrays
 *   - FM_STRUCT_ARRAY - a structure array
 *   - FM_UINT8 - unsigned, 8-bit integers
 *   - FM_INT8  - signed, 8-bit integers
 *   - FM_UINT16 - unsigned, 16-bit integers
 *   - FM_INT16 - signed, 16-bit integers
 *   - FM_UINT32 - unsigned, 32-bit integers
 *   - FM_INT32 - signed, 32-bit integers
 *   - FM_UINT64 - unsigned, 64-bit integers
 *   - FM_INT64 - signed, 64-bit integers
 *   - FM_FLOAT - 32-bit floating point
 *   - FM_DOUBLE - 64-bit floating point
 *   - FM_COMPLEX - 32-bit complex floating point
 *   - FM_DCOMPLEX - 64-bit complex floating point
 *   - FM_STRING - a string class
 * 
 * The Dimensions class is used to record the dimensions of the given Array.
 * The Dimension class represents an n-tuple of integers that record the
 * number of elements along the array in each dimension.  The data is stored
 * in a generalization of "column-major" order.  Note that the array
 * class does \em not actually include the data - the data is stored in the
 * Data class, to which Array contains a pointer.  This design allows for
 * rapid copying of Array objects.
 */
class Array {
private:
  /**
   * This is a pointer to our data object - which is shared between
   * different Array objects.  It is essentially the memory block
   * directly associated with this data object.
   */
  QSharedDataPointer<Data> dp;
  /**
   * Add another fieldname to our structure array.
   */
  int insertFieldName(std::string fieldName);
  /** Get a binary map from an array over the given range.
   * This member function converts an array into a boolean vector, 
   * mathematically $$b(a(i)-1) = \mathrm{true}, 0 \leq i < \mathrm{maxD}$$, 
   * where $a(i)$ is the contents of the array (after calling 
   * Array::toOrdinal).  This is essentially a shortcut to converting
   * the array to a logical array and then converting to a boolean
   * array.  Throws an exception if $$a(i)$$ is outside the range
   * $$1,\ldots,\mathrm{maxD}$.
   */
  bool* getBinaryMap(uint32);
  /** Get the internal index corresponding to a given field name.
   * Get the internal index corresponding to a given field name.  This
   * is the index into the fieldname array of the argument.  If the
   * argument is not found, a value of -1 is returned.
   */
  int32 getFieldIndex(std::string fieldName);
public:
  /** Compute the maximum index.
   * This computes the maximum value of the array as an index (meaning
   * that it must be greater than 0.  Because this is an internal function, it
   * assumes that the variable has already been passed through toOrdinalType()
   * successfully.  Throws an exception if the maximum value is zero or
   * negative.
   */
  uint32 getMaxAsIndex();
  /**
   * Allocate an array.
   */
  static void* allocateArray(Class, uint32 length,rvstring names = rvstring());
  /** Convert us to an index type
   * Convert the current object to an ordinal one.  This has different
   * meanings for different data types.  
   *  - For string types, this conversion is not defined. 
   *  - For logical types, this is accomplished using a linear search (done in 
   *      two passes - one to identify the length of
   *      the final array, and another to identify the indices of non-zero values.
   *  - For double types, this is done by typecasting (truncation).  A warning
   *      is emitted if the source value is fractional (non-integer) or invalid
   *      (zero or negative).  
   *  - For complex types, this is done by typecasting.  The complex part 
   *      is ignored, and a warning that this is the case is emitted also.
   * Throws an exception for string, cell, structure types, or if a zero or 
   * negative index is encountered.
   */
  void toOrdinalType(Interpreter *m_eval);
  /**
   * Default constructor.
   */
  inline Array() {
    dp = NULL;
  }
  /**
   * Create an empty Array of the specified type.
   */
  inline Array(Class type) {
    dp = new Data(type,Dimensions(),NULL,false,rvstring(),rvstring());
  }
  /**
   * Create an Array with the specified contents.
   */
  inline Array(Class type, const Dimensions& dims, void* data, bool sparse = false, 
	       rvstring fieldNames = rvstring(), rvstring classname = rvstring()) {
    dp = new Data(type, dims, data, sparse, fieldNames, classname);
  }
  /**
   * Create an Array with a default allocation of space - only useful for P.O.D. arrays
   */
  inline Array(Class type, const Dimensions& dims) {
    dp = new Data(type, dims, 
		  allocateArray(type,dims.getElementCount()), 
		  false, rvstring(), rvstring());
  }
  /**
   * Get the length of the array as a vector.  This is equivalent
   * to computing length(this(:)).
   */
  inline int getLength() const {
    if (dp)
      return dp->dimensions().getElementCount();
    else
      return 0;
  }
  /**
   * Return true if this is a user-defined class
   */    
  inline bool isUserClass() const {
    if (dp)
      return dp->isUserClass();
    else
      return false;
  }
  /**
   * Return name of user-defined class
   */
  inline rvstring className() const {
    if (dp)
      return dp->className();
    else
      return rvstring();
  }
  /**
   * Set classname tag - implies this is a structure array.
   */
  inline void setClassName(rvstring cname) {
    if (dataClass() != FM_STRUCT_ARRAY)
      throw Exception("cannot set class name for non-struct array");
    dp->setClassName(cname);
  }
  /**
   * Get a copy of our dimensions vector.
   */
  inline const Dimensions& dimensions() const {
    if (dp)
      return dp->dimensions();
    else
      return zeroDim;
  }
  /**
   * Get the fieldnames.
   */
  inline rvstring fieldNames() const {
    if (dp)
      return dp->fieldNames();
    else
      return rvstring();
  }
  /**
   * Get our length along the given dimension.
   */
  inline int getDimensionLength(int t) const {
    if (dp)
      return dp->dimensions().get(t);
    else
      return 0;
  }
  /** Get the contents of our data block as a (read-only) void* pointer.
   * Get the contents of our data block as a void* pointer.  The
   * resulting pointer is read only, so that no modifications can
   * be made to the contents of our array.  To modify
   * the contents, you must make a copy and use setDataPointer to replace the
   * current data.  This "copy-on-write" technique avoids copies on
   * references to variables -- a good thing in this interpreted
   * environment where read-references dominate the accesses to variables.
   * Another option is to use getReadWriteDataPointer, which returns a 
   * pointer that is free of object aliases.
   */
  inline const void* getDataPointer() const {
    if (sparse())
      throw Exception("operation does not support sparse matrix arguments.");
    if (dp)
      return dp->getData();
    else
      return NULL;
  }

  inline const void* getSparseDataPointer() const {
    if (dp)
      return dp->getData();
    else
      return NULL;
  }

  inline const void* data() const {
    if (dp)
      return dp->getData();
    else
      return NULL;
  }

  inline void setData(Class aClass, const Dimensions& dims, void *s, 
		      bool sparseflag = false, 
		      rvstring fields = rvstring(),
		      rvstring classname = rvstring()) {
    if (dp)
      dp->putData(aClass,dims,s,sparseflag,fields,classname);
    else 
      dp = new Data(aClass,dims,s,sparseflag,fields,classname);
  }


  /** Get the contents of our data block as a read-write void* pointer.
   * Get the contents of our data block as a read-write void*
   * pointer.  It ensures that our data block is not aliased (meaning
   * that no other array objects share the data block), prior
   * to returning the pointer. To do this,
   * we have to go through the following steps:
   *   - Check the number of owners of our byte array.
   *   - If there is only one owner for the byte array, return 
   *      a non-const pointer to the data
   *   - If there is more than one owner, copy our data.
   */
  inline void* getReadWriteDataPointer() {
    if (sparse()) {
      makeDense();
    }
    return dp->getWriteableData();
  }
  /** Set the contents of our data block to the supplied pointer.
   * Set the contents of our data block to the supplied pointer.
   * Ownership of the data block is passed to the array, i.e., the
   * caller should not manipulate the block in any way after 
   * calling this function. To avoid recopying, ownership of 
   * the byte-array is passed
   * to the reference counting data object Data at this point.
   * That means that the caller is _not_ responsible for freeing
   * the memory block.
   */
  inline void setDataPointer(void* rp) {
    dp->setData(rp);
  }
  /** Resize an array.
   * Resize the array to a new set of dimensions.  This resize operation
   * puts the contents of the array at the (0,...,0) corner of the new
   * array.  
   */
  void resize(Dimensions& a);
  /** Resize an array based on a vector indexing expression.
   * This method resizes an object so that an index of the type this(n)
   * is valid.  In particular, if "this" is a scalar, then this(n) extends
   * the array in the column dimension.  If "this" is a column vector, then
   * this(n) extends the array in the row dimension.  If "this" is a row
   * vector, then this(n) extends the array in the column direction.
   * For an arbitrarily dimensioned array, this(n) makes the array into 
   * a row vector of length n.
   */
  void vectorResize(int);
  /** Reshape an array.
   * Reshape the array along a new set of dimensions.  Valid provided that
   * setting the dimensions of the array to a does not change the number of
   * elements in the array.
   * Throws an exception if the new dimension has a different number of elements
   * than we currently have.
   */
  void reshape(Dimensions& a);
  /**
   * Take the hermitian transpose of the array.  For non-complex data types,
   * this is equivalent to a transpose operation.
   * Throws an exception if we are not a 2D array.
   */
  void hermitian();
  /**
   * Transposes the array (provided the array is 2-Dimensional).
   * Throws an exception if we are not a 2D array.
   */
  void transpose();
  /**
   * Get our data class (of type Class).
   */
  Class dataClass() const {
    if (dp)
      return dp->dataClass();
    else
      return FM_DOUBLE;
  }
  /**
   * Calculate the size of each element in this array.
   */
  int getElementSize() const;
  /**
   * Calculate the bytes required to hold this array (element size * length)
   */
  inline int getByteSize() const {
    if (sparse())
      throw Exception("Byte size calculation not supported for sparse arrays.");
    return getElementSize()*getLength();
  }
  /**
   * Returns true if we are (meaningfully) positive.  For the unsigned integer types,
   * this is always true.  For complex types, this is false.  For the signed integer
   * types or the floating point types, the result is based on a linear scan through
   * the array.
   */
  const bool isPositive() const;
  /**
   * Returns true if we are symmetric, 2D and square.
   */
  const bool isSymmetric() const;
  /**
   * Returns true if our real part is all zeros.  For integer types, this is an
   * element-wise test.  For complex types, we check only the real part.
   * Throws an exception if we are a string, cell-array or struct-array type.
   */
  const bool isRealAllZeros() const;
  inline const bool sparse() const {
    return (dp && dp->sparse());
  }
  void makeSparse();
  void makeDense();
  int getNonzeros() const;
  /**
   * Returns true if we match the scalar value in x.  For strings, this is done by
   * doing a string compare.  For numerical values, we promote to a common type
   * and do a comparison.
   */
  const bool testCaseMatchScalar(Array x) const;
  /**
   * Returns true if we match the argument x, or if x is a cell-array,
   * returns true if we match any of the cells in x.  Uses Array::testCaseMatchScalar
   * to do the actual testing.
   * Throws an exception for non-scalars (apart from strings) or reference types.
   * Also throws an exception if the argument is not either a scalar or a cell
   * array.
   */
  const bool testForCaseMatch(Array x) const;
  /**
   * Returns TRUE if we are empty (we have no elements).
   */
  inline const bool isEmpty() const {
    return ((dp == NULL) || (getLength() == 0) || 
	    (!dp->getData()));
  }
  /**
   * Returns TRUE if we have only a single element.
   */
  inline const bool isScalar() const {
    if (isEmpty()) return false;
    return dp->dimensions().isScalar();
  }
  /**
   * Returns TRUE if we are 2-Dimensional.
   */
  inline const bool is2D() const {
    return dp->dimensions().is2D();
  }
  /**
   * Returns TRUE if we are a vector.
   */
  inline const bool isVector() const {
    return dp->dimensions().isVector();
  }
  /**
   * Returns TRUE if we are a reference type (cell array or
   * struct array).
   */
  inline const bool isReferenceType() const {
    if (isEmpty())
      return false;
    return ((dataClass() == FM_CELL_ARRAY) ||
	    (dataClass() == FM_STRUCT_ARRAY) ||
	    (dataClass() == FM_FUNCPTR_ARRAY));
  }
  /**
   * Returns TRUE if we are a complex data type.
   */
  inline const bool isComplex() const {
    return (dp->dataClass() == FM_DCOMPLEX || dp->dataClass() == FM_COMPLEX);
  }
  /**
   * Returns TRUE if we are a real data type.
   */
  inline const bool isReal() const {
    return (!isComplex());
  }
  /**
   * Returns TRUE if we are a string.
   */
  inline const bool isString() const {
    return (dp && (dp->dataClass() == FM_STRING));
  }
  /**
   * Returns TRUE if we are an integer class.
   */
  inline const bool isIntegerClass() const {
    return ((dp->dataClass() < FM_FLOAT) && (dp->dataClass() >= FM_LOGICAL));
  }
  inline bool isColumnVector() const {
    return (is2D() && columns() == 1);
  }
  inline bool isRowVector() const {
    return (is2D() && rows() == 1);
  }
  /**
   * Copy data from our data array to the specified array.  This is a 
   * deep copy, in the sense that pointers are copied by creating 
   * new objects.  Copy count elements, starting at index srcIndex 
   * to the destination address starting at index dstIndex.   The addresses
   * are in terms of indices, not bytes.
   */
  void copyElements(int srcIndex, void* dstPtr, int dstIndex, int count) const;
  /**
   * Promote our array to a new type.  For empty arrays, this type
   * promotion always succeeds.  For cell arrays, this does nothing (except
   * throw an error if we attempt to promote it to a different type).  For
   * structure arrays, promoting to a structure array has three possible outcomes:
   *   - If the fields match in order and contents then the promotion is successful.
   *   - If the fields match in contents but not in-order then the promotion involves
   *     reordering the data.
   *   - If the fields match in contents but the destination type has 
   *     additional fields, then the promotion involves reordering the data and 
   *     adding space.
   * Throws an exception if 
   *   - we try to convert a cell-array to another type.
   *   - we try to promote a structure-array to another array with 
   *     an incompatible field setup (i.e., not one of the three outcomes
   *     listed above).  
   *   - we try to convert a structure-array to a non-structure array type.
   *   - we try to convert any numerical types to a reference type.
   */
  void promoteType(Class new_type, rvstring fieldNames);
  /**
   * Promote our array to a new type.  This is a shortcut for when new_type is not
   * FM_STRUCT_ARRAY, so that the fieldNames argument is not needed.
   */
  void promoteType(Class new_type);
  /**
   * Permute our array according to the given permutation vector.  Note that
   * we assume that the permutation vector is of the correct size and is a
   * valid permutation.
   */
  void permute(const int32* permutation);
  /**
   * Diagonal constructor - construct an array from a given vector, with
   * the contents of the vector stored into the specified diagonal of the
   * matrix.
   * Throwsn an exception if the argument is not a vector.
   */
  static Array diagonalConstructor(Array src, int diagonalOrder);

  /**
   * Empty constructor
   */
  static Array emptyConstructor();
  static Array funcPtrConstructor(FuncPtr fptr);
  /**
   * Scalar constructor - Construct an FM_LOGICAL object with a scalar
   * value.
   */
  static Array logicalConstructor(bool aval);
  /**
   * Scalar constructor - Construct an FM_UINT8 object with a scalar
   * value.
   */
  static Array uint8Constructor(uint8 aval);
  /**
   * Scalar constructor - Construct an FM_INT8 object with a scalar
   * value.
   */
  static Array int8Constructor(int8 aval);
  /**
   * Scalar constructor - Construct an FM_UINT16 object with a scalar
   * value.
   */
  static Array uint16Constructor(uint16 aval);
  /**
   * Scalar constructor - Construct an FM_INT16 object with a scalar
   * value.
   */
  static Array int16Constructor(int16 aval);
  /**
   * Scalar constructor - Construct an FM_UINT32 object with a scalar
   * value.
   */
  static Array uint32Constructor(uint32 aval);
  /**
   * Scalar constructor - Construct an FM_INT32 object with a scalar
   * value.
   */
  static Array int32Constructor(int32 aval);
  /**
   * Scalar constructor - Construct an FM_UINT64 object with a scalar
   * value.
   */
  static Array uint64Constructor(uint64 aval);
  /**
   * Scalar constructor - Construct an FM_INT64 object with a scalar
   * value.
   */
  static Array int64Constructor(int64 aval);
  /**
   * Scalar constructor - Construct an FM_FLOAT object with a scalar
   * value.
   */
  static Array floatConstructor(float aval);
  /**
   * Scalar constructor - Construct an FM_DOUBLE object with a scalar
   * value.
   */
  static Array doubleConstructor(double aval);
  /**
   * Double matrix constructor - Construct an FM_DOUBLE object with the
   * specified dimenstions.
   */
  static Array doubleMatrixConstructor(int rows, int cols);
  /**
   * Double complex matrix constructor - Construct an FM_DCOMPLEX object 
   * with the specified dimenstions.
   */  
  static Array dcomplexMatrixConstructor(int rows, int cols);
  /**
   * Complex constructor - Construct an FM_COMPLEX object with a 
   * complex scalar value.
   */
  static Array complexConstructor(float aval, float bval);
  /**
   * Complex constructor - Construct an FM_DCOMPLEX object with a 
   * complex scalar value.
   */
  static Array dcomplexConstructor(double aval, double bval);
  /**
   * String constructor - Construct an FM_STRING object with the given
   * string as a value.
   */
  static Array stringConstructor(std::string aval);
  /**
   * Double vector constructor - Construct an FM_DOUBLE object
   * that is a (row) vector with the given length.
   */
  static Array doubleVectorConstructor(int len);
  /**
   * Float vector constructor - Construct an FM_FLOAT object
   * that is a (row) vector with the given length.
   */
  static Array floatVectorConstructor(int len);
  /**
   * Unsigned int vector constructor - Construct an FM_INT32 object
   * that is a (row) vector with the given length.
   */
  static Array uint32VectorConstructor(int len);
  /**
   * Construct a FM_INT32 vector (either vertical or horizontal) corresponding to 
   * minval:stepsize:maxval, with an optional transpose.
   */
  static Array int32RangeConstructor(int32 minval, int32 stepsize, int32 maxval, bool vertical);
  /**
   * Construct a FM_INT64 vector (either vertical or horizontal) corresponding to 
   * minval:stepsize:maxval, with an optional transpose.
   */
  static Array int64RangeConstructor(int64 minval, int64 stepsize, int64 maxval, bool vertical);
  /**
   * Construct a FM_FLOAT vector (either vertical or horizontal) corresponding to 
   * minval:stepsize:maxval, with an optional transpose.
   */
  static Array floatRangeConstructor(float minval, float stepsize, float maxval, bool vertical);
  /**
   * Construct a FM_DOUBLE vector (either vertical or horizontal) corresponding to 
   * minval:stepsize:maxval, with an optional transpose.
   */
  static Array doubleRangeConstructor(double minval, double stepsize, double maxval, bool vertical);

  /**
   * There are two things to check when constructing a data object
   * from a matrix of data objects.  The first is the sizes of the
   * objects being composited.  The second is the type of the components.
   * We assume that the Character type is "dominant" in the sense that 
   * if any of the subobjects is a character type, the final type is
   * a character.  Hence, we first construct the size of the final
   * object, and then check its type. This type rule is handled using 
   * the natural ordering of the enumerated types.  The dimensions
   * rule for elements that make up a matrix are straightforward:
   *    - All elements in one row must have the same number of rows,
   *      can have an arbitrary number of columns, and must match in 
   *      all remaining dimensions (dims 3,4,etc.)
   *    - The total number of columns (dim 2) in each row must match
   * Throws an exception if 
   *    - the dimensions of the arguments do not match up properly 
   *      (i.e., do not satisfy the two rules listed above).
   *    - the types are incompatible (e.g., cell and struct arrays are
   *      being combined).
   */
  static Array matrixConstructor(ArrayMatrix& m);
  /**
   * The constructor for a cell array is significantly simpler than
   * the matrix constructor.  The argument is a list of rowdefs.  Each
   * element in the rowdef is a Array pointer that we assign to our
   * data pointer.  The only thing we need to make sure of is that 
   * each row has the same number of elements in it.
   * Throws an exception if the geometry of the argumens is incompatible.
   */
  static Array cellConstructor(ArrayMatrix& m);
  static Array cellConstructor(ArrayVector& m);
  static Array cellConstructor(Array m);
  /**
   * Structure constructor - this is equivalent to the built in struct command.
   * First, we have to make sure that each entry of "values" have 
   *  -  cell arrays of the same size (say MxN)
   *  -  single element cell arrays,
   *  -  single values.
   * With such a setup, the output is a structure array of size MxN.  Elements
   * which are defined by a single value or a single-element cell array are
   * replicated throughout all MxN entries.  Remaining elements take their
   * values from the cell-array.
   * Throws an exception if
   *  - the number of entries in the fieldnames vector does not match
   *    the number of entries in the values vector
   *  - the non-scalar values do not agree in dimension
   */
  static Array structConstructor(rvstring fNames, ArrayVector& values);
  /**
   * Get a subset of an Array.  This is for vector-indexing, meaning that
   * the argument is assumed to refer to the elements in their order as a vector.
   * So, x(10) is equivalent to x(:)(10), even if, say, x is 3 x 4.
   * Throws an exception if
   *  - the variable is empty
   *  - the argument subset exceeds our valid domain
   */
  Array getVectorSubset(Array& index, Interpreter* m_eval);
  /**
   * Get a subset of an Array.  This if for n-Dimensional-indexing, meaning
   * that x(10) is really x(10,1).
   * Throws an exception if the variable is empty.
   */
  Array getNDimSubset(ArrayVector& index, Interpreter* m_eval);
  Array getNDimSubsetScalars(ArrayVector& index, Interpreter* m_eval);
  /**
   * Get the diagonal elements of an array.  Only applicable to 2-dimensional arrays.
   * The diagonal part of a rectangular matrix
   * is a vector of length K.  For an M x N matrix, the L order diagonal has a length
   * that can be calculated as:
   *    K = min(M,N-L) for L > 0 or
   *    K = min(M+L,N) for L < 0
   * Throws an exception for multi-dimensional arrays.
   */
  Array getDiagonal(int diagonalOrder);
  /**
   * Get the contents of a field as an array from its field name.  This is used 
   * when a structure array is used to supply a list of expressions.
   * Throws an exception if
   *   - we are not a structure array
   *   - the field does not exist
   */
  Array getField(std::string fieldName);
  ArrayVector getFieldAsList(std::string fieldName);
  /**
   * Get a subset of a (cell) Array using contents-addressing.  This is used when a 
   * cell array is used to supply a list of expressions.
   * Throws an exception if
   *   - we are not a cell-array
   *   - the indices exceed the array bounds
   */
  Array getVectorContents(Array& index, Interpreter* m_eval);
  ArrayVector getVectorContentsAsList(Array& index, Interpreter* m_eval);
  /**
   * Get a subset of an Array using contents-addressing.  This is used when a cell array
   * is used to supply a list of expressions.
   * Throws an exception if we are not a cell-array.
   */  
  Array getNDimContents(ArrayVector& index, Interpreter* m_eval);
  ArrayVector getNDimContentsAsList(ArrayVector& index, Interpreter* m_eval);
  void setValue(const Array &x);
  /**
   * Set a subset of an Array.  Uses vector-indexing, meaning that the
   * argument is assumed to refer to the elements in their order as a vector.
   * So, x(10) is equivalent to x(:)(10), even if, say, x is 3 x 4.
   * Throws an exception if there is a size mismatch between the index and the data.
   */
  void setVectorSubset(Array& index, Array& data, Interpreter* m_eval);
  /**
   * Set a subset of an Array.   This if for n-Dimensional-indexing, meaning
   * that x(10) is really x(10,1).
   * Throws an exception if there is a size mismatch between the index and the data.
   */
  void setNDimSubset(ArrayVector& index, Array& data, Interpreter* m_eval);
  void setNDimSubsetScalars(ArrayVector& index, Array& data, Interpreter* m_eval);
  /**
   * Set a subset of an Array using contents-indexing, meaning that the
   * argument is assumed to refer to the elements in their order as a vector.
   * So, x{10} is equivalent to x(:){10}, even if, say, x is 3 x 4. 
   * This is used when a cell-array is used as the return of a 
   * multi-function call, i.e.: [x{3:5}] = foo.
   * Throws an exception if the number of elements in data do not match
   * the number of indices in index.
   */
  void setVectorContentsAsList(Array& index, ArrayVector& data, Interpreter* m_eval);
  /**
   * Set a subset of an Array.   This if for n-Dimensional-indexing, meaning
   * that x{10} is really x{10,1}.  This is used when a cell-array is used
   * as the return of a multi-function call, i.e.: [x{1,3:5}] = foo.
   * Throws an exception if the number of elements in data do not match
   * the number of indices covered by index (which is the product of the
   * number of elements in each dimension of index).
   */
  void setNDimContentsAsList(ArrayVector& index, ArrayVector& data, Interpreter* m_eval);
  /**
   * Replace the contents of a field with the supplied array.  This is used
   * when a structure array is used to hold the return of a multi-function
   * call, i.e.: [x.foo] = foo
   * Throws an exception if
   *   - we are not a structure array
   *   - the number of elements in data is not equal to the number of elements in 
   *     our array.
   */
  void setFieldAsList(std::string fieldName, ArrayVector& data);
  /**
   * Delete a subset of this array using the argument for vector indexing.
   * This is _much_ simpler than the planar case.  Here, we simply:
   *   -  Create a deletion map from the index variable.
   *   -  Adjust the size of the output, and reshape to
   *       a vector.
   *   -  Copy (and skip) as necessary.
   * The result is then resized using the same rules as in vectorResize.
   */ 
  void deleteVectorSubset(Array& ndx, Interpreter* m_eval);
  /**
   * Delete a subset of this array using the arguments for n-Dimensional
   * indexing.  This method is the "planar" delete, meaning that its 
   * designed to delete all the entries in an N-ary array in one dimension.
   * It cannot be used to create "holes" in an array.
   * Throws an exception if the argument contains more than one non-colon index
   */
  void deleteNDimSubset(ArrayVector& args, Interpreter* m_eval);
  /**
   * Return the number of rows.
   */
  inline int rows() const {
    return getDimensionLength(0);
  }
  /**
   * Return the number of columns.
   */
  inline int columns() const {
    return getDimensionLength(1);
  }
  /**
   * Get our contents as a C-string.  Only works for _STRING types.
   * Throws an exception for non-string types.
   */
  string getContentsAsString() const;
  string getContentsAsStringUpper() const;
  /**
   * Get our contents as an integer scalar.
   * Throws an exception if we are not a scalar integer type.
   */
  int32 getContentsAsIntegerScalar();
  /**
   * Get our contents as a double scalar.
   * Throws an exception if we are not scalar or cannot meaningfully
   * be converted to a double precision value.
   */
  double getContentsAsDoubleScalar();
  /**
   * Returns true if the given Class is either FM_CELL_ARRAY or
   * FM_STRUCT_ARRAY.
   */
  static bool isDataClassReferenceType(Class);
  /**
   * Returns true if any of the entries in the array are not finite
   * (always false for reference and integer types)
   */
  bool anyNotFinite();
  /**
   * Returns the number of nonzero elements in the array.  For reference
   * types, this is a best-guess.
   */
  int32 nnz() const;
};

bool isColonOperator(Array& a);
int32 ArrayToInt32(const Array& a);
double ArrayToDouble(const Array& a);
string ArrayToString(const Array& a);
Array ToSingleArray(const ArrayVector& a);
Array CellArrayFromQStringList(QStringList t);
Array Uint32VectorFromQList(QList<uint32> p);

void printObjectBalance();
void dumpAllArrays();

uint32 TypeSize(Class cls);

string operator+(string a, int d);
string operator+(int d, string a);
stringVector operator+(stringVector a, stringVector b);
#endif


syntax highlighted by Code2HTML, v. 0.9.1