/*
 * 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
 *
 */

#include "Array.hpp"
#include "Math.hpp"
#include "Exception.hpp"
#include <math.h>
#include <stdio.h>
#include "Core.hpp"
#include "Malloc.hpp"
#include <ctype.h>


//!
//@Module RESHAPE Reshape An Array
//@@Section ARRAY
//@@Usage
//Reshapes an array from one size to another. Two seperate 
//syntaxes are possible.  The first syntax specifies the array 
//dimensions as a sequence of scalar dimensions:
//@[
//   y = reshape(x,d1,d2,...,dn).
//@]
//The resulting array has the given dimensions, and is filled with
//the contents of @|x|.  The type of @|y| is the same as @|x|.  
//The second syntax specifies the array dimensions as a vector,
//where each element in the vector specifies a dimension length:
//@[
//   y = reshape(x,[d1,d2,...,dn]).
//@]
//This syntax is more convenient for calling @|reshape| using a 
//variable for the argument. The
//@|reshape| function requires that the length of @|x| equal the product
//of the @|di| values.
//Note that arrays are stored in column format, 
//which means that elements in @|x| are transferred to the new array
//@|y| starting with the first column first element, then proceeding to 
//the last element of the first column, then the first element of the
//second column, etc.
//@@Example
//Here are several examples of the use of @|reshape| applied to
//various arrays.  The first example reshapes a row vector into a 
//matrix.
//@<
//a = uint8(1:6)
//reshape(a,2,3)
//@>
//The second example reshapes a longer row vector into a volume with 
//two planes.
//@<
//a = uint8(1:12)
//reshape(a,[2,3,2])
//@>
//The third example reshapes a matrix into another matrix.
//@<
//a = [1,6,7;3,4,2]
//reshape(a,3,2)
//@>  
//!
ArrayVector ReshapeFunction(int nargout, const ArrayVector& arg) {
  Array t, s;
  Dimensions dims;
  int32 *dp;
  int i;
  if (arg.size() == 0)
    throw Exception("reshape function requires at least one argument");
  Array x(arg[0]);
  if (arg.size() == 1) {
    ArrayVector retval;
    retval.push_back(x);
    return retval;
  }
  // Case 1 - all of the entries are scalar
  bool allScalars;
  allScalars = true;
  for (i=1;i<arg.size();i++)
    allScalars &= arg[i].isScalar();
  if (allScalars) {
    t = arg[1];
    if (arg.size() == 2) {
      // If all scalars and only one argument - we want a square output matrix
      dims.set(0,t.getContentsAsIntegerScalar());
      dims.set(1,dims.get(0));
    } else {
      // If all scalars and and multiple arguments, we count dimensions
      for (i=1;i<arg.size();i++) {
	t = arg[i];
	dims.set(i-1,t.getContentsAsIntegerScalar());
      }

    }
  } else {
    if (arg.size() > 2)
      throw Exception("Arguments to reshape function must be either all scalars or a single vector");
    t = arg[1];
    t.promoteType(FM_UINT32);
    dp = (int*) t.getDataPointer();
    for (i=0;i<t.getLength();i++)
      dims.set(i,dp[i]);
  }
  bool allPositive;
  allPositive = true;
  for (i=0;i<dims.getLength();i++)
    allPositive &= (dims.get(i) >= 0);
  if (!allPositive)
    throw Exception("reshape function requires positive arguments");
  dims.simplify();
  x.reshape(dims);
  ArrayVector retval;
  retval.push_back(x);
  return retval;
}
  
//!
//@Module ZEROS Array of Zeros
//@@Section ARRAY
//@@Usage
//Creates an array of zeros of the specified size.  Two seperate 
//syntaxes are possible.  The first syntax specifies the array 
//dimensions as a sequence of scalar dimensions:
//@[
//   y = zeros(d1,d2,...,dn).
//@]
//The resulting array has the given dimensions, and is filled with
//all zeros.  The type of @|y| is @|double|, a 64-bit floating
//point array.  To get arrays of other types, use the typecast 
//functions (e.g., @|uint8|, @|int8|, etc.).  An alternative syntax
//is to use the following notation:
//@[
//   y = zeros(d1,d2,...,dn,classname)
//@]
//where @|classname| is one of 'double', 'single', 'int8', 'uint8',
//'int16', 'uint16', 'int32', 'uint32', 'int64', 'uint64', 'float', 'logical'.  
//    
//The second syntax specifies the array dimensions as a vector,
//where each element in the vector specifies a dimension length:
//@[
//   y = zeros([d1,d2,...,dn]),
//@]
//or
//@[
//   y = zeros([d1,d2,...,dn],classname).
//@]
//This syntax is more convenient for calling @|zeros| using a 
//variable for the argument.  In both cases, specifying only one
//dimension results in a square matrix output.
//@@Example
//The following examples demonstrate generation of some zero arrays 
//using the first form.
//@<
//zeros(2,3,2)
//zeros(1,3)
//@>
//The same expressions, using the second form.
//@<
//zeros([2,6])
//zeros([1,3])
//@>
//Finally, an example of using the type casting function @|uint16| to generate an array of 16-bit unsigned integers with zero values.
//@<
//uint16(zeros(3))
//@>
//Here we use the second syntax where the class of the output is specified 
//explicitly
//@<
//zeros(3,'int16')
//@>
//@@Tests
//@{ test_sparse69.m
//% Test the zeros function
//function x = test_sparse69
//xi = int32(sparse(100,200));
//yi = int32(zeros(100,200));
//xf = float(sparse(100,200));
//yf = float(zeros(100,200));
//xd = double(sparse(100,200));
//yd = double(zeros(100,200));
//xc = complex(sparse(100,200));
//yc = complex(zeros(100,200));
//xz = dcomplex(sparse(100,200));
//yz = dcomplex(zeros(100,200));
//x = testeq(xi,yi) & testeq(xf,yf) & testeq(xd,yd) & testeq(xc,yc) & testeq(xz,yz);
//@}
//!
ArrayVector ZerosFunction(int nargout, const ArrayVector& arg) {
  Array t, s;
  Dimensions dims;
  int32 *dp;
  Class cls = FM_DOUBLE;
  int i;
  // Trim out the classname if it was specified
  ArrayVector trim_arg(arg);
  if (trim_arg.size() > 0) {
    // Check for the classname
    if (trim_arg.back().isString()) {
      // Get the classname as a string
      string cp = ArrayToString(trim_arg.back());
      // Convert to lowercase
      string dp(cp);
      std::transform(dp.begin(),dp.end(),dp.begin(),(int(*)(int))tolower);
      if (cp=="double")
	cls = FM_DOUBLE;
      else if (cp=="single")
	cls = FM_FLOAT;
      else if (cp=="float")
	cls = FM_FLOAT;
      else if (cp=="int8")
	cls = FM_INT8;
      else if (cp=="uint8")
	cls = FM_UINT8;
      else if (cp=="int16")
	cls = FM_INT16;
      else if (cp=="uint16")
	cls = FM_UINT16;
      else if (cp=="int32")
	cls = FM_INT32;
      else if (cp=="uint32")
	cls = FM_UINT32;
      else if (cp=="int64")
	cls = FM_INT64;
      else if (cp=="uint64")
	cls = FM_UINT64;
      else if (cp=="logical")
	cls = FM_LOGICAL;
      else
	throw Exception(std::string("Unsupported type ") + cp + std::string(" as classname argument to zeros function"));
      // Remove the classspec
      trim_arg.pop_back();
    }
  }
  if (trim_arg.size() == 0)
    dims.makeScalar();
  else {
    // Case 1 - all of the entries are scalar
    bool allScalars;
    allScalars = true;
    for (i=0;i<trim_arg.size();i++)
      allScalars &= trim_arg[i].isScalar();
    if (allScalars) {
      t = trim_arg[0];
      if (trim_arg.size() == 1) {
	// If all scalars and only one argument - we want a square zero matrix
	dims.set(0,t.getContentsAsIntegerScalar());
	dims.set(1,dims.get(0));
      } else {
	// If all scalars and and multiple arguments, we count dimensions
	for (i=0;i<trim_arg.size();i++) {
	  t = trim_arg[i];
	  dims.set(i,t.getContentsAsIntegerScalar());
	}
	  
      }
    } else {
      if (trim_arg.size() > 1)
	throw Exception("Arguments to zeros function must be either all scalars or a single vector");
      t = trim_arg[0];
      t.promoteType(FM_UINT32);
      dp = (int*) t.getDataPointer();
      for (i=0;i<t.getLength();i++)
	dims.set(i,dp[i]);
    }
    bool allPositive;
    allPositive = true;
    for (i=0;i<dims.getLength();i++)
      allPositive &= (dims.get(i) >= 0);
    if (!allPositive)
      throw Exception("Zeros function requires positive arguments");
  }
  s = Array(cls,dims,Calloc(TypeSize(cls)*dims.getElementCount()));
  ArrayVector retval;
  retval.push_back(s);
  return retval;
}

//!
//@Module CELL Cell Array of Empty Matrices
//@@Section ARRAY
//@@Usage
//Creates a cell array of empty matrix entres.  Two seperate 
//syntaxes are possible.  The first syntax specifies the array 
//dimensions as a sequence of scalar dimensions:
//@[
//   y = cell(d1,d2,...,dn).
//@]
//The resulting array has the given dimensions, and is filled with
//all zeros.  The type of @|y| is @|cell|, a cell array.  
//    
//The second syntax specifies the array dimensions as a vector,
//where each element in the vector specifies a dimension length:
//@[
//   y = cell([d1,d2,...,dn]).
//@]
//This syntax is more convenient for calling @|zeros| using a 
//variable for the argument.  In both cases, specifying only one
//dimension results in a square matrix output.
//@@Example
//The following examples demonstrate generation of some zero arrays 
//using the first form.
//@<
//cell(2,3,2)
//cell(1,3)
//@>
//The same expressions, using the second form.
//@<
//cell([2,6])
//cell([1,3])
//@>
//!
ArrayVector CellFunction(int nargout, const ArrayVector& arg) {
  Array t, s;
  Dimensions dims;
  int32 *dp;
  int i;
  if (arg.size() == 0)
    dims.makeScalar();
  else {
    // Case 1 - all of the entries are scalar
    bool allScalars;
    allScalars = true;
    for (i=0;i<arg.size();i++)
      allScalars &= arg[i].isScalar();
    if (allScalars) {
      t = arg[0];
      if (arg.size() == 1) {
	// If all scalars and only one argument - we want a square zero matrix
	dims.set(0,t.getContentsAsIntegerScalar());
	dims.set(1,dims.get(0));
      } else {
	// If all scalars and and multiple arguments, we count dimensions
	for (i=0;i<arg.size();i++) {
	  t = arg[i];
	  dims.set(i,t.getContentsAsIntegerScalar());
	}
	  
      }
    } else {
      if (arg.size() > 1)
	throw Exception("Arguments to cell function must be either all scalars or a single vector");
      t = arg[0];
      t.promoteType(FM_UINT32);
      dp = (int*) t.getDataPointer();
      for (i=0;i<t.getLength();i++)
	dims.set(i,dp[i]);
    }
    bool allPositive;
    allPositive = true;
    for (i=0;i<dims.getLength();i++)
      allPositive &= (dims.get(i) >= 0);
    if (!allPositive)
      throw Exception("Zeros function requires positive arguments");
  }
  s = Array(FM_CELL_ARRAY,dims,Array::allocateArray(FM_CELL_ARRAY,dims.getElementCount()));
  ArrayVector retval;
  retval.push_back(s);
  return retval;
}

//!
//@Module ONES Array of Ones
//@@Section ARRAY
//@@Usage
//Creates an array of ones of the specified size.  Two seperate 
//syntaxes are possible.  The first syntax specifies the array 
//dimensions as a sequence of scalar dimensions:
//@[
//   y = ones(d1,d2,...,dn).
//@]
//The resulting array has the given dimensions, and is filled with
//all ones.  The type of @|y| is @|float|, a 32-bit floating
//point array.  To get arrays of other types, use the typecast 
//functions (e.g., @|uint8|, @|int8|, etc.).
//    
//The second syntax specifies the array dimensions as a vector,
//where each element in the vector specifies a dimension length:
//@[
//   y = ones([d1,d2,...,dn]).
//@]
//This syntax is more convenient for calling @|ones| using a 
//variable for the argument.  In both cases, specifying only one
//dimension results in a square matrix output.
//@@Example
//The following examples demonstrate generation of some arrays of ones
//using the first form.
//@<
//ones(2,3,2)
//ones(1,3)
//@>
//The same expressions, using the second form.
//@<
//ones([2,6])
//ones([1,3])
//@>
//Finally, an example of using the type casting function @|uint16| to generate an array of 16-bit unsigned integers with a value of 1.
//@<
//uint16(ones(3))
//@>
//!
ArrayVector OnesFunction(int nargout, const ArrayVector& arg) {
  Array t, s;
  Dimensions dims;
  int32 *dp;
  int i;
  if (arg.size() == 0)
    dims.makeScalar();
  else {
    // Case 1 - all of the entries are scalar
    bool allScalars;
    allScalars = true;
    for (i=0;i<arg.size();i++)
      allScalars &= arg[i].isScalar();
    if (allScalars) {
      t = arg[0];
      if (arg.size() == 1) {
	// If all scalars and only one argument - we want a square zero matrix
	dims.set(0,t.getContentsAsIntegerScalar());
	dims.set(1,dims.get(0));
      } else {
	// If all scalars and and multiple arguments, we count dimensions
	for (i=0;i<arg.size();i++) {
	  t = arg[i];
	  dims.set(i,t.getContentsAsIntegerScalar());
	}
	  
      }
    } else {
      if (arg.size() > 1)
	throw Exception("Arguments to ones function must be either all scalars or a single vector");
      t = arg[0];
      t.promoteType(FM_UINT32);
      dp = (int*) t.getDataPointer();
      for (i=0;i<t.getLength();i++)
	dims.set(i,dp[i]);
    }
    bool allPositive;
    allPositive = true;
    for (i=0;i<dims.getLength();i++)
      allPositive &= (dims.get(i) >= 0);
    if (!allPositive)
      throw Exception("Ones function requires positive arguments");
  }
  int len;
  len = dims.getElementCount();
  float *qp;
  qp = (float*) Malloc(sizeof(float)*len);
  for (i=0;i<len;i++)
    qp[i] = 1.0f;
  s = Array(FM_FLOAT,dims,qp);
  ArrayVector retval;
  retval.push_back(s);
  return retval;
}

//!
//@Module STRUCT Structure Array Constructor
//@@Section VARIABLES
//@@Usage
//Creates an array of structures from a set of field, value pairs.
//The syntax is
//@[
//   y = struct(n_1,v_1,n_2,v_2,...)
//@]
//where @|n_i| are the names of the fields in the structure array, and
//@|v_i| are the values.  The values @|v_i| must either all be
//scalars, or be cell-arrays of all the same dimensions.  In the latter 
//case, the
//output structure array will have dimensions dictated by this common
//size.  Scalar entries for the @|v_i| are replicated to fill out
//their dimensions. An error is raised if the inputs are not properly matched (i.e., are
//not pairs of field names and values), or if the size of any two non-scalar
//values cell-arrays are different.
//
//Another use of the @|struct| function is to convert a class into a 
//structure.  This allows you to access the members of the class, directly 
//but removes the class information from the object.
//
//@@Example
//This example creates a 3-element structure array with three fields, @|foo|
//@|bar| and @|key|, where the contents of @|foo| and @|bar| are provided 
//explicitly as cell arrays of the same size, and the contents of @|bar| 
//are replicated from a scalar.
//@<
//y = struct('foo',{1,3,4},'bar',{'cheese','cola','beer'},'key',508)
//y(1)
//y(2)
//y(3)
//@>
//
//An alternate way to create a structure array is to initialize the last
//element of each field of the structure
//@<
//Test(2,3).Type = 'Beer';
//Test(2,3).Ounces = 12;
//Test(2,3).Container = 'Can';
//Test(2,3)
//Test(1,1)
//@>
//@@Tests
//@{ test_struct1.m
//% Test the ability to combine structures with different ordering in their elements.
//function test_val = test_struct1
//a.foo = 1;
//a.goo = 2;
//b.goo = 5;
//b.foo = 4;
//c = [a,b];
//test_val = (c(1).foo == 1) & (c(2).foo == 4) & (c(1).goo == 2) & (c(2).foo == 4);
//@}
//@{ test_struct2.m
//% Test the ability to combine structues with different fields - when valid
//function test_val = test_struct2
//a.foo = 1;
//a.goo = 2;
//b.goo = 5;
//c = [a,b];
//test_val = (c(1).foo == 1) & (c(1).goo == 2) & (c(2).goo == 5);
//@}
//@{ test_struct3.m
//% Test the ability to add a new field name to a structure
//function test_val = test_struct3
//a.foo = 1;
//a.goo = 2;
//c(1) = a;
//c(2) = a;
//c(1).hoo = 6;
//test_val = (c(1).foo == 1) & (c(2).foo == 1) & (c(1).goo == 2) & (c(2).goo == 2) & (c(1).hoo == 6);
//@}
//@{ test_struct4.m
//% Test the structure constructor
//function test_val = test_struct4
//a = struct('foo',4,'goo',{5},'hoo',{'time',8});
//test_val = test(a(1).foo == 4) & test(a(2).foo == 4) ...
//    & test(a(1).goo == 5) & test(a(2).goo == 5) ...
//    & test(strcmp(a(1).hoo,'time')) & test(a(2).hoo == 8);
//@}
//@{ test_struct5.m
//% Test the field dereference as an expression list
//function test_val = test_struct5
//  a = struct('foo',{5,8,10});
//  sm = test_struct5_assist(a.foo);
//  test_val = test(sm == 23);
//
//function a = test_struct5_assist(b,c,d)
//  a = b + c + d;
//@}
//@{ test_struct6.m
//% Test the field set function with a scalar argument
//function test_val = test_struct6
//  a = struct('foo',5,'color','blue');
//  a.color = 'red';
//  test_val = test(strcmp(a.color,'red'));
//@}
//@{ test_struct7.m
//% Test the field set function with a vector argument
//function test_val = test_struct7
//  a = struct('foo',{6,5,3},'color','blue');
//  test_val = 0;
//  try
//    a.foo = 7;
//  catch
//    test_val = 1;
//  end
//@}
//@{ test_struct8.m
//% Test the structure field-based multiple assign in a function call
//function test_val = test_struct8
//a = struct('foo',{0,0,0,0});
//[a.foo] = test_struct8_assist;
//test_val = test(a(1).foo == 1) & test(a(2).foo == 2) & ...
//    test(a(3).foo == 3) & test(a(4).foo == 4);
//
//function [a,b,c,d] = test_struct8_assist
//  a = 1;
//  b = 2;
//  c = 3;
//  d = 4;
//@}
//@{ test_struct9.m
//% Test the ability to assign a single element in a structure array
//function test_val = test_struct9
//d = struct('foo',3,'goo','hello','shoo',1:5);
//d(3).shoo = 3;
//test_val = test(d(3).shoo == 3);
//@}
//!
ArrayVector StructFunction(int nargout, const ArrayVector& arg) {
  if (arg.size() == 1) {
    Array t(arg[0]);
    if (!t.isUserClass() && t.dataClass() == FM_STRUCT_ARRAY)
      return singleArrayVector(t);
    if (!t.isUserClass())
      throw Exception("can only convert objects (user-defined types) into structs");
    t.setClassName(rvstring());
    return singleArrayVector(t);
  }
  if (arg.size() % 2)
    throw Exception("struct function requires pairs of field names and values");
  int pairCount = arg.size() / 2;
  rvstring names;
  ArrayVector values;
  for (int i=0;i<pairCount;i++) values.push_back(Array());
  for (int i=0;i<pairCount*2;i+=2) {
    if (!(arg[i].isString()))
      throw Exception("struct function requires pairs of field names and values");
    names.push_back(arg[i].getContentsAsString());
    values[i/2] = arg[i+1];
  }
  ArrayVector retval;
  retval.push_back(Array::structConstructor(names,values));
  return retval;
}


syntax highlighted by Code2HTML, v. 0.9.1