/* * 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 "Exception.hpp" #include "Data.hpp" #include "Malloc.hpp" #include "IEEEFP.hpp" #include "Interpreter.hpp" #include "Sparse.hpp" #include #include #include #include "FunctionDef.hpp" #include "NumericArray.hpp" #include "LAPACK.hpp" static int objectBalance; #define MSGBUFLEN 2048 typedef std::set > intSet; intSet addresses; bool isColonOperator(Array& a) { return ((a.dataClass() == FM_STRING) && (a.getLength() == 1) && (((const char*) a.getDataPointer())[0] == ':')); } ArrayVector singleArrayVector(Array a) { ArrayVector retval; retval.push_back(a); return retval; } ArrayVector operator+(Array a, Array b) { ArrayVector retval; retval.push_back(a); retval.push_back(b); return retval; } ArrayVector operator+(ArrayVector a, Array b) { a.push_back(b); return a; } ArrayVector operator+(Array a, ArrayVector b) { b.push_front(a); return b; } void outputDoublePrecisionFloat(char *buf, double num) { char temp_buf[100]; char *tbuf; int len; tbuf = temp_buf; if (num>=0) { sprintf(tbuf," "); tbuf++; } if (IsNaN(num)) sprintf(tbuf," nan"); else if (fabs(num)>=0.1f && fabs(num)<1.0f || num == 0.0f) sprintf(tbuf," %0.15f",num); else if (fabs(num)>=0.01f && fabs(num)<0.1f) sprintf(tbuf," %0.16f",num); else if (fabs(num)>=0.001f && fabs(num)<0.01f) sprintf(tbuf," %0.17f",num); else if (fabs(num)>=1.0f && fabs(num)<10.0f) sprintf(tbuf," %1.15f",num); else if (fabs(num)>=10.0f && fabs(num)<100.0f) sprintf(tbuf," %2.13f",num); else if (fabs(num)>=100.0f && fabs(num)<1000.0f) sprintf(tbuf,"%3.12f",num); else sprintf(tbuf," %1.14e",num); len = strlen(temp_buf); memcpy(buf,temp_buf,len); memset(buf+len,' ',24-len); buf[24] = 0; } void outputSinglePrecisionFloat(char *buf, float num) { char temp_buf[100]; char *tbuf; int len; tbuf = temp_buf; if (num>=0) { sprintf(tbuf," "); tbuf++; } if (IsNaN(num)) sprintf(tbuf," nan"); else if (fabs(num)>=0.1f && fabs(num)<1.0f || num == 0.0f) sprintf(tbuf," %0.8f",num); else if (fabs(num)>=0.01f && fabs(num)<0.1f) sprintf(tbuf," %0.9f",num); else if (fabs(num)>=0.001f && fabs(num)<0.01f) sprintf(tbuf," %0.10f",num); else if (fabs(num)>=1.0f && fabs(num)<10.0f) sprintf(tbuf," %1.7f",num); else if (fabs(num)>=10.0f && fabs(num)<100.0f) sprintf(tbuf," %2.6f",num); else if (fabs(num)>=100.0f && fabs(num)<1000.0f) sprintf(tbuf,"%3.5f",num); else sprintf(tbuf," %1.7e",num); len = strlen(temp_buf); memcpy(buf,temp_buf,len); memset(buf+len,' ',17-len); buf[17] = 0; } void* Array::allocateArray(Class type, uint32 length, rvstring names) { if (length == 0) return NULL; switch(type) { case FM_FUNCPTR_ARRAY: { FuncPtr *dp = new FuncPtr[length]; for (int i=0;i= maxD) { Free(map); throw Exception("Array index exceeds bounds"); } map[n] = true; } return map; } uint32 Array::getMaxAsIndex() { indexType maxval; int k; constIndexPtr rp = (constIndexPtr) data(); int K = getLength(); maxval = rp[0]; for (k=1;k maxval) maxval = rp[k]; if (maxval <= 0) throw Exception("Illegal zero or negative index"); return maxval; } void Array::toOrdinalType(Interpreter *m_eval) { // Special case : sparse matrices with logical type can be efficiently // converted to ordinal types if (sparse() && dataClass() == FM_LOGICAL) { int outcount; uint32 *sp = SparseLogicalToOrdinal(dimensions().get(0),dimensions().get(1),data(),outcount); setData(FM_UINT32,Dimensions(outcount,1),sp); return; } if (sparse()) makeDense(); switch(dataClass()) { case FM_LOGICAL: { // We make a first pass through the array, and count the number of // non-zero entries. const logical *rp = (const logical *) data(); int indexCount = 0; int len = getLength(); int i; for (i=0;iwarningMessage("Imaginary part of complex index ignored.\n"); // We convert complex values into real values const double *rp = (const double *) data(); int len = getLength(); indexType ndx; bool fractionalWarning = false; // Allocate space to hold the new type indexType *lp = (indexType *) Malloc(len*sizeof(indexType)); for (int i=0;iwarningMessage("Fractional part of index ignored.\n"); fractionalWarning = true; } if (ndx <= 0) throw Exception("Zero or negative index encountered."); lp[i] = ndx; } setData(FM_UINT32,dimensions(),lp); } break; case FM_COMPLEX: { m_eval->warningMessage("Imaginary part of complex index ignored.\n"); // We convert complex values into real values const float *rp = (const float *) data(); int len = getLength(); indexType ndx; bool fractionalWarning = false; // Allocate space to hold the new type indexType *lp = (indexType *) Malloc(len*sizeof(indexType)); for (int i=0;iwarningMessage("Fractional part of index ignored.\n"); fractionalWarning = true; } if (ndx <= 0) throw Exception("Zero or negative index encountered."); lp[i] = ndx; } setData(FM_UINT32,dimensions(),lp); } break; case FM_DOUBLE: { const double *rp = (const double *) data(); int len = getLength(); indexType ndx; bool fractionalWarning = false; // Allocate space to hold the new type indexType *lp = (indexType *) Malloc(len*sizeof(indexType)); for (int i=0;iwarningMessage("Fractional part of index ignored.\n"); fractionalWarning = true; } if (ndx <= 0) throw Exception("Zero or negative index encountered."); lp[i] = ndx; } setData(FM_UINT32,dimensions(),lp); } break; case FM_FLOAT: { const float *rp = (const float *) data(); int len = getLength(); indexType ndx; bool fractionalWarning = false; // Allocate space to hold the new type indexType *lp = (indexType *) Malloc(len*sizeof(indexType)); for (int i=0;iwarningMessage("Fractional part of index ignored.\n"); fractionalWarning = true; } if (ndx <= 0) throw Exception("Zero or negative index encountered."); lp[i] = ndx; } setData(FM_UINT32,dimensions(),lp); } break; case FM_INT64: { const int64 *rp = (const int64 *) data(); int len = getLength(); indexType ndx; // Allocate space to hold the new type indexType *lp = (indexType *) Malloc(len*sizeof(indexType)); for (int i=0;i= 2) that is outside the //current dimension bounds of the array, then the array is //zero padded until the it is large enough for the assignment //to work. If the array is a scalar, and an assignment is //made to the non-unity element, such as: //@[ //a = 1; //a(3) = 4; //@] //then the result will be a row vector (in this case, of size 3). //Row and column vectors will be resized so as to preserve their //orientation. And if an n-dimensional array is forced to resize //using the vector notation, then the result is a row vector. //@@Tests //@{ test_resize1.m //function test_val = test_resize1 //% Check a normal resize in 2D //a = []; //a = [1,2;3,4]; //a(3,3) = 1; //test_val = all(a == [1,2,0;3,4,0;0,0,1]); //@} //@{ test_resize2.m //function test_val = test_resize2 //% Check a scalar -> vector resize //a = 1; //a(3) = 1; //test_val = all(a == [1,0,1]); //@} //@{ test_resize3.m //function test_val = test_resize3 //% Check a row-vector -> vector resize //a = [1 2]; //a(3) = 1; //test_val = all(a == [1,2,1]); //@} //@{ test_resize4.m //function test_val = test_resize4 //% Check a column-vector -> vector resize //a = [1;2]; //a(3) = 1; //test_val = all(a == [1;2;1]); //@} //@{ test_resize5.m //function test_val = test_resize5 //a = 0; //a(2,1,2) = 0; //a(5) = 1; //test_val = all(a == [0,0,0,0,1]); //@} //@{ test_delete1.m //% Check the delete functionality in a vector setting //function test_val = test_delete1 //a = [1,2,3,4,5]; //a([2,3,4]) = []; //test_val = test(a == [1,5]); //@} //@{ test_delete2.m //% Check the delete functionality in an ndim setting //function test_val = test_delete2 //a = [1,2,3;4,5,6]; //a(:,2) = []; //test_val = test(a == [1,3;4,6]); //@} //@{ test_delete3.m //% Check the delete functionality in an ndim setting //function test_val = test_delete3 //a = [1,2;3,4]; //a(2,2,2) = 7; //a(:,:,2) = []; //test_val = test(a == [1,2;3,4]); //@} //@{ test_delete4.m //% Check the delete-all functionality in an ndim setting //function test_val = test_delete4 //a = [1,2,3;4,5,6]; //a(:,:) = []; //test_val = test(isempty(a)); //@} //@{ test_delete5.m //% Check the delete-all functionality in a vector setting //function test_val = test_delete5 //a = [1,2,3;4,5,6]; //a(:) = []; //test_val = test(isempty(a)); //@} //@{ test_delete6.m //% Check the multi-dim delete function with an invalid argument //function test_val = test_delete6 //a = [1,2,3;4,5,6;7,8,9]; //a(3,3,2) = 10; //test_val = 0; //try // a(1,:,2) = []; //catch // test_val = 1; //end //@} //! void Array::resize(Dimensions& a) { Dimensions newSize; // Make a copy of the current dimension vector, and // compute the new dimension size. newSize = dimensions(); newSize.expandToCover(a); // Check to see if the dimensions are unchanged. if (newSize.equals(dimensions())) return; // Check to see if the total number of elements is unchanged. if (newSize.getElementCount() == getLength()) { reshape(newSize); return; } if (!dp) { void *dst_data = allocateArray(dataClass(),newSize.getElementCount(), fieldNames()); setData(dataClass(),newSize,dst_data,sparse(),fieldNames(),className()); return; } if (sparse()) { setData(dataClass(),newSize, CopyResizeSparseMatrix(dataClass(), dimensions().get(0), dimensions().get(1), data(), newSize.get(0), newSize.get(1)),true); return; } // Allocate space for our new size. void *dst_data = allocateArray(dataClass(),newSize.getElementCount(), fieldNames()); if (!isEmpty()) { // Initialize a pointer to zero. Dimensions curPos(dimensions().getLength()); // Because we copy & convert data a column at a time, we retrieve // the number of rows in each column. int rowCount = dimensions().get(0); // Track our offset into the original data. int srcIndex = 0; // Loop until we have exhausted the original data. int dstIndex; while (curPos.inside(dimensions())) { // Get the destination index for the current source position. dstIndex = newSize.mapPoint(curPos); // Copy the data from our original data structure to the // new data structure, starting from the source index // srcIndex, and moving to dstIndex. copyElements(srcIndex,dst_data,dstIndex,rowCount); // Update the column number (as we have just copied an // entire column). curPos.incrementModulo(dimensions(),1); // Advance the source data pointer so that it points to the // start of the next column. srcIndex += rowCount; } } setData(dataClass(),newSize,dst_data, sparse(),fieldNames(),className()); } void Array::permute(const int32 *permutation) { // Check for an identity permutation int Adims = dimensions().getLength(); bool id_perm = true; for (int i=0;i getLength()) { Dimensions newDim; if (isEmpty() || dimensions().isScalar()) { newDim.reset(); newDim = Dimensions(1,max_index); } else if (dimensions().isVector()) { newDim = dimensions(); if (dimensions().get(0) != 1) newDim.set(0,max_index); else newDim.set(1,max_index); } else { // First reshape it Dimensions tDim(1,getLength()); reshape(tDim); newDim = Dimensions(1,max_index); } resize(newDim); } } /** * Reshape an array. This is only legal if the number of * elements remains the same after reshaping. */ void Array::reshape(Dimensions& a) { if (a.getElementCount() != getLength()) throw Exception("Reshape operation cannot change the number of elements in array."); if (sparse()) { a.simplify(); if (a.getLength() > 2) throw Exception("Cannot reshape sparse matrix to an N-dimensional array - FreeMat does not support N-dimensional sparse arrays"); setData(dataClass(),a, ReshapeSparseMatrix(dataClass(), dimensions().get(0), dimensions().get(1), data(), a.get(0), a.get(1)),true); } else { dp->setDimensions(a); } } /** * Hermitian transpose our array. By default, this is just a transpose * operation. The distinction is made in complex classed by overriding * this method. */ void Array::hermitian() { if (!is2D()) throw Exception("Cannot apply Hermitian transpose operation to multi-dimensional array."); if (isEmpty()) { int rows = getDimensionLength(0); int cols = getDimensionLength(1); dp->setDimensions(Dimensions(cols,rows)); return; } if (!isComplex()) transpose(); else { if (sparse()) { int rows = getDimensionLength(0); int cols = getDimensionLength(1); void *qp = SparseArrayHermitian(dataClass(), rows, cols, data()); setData(dataClass(),Dimensions(cols,rows),qp,true); return; } if (dataClass() == FM_COMPLEX) { // Allocate space for our transposed array void *dstPtr = allocateArray(dataClass(),getLength()); float *qp, *sp; int i, j; int rowCount; int colCount; rowCount = dimensions().get(0); colCount = dimensions().get(1); int ptr; qp = (float*) dstPtr; sp = (float*) data(); ptr = 0; for (i=0;isetDimensions(Dimensions(cols,rows)); return; } if (sparse()) { int rows = getDimensionLength(0); int cols = getDimensionLength(1); void *qp = SparseArrayTranspose(dataClass(), rows, cols, data()); setData(dataClass(),Dimensions(cols,rows),qp,true); return; } // Allocate space for our transposed array void *dstPtr = allocateArray(dataClass(),getLength(),fieldNames()); int i, j; int rowCount; int colCount; rowCount = dimensions().get(0); colCount = dimensions().get(1); int ptr; ptr = 0; for (i=0;i= 0);\ i++;\ }\ return allPositive;\ } const bool Array::isPositive() const { if (dataClass() == FM_UINT8 || dataClass() == FM_UINT16 || dataClass() == FM_UINT32 || dataClass() == FM_UINT64) return true; if (dataClass() == FM_COMPLEX || dataClass() == FM_DCOMPLEX) return false; if (sparse()) return SparseIsPositive(dataClass(), getDimensionLength(0), getDimensionLength(1), getSparseDataPointer()); switch (dataClass()) { caseMacro(FM_FLOAT,float); caseMacro(FM_DOUBLE,double); caseMacro(FM_INT8,int8); caseMacro(FM_INT16,int16); caseMacro(FM_INT32,int32); caseMacro(FM_INT64,int64); } return false; } #undef caseMacro const bool Array::isRealAllZeros() const { bool allZeros; int len = getLength(); int i; #define caseMacro(caseLabel,dpType,testcode) \ case caseLabel:\ {\ const dpType* qp = (const dpType*) data();\ while (allZeros && (i y.dataClass()) y.promoteType(x.dataClass()); else x.promoteType(y.dataClass()); // Finally, we can do a compare.... const void *x_dp = x.data(); const void *y_dp = y.data(); bool retval; switch(x.dataClass()) { caseMacroReal(FM_LOGICAL,logical); caseMacroReal(FM_UINT8,uint8); caseMacroReal(FM_INT8,int8); caseMacroReal(FM_UINT16,uint16); caseMacroReal(FM_INT16,int16); caseMacroReal(FM_UINT32,uint32); caseMacroReal(FM_INT32,int32); caseMacroReal(FM_UINT64,uint64); caseMacroReal(FM_INT64,int64); caseMacroReal(FM_FLOAT,float); caseMacroReal(FM_DOUBLE,double); caseMacroComplex(FM_COMPLEX,float); caseMacroComplex(FM_DCOMPLEX,double); } return retval; } #undef caseMacroReal #undef caseMacroComplex const bool Array::testForCaseMatch(Array x) const { if (sparse()) throw Exception("isPositive not supported for sparse arrays."); // We had better be a scalar if (!(isScalar() || isString())) throw Exception("Switch argument must be a scalar or a string"); // And we had better not be a reference type if (isReferenceType()) throw Exception("Switch argument cannot be a reference type (struct or cell array)"); // If x is a scalar, we just need to call the scalar version if (((x.dataClass() != FM_CELL_ARRAY) && x.isScalar()) || x.isString()) return testCaseMatchScalar(x); if (x.dataClass() != FM_CELL_ARRAY) throw Exception("Case arguments must either be a scalar or a cell array"); const Array* qp = (const Array*) x.data(); int len; len = x.getLength(); bool foundMatch = false; int i = 0; while (i fNames.size()) throw Exception("Cannot combine structures with different fields if the combination requires fields to be deleted from one of the structures."); // We are promoting a struct array to a struct array. // To do so, we have to make sure that the field names work out. // The only thing we must check for is that every field name // in fieldnames is present in fnames. int extraCount = 0; int matchCount = 0; int ndx; int i; for (i=0;i // //One important (tricky) point in FreeMat is the treatment //of escape sequences. Recall that in @|C| programming, an //escape sequence is a special character that causes the output //to do something unusual. FreeMat supports the following //escape sequences: //\begin{itemize} //\item @|\t| - causes a tab to be output //\item @|\r| - causes a carriage return (return to the beginning // of the line of output, and overwrite the text) //\item @|\n| - causes a linefeed (advance to next line) //\end{itemize} //FreeMat follows the @|Unix/Linux| convention, that a @|\n| //causes both a carriage return and a linefeed. //To put a single quote into a string use the MATLAB convention //of two single quotes, not the @|\'| sequence. Here is an //example of a string containing some escape sequences: //@< //a = 'I can''t use contractions\n\tOr can I?\n' //@> //Now, note that the string itself still contains the @|\n| //characters. With the exception of the @|\'|, the escape //sequences do not affect the output unless the strings are //put through @|printf| or @|fprintf|. For example, if we //@|printf| the variable @|a|, we see the @|\n| and @|\t| //take effect: //@< //printf(a); //@> //The final complicating factor is on @|MSWin| systems. There, //filenames regularly contain @|\| characters. Thus, if you //try to print a string containing the filename //@|C:\redball\timewarp\newton.txt|, the output will be mangled //because FreeMat thinks the @|\r|, @|\t| and @|\n| are escape //sequences. You have two options. You can use @|disp| to show //the filename (@|disp| does not do escape translation to be //compatible with MATLAB). The second option is to escape the //backslashes in the string, so that the string you send to //@|printf| contains @|C:\\redball\\timewarp\\newton.txt|. //@< //% disp displays it ok //a = 'C:\redball\timewarp\newton.txt' //% printf makes a mess //printf(a) //% If we double up the slashes it works fine //a = 'C:\\redball\\timewarp\\newton.txt' //printf(a) //@> //! Array Array::stringConstructor(std::string astr) { int length; char *cp; length = astr.length(); cp = (char *) allocateArray(FM_STRING,length); memcpy(cp,astr.c_str(),length); return Array(FM_STRING,Dimensions(1,length),cp); } Array Array::int32RangeConstructor(int32 minval, int32 stepsize, int32 maxval, bool vert) { Dimensions dim; int32 *rp = NULL; if (stepsize == 0) throw Exception("step size must be nonzero in colon expression"); int scount = 0; int accum = minval; if (stepsize > 0) { while (accum <= maxval) { accum += stepsize; scount++; } } else { while (accum >= maxval) { accum += stepsize; scount++; } } if (vert) { dim = Dimensions(scount,1); } else { dim = Dimensions(1,scount); } rp = (int32 *) allocateArray(FM_INT32,scount); for (int i=0;i 0) { while (accum <= maxval) { accum += stepsize; scount++; } } else { while (accum >= maxval) { accum += stepsize; scount++; } } if (vert) { dim = Dimensions(scount,1); } else { dim = Dimensions(1,scount); } rp = (int64 *) allocateArray(FM_INT64,scount); for (int i=0;i |x|, sign(y) = sign(x)} float ntest_min = nextafterf(maxval-minval,0)/nextafterf(stepsize,stepsize+stepsize); float ntest_max = nextafterf(maxval-minval,maxval-minval+stepsize)/nextafterf(stepsize,0); int npts = (int) floor(ntest_max); bool use_double_sided = (ntest_min <= npts) && (npts <= ntest_max); npts++; if (npts < 0) npts = 0; if (vert) { dim = Dimensions(npts,1); } else { dim = Dimensions(1,npts); } rp = (float *) allocateArray(FM_FLOAT,npts); if (use_double_sided) do_double_sided_algo_float(minval,stepsize,maxval,rp,1,npts); else do_single_sided_algo_float(minval,stepsize,rp,1,npts); return Array(FM_FLOAT,dim,rp); } void do_single_sided_algo_double(double a, double b,double *pvec, int adder, int count) { double d = a; for (int i=0;i |x|, sign(y) = sign(x)} double ntest_min = nextafter(maxval-minval,0)/nextafter(stepsize,stepsize+stepsize); double ntest_max = nextafter(maxval-minval,maxval-minval+stepsize)/nextafter(stepsize,0); int npts = (int) floor(ntest_max); bool use_double_sided = (ntest_min <= npts) && (npts <= ntest_max); npts++; if (npts < 0) npts = 0; if (vert) { dim = Dimensions(npts,1); } else { dim = Dimensions(1,npts); } rp = (double *) allocateArray(FM_DOUBLE,npts); if (use_double_sided) do_double_sided_algo_double(minval,stepsize,maxval,rp,1,npts); else do_single_sided_algo_double(minval,stepsize,rp,1,npts); return Array(FM_DOUBLE,dim,rp); } Array Array::matrixConstructor(ArrayMatrix& m) { Dimensions mat_dims; Dimensions row_dims; Class maxType, minType; Class retType; rvstring retNames; Dimensions retDims; void *dstPtr = NULL; bool sparseArg = false; try { maxType = FM_FUNCPTR_ARRAY; minType = FM_STRING; ArrayMatrix::iterator i = m.begin(); bool firstNonzeroColumn = true; bool firstNonzeroRow = true; while (i != m.end()) { ArrayVector ptr = (ArrayVector) *i; for (int j=0;j d.dataClass()) minType = d.dataClass(); if (firstNonzeroColumn) { /** * For the first element in the row, we copy * the dimensions into the row buffer. */ row_dims = d.dimensions(); /** * Because we are concatenating in columns, * we must have at least 1 column in the data. * The dimension array can be thought of as being * infinitely extendable with 1's for all higher * dimensions. We will take advantage of this to * force the elements to have at least one column. */ firstNonzeroColumn = false; } else { /** * Cycle through the elements of the row. * Check these elements against the dimension * buffer, and update the second dimension (column * count). */ if ((d.dimensions().getLength() != row_dims.getLength()) && (d.dimensions().getLength()>1)) throw Exception("Number of dimensions must match for each element in a row definition"); if (d.dimensions().get(0) != row_dims.get(0)) throw Exception("Mismatch in first dimension for elements in row definition"); for (int k=2;k= bound) throw Exception("Index exceeds variable dimensions"); copyElements(ndx,qp,i,1); } return Array(dataClass(),retdims,qp,sparse(),fieldNames(), className()); } catch (Exception &e) { throw; } } /** * Given a vector of indexing arrays, convert them into * index pointers. If a colon is encountered, it is * preserved (the first one -- the remaining colon expressions * are expanded out into vectors). */ constIndexPtr* ProcessNDimIndexes(bool preserveColons, Dimensions dims, ArrayVector& index, bool& anyEmpty, int& colonIndex, Dimensions& outDims, bool argCheck, Interpreter* m_eval) { int L = index.size(); constIndexPtr* outndx = (constIndexPtr *) Malloc(sizeof(constIndexPtr*)*L); bool colonFound = false; anyEmpty = false; colonIndex = -1; for (int i=0;i dims.get(i))) throw Exception("index exceeds array bounds"); outndx[i] = (constIndexPtr) index[i].getDataPointer(); outDims.set(i,index[i].getLength()); } } return outndx; } bool allScalars(const ArrayVector& index) { for (int i=0;iwarningMessage("Imaginary part of complex index ignored.\n"); // We convert complex values into real values const double *rp = (const double *) a.data(); return (indexType) rp[0]; } break; case FM_COMPLEX: { m_eval->warningMessage("Imaginary part of complex index ignored.\n"); // We convert complex values into real values const float *rp = (const float *) a.data(); return (indexType) rp[0]; } break; case FM_DOUBLE: { const double *rp = (const double *) a.data(); if (rp[0] != (indexType) rp[0]) m_eval->warningMessage("Fractional part of index ignored.\n"); return (indexType) rp[0]; } break; case FM_FLOAT: { const float *rp = (const float *) a.data(); if (rp[0] != (indexType) rp[0]) m_eval->warningMessage("Fractional part of index ignored.\n"); return (indexType) rp[0]; } break; case FM_INT64: { const int64 *rp = (const int64 *) a.data(); return (indexType) rp[0]; } break; case FM_UINT64: { const uint64 *rp = (const uint64 *) a.data(); return (indexType) rp[0]; } break; case FM_INT32: { const int32 *rp = (const int32 *) a.data(); return (indexType) rp[0]; } break; case FM_UINT32: { const uint32 *rp = (const uint32 *) a.data(); return (indexType) rp[0]; } break; case FM_INT16: { const int16 *rp = (const int16 *) a.data(); return (indexType) rp[0]; } break; case FM_UINT16: { const uint16 *rp = (const uint16 *) a.data(); return (indexType) rp[0]; } break; case FM_INT8: { const int8 *rp = (const int8 *) a.data(); return (indexType) rp[0]; } break; case FM_UINT8: { const uint8 *rp = (const uint8 *) a.data(); return (indexType) rp[0]; } break; case FM_CELL_ARRAY: { throw Exception("Cannot convert cell arrays to indices."); } break; case FM_STRUCT_ARRAY: { throw Exception("Cannot convert structure arrays to indices."); } break; case FM_FUNCPTR_ARRAY: { throw Exception("Cannot convert function pointer arrays to indices."); } break; } } Array Array::getNDimSubsetScalars(ArrayVector& index, Interpreter* m_eval) { int ndx = 0; int pagesize = 1; for (int i=0;i getDimensionLength(i))) throw Exception("index exceeds array bounds"); ndx += pagesize*(q-1); pagesize *= getDimensionLength(i); } void *qp = allocateArray(dataClass(),1,fieldNames()); copyElements(ndx,qp,0,1); return Array(dataClass(),Dimensions(1,1),qp,sparse(),fieldNames(),className()); } void Array::setNDimSubsetScalars(ArrayVector& index, Array &rdata, Interpreter* m_eval) { if (!rdata.isScalar()) throw Exception("rhs must be scalar for scalar set!"); if (sparse() || rdata.sparse()) throw Exception("sparse case not allowed for scalar set!"); if (dataClass() == FM_STRUCT_ARRAY) throw Exception("structure arrays not allowed for scalar set!"); if (dataClass() < rdata.dataClass()) throw Exception("type mismatch not allowed for scalar set!"); rdata.promoteType(dataClass()); int ndx = 0; int pagesize = 1; for (int i=0;i getDimensionLength(i))) throw Exception("index exceeds array bounds"); ndx += pagesize*(q-1); pagesize *= getDimensionLength(i); } rdata.copyElements(0,getReadWriteDataPointer(),ndx,1); } /** * Take the current variable, and return a new array consisting of * the elements in source indexed by the index argument. Indexing * is done using ndimensional indices. */ Array Array::getNDimSubset(ArrayVector& index, Interpreter* m_eval) { constIndexPtr* indx = NULL; void *qp = NULL; int i; bool anyEmpty; int colonIndex; Dimensions myDims(dimensions()); if (!sparse() && allScalars(index)) return getNDimSubsetScalars(index, m_eval); // if (isEmpty()) // throw Exception("Cannot index into empty variable."); try { int L = index.size(); Dimensions outDims(L); indx = ProcessNDimIndexes(true,myDims, index, anyEmpty, colonIndex, outDims, true, m_eval); if (anyEmpty) { Free(indx); return Array::emptyConstructor(); } if (sparse()) { if (L > 2) throw Exception("multidimensional indexing (more than 2 dimensions) not legal for sparse arrays"); if ((outDims.get(0) == 1) && (outDims.get(1) == 1)) return Array(dataClass(),outDims, GetSparseScalarElement(dataClass(), getDimensionLength(0), getDimensionLength(1), data(), *((const indexType*) indx[0]), *((const indexType*) indx[1])), false); else return Array(dataClass(),outDims, GetSparseNDimSubsets(dataClass(), getDimensionLength(0), getDimensionLength(1), data(), (const indexType*) indx[0], outDims.get(0), (const indexType*) indx[1], outDims.get(1)), true); } qp = allocateArray(dataClass(),outDims.getElementCount(),fieldNames()); int outDimsInt[maxDims]; int srcDimsInt[maxDims]; for (int i=0;i(colonIndex,(const float*) getDataPointer(), (float*) qp,outDimsInt,srcDimsInt, indx, L, 2); break; case FM_DCOMPLEX: getNDimSubsetNumericDispatchBurst(colonIndex,(const double*) getDataPointer(), (double*) qp,outDimsInt,srcDimsInt, indx, L, 2); break; case FM_LOGICAL: getNDimSubsetNumericDispatchReal(colonIndex,(const logical*) getDataPointer(), (logical*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_FLOAT: getNDimSubsetNumericDispatchReal(colonIndex,(const float*) getDataPointer(), (float*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_DOUBLE: getNDimSubsetNumericDispatchReal(colonIndex,(const double*) getDataPointer(), (double*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_INT8: getNDimSubsetNumericDispatchReal(colonIndex,(const int8*) getDataPointer(), (int8*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_UINT8: getNDimSubsetNumericDispatchReal(colonIndex,(const uint8*) getDataPointer(), (uint8*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_INT16: getNDimSubsetNumericDispatchReal(colonIndex,(const int16*) getDataPointer(), (int16*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_UINT16: getNDimSubsetNumericDispatchReal(colonIndex,(const uint16*) getDataPointer(), (uint16*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_INT32: getNDimSubsetNumericDispatchReal(colonIndex,(const int32*) getDataPointer(), (int32*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_UINT32: getNDimSubsetNumericDispatchReal(colonIndex,(const uint32*) getDataPointer(), (uint32*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_INT64: getNDimSubsetNumericDispatchReal(colonIndex,(const int64*) getDataPointer(), (int64*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_UINT64: getNDimSubsetNumericDispatchReal(colonIndex,(const uint64*) getDataPointer(), (uint64*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_STRING: getNDimSubsetNumericDispatchReal(colonIndex,(const char*) getDataPointer(), (char*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_FUNCPTR_ARRAY: getNDimSubsetNumericDispatchReal(colonIndex, (const FuncPtr*) getDataPointer(), (FuncPtr*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_CELL_ARRAY: getNDimSubsetNumericDispatchReal(colonIndex,(const Array*) getDataPointer(), (Array*) qp,outDimsInt,srcDimsInt, indx, L); break; case FM_STRUCT_ARRAY: getNDimSubsetNumericDispatchBurst(colonIndex,(const Array*) getDataPointer(), (Array*) qp,outDimsInt,srcDimsInt, indx, L, fieldNames().size()); break; } Free(indx); outDims.simplify(); return Array(dataClass(),outDims,qp,sparse(),fieldNames(), className()); } catch (Exception &e) { Free(indx); Free(qp); throw e; } } Array Array::getDiagonal(int diagonalOrder) { if (!is2D()) throw Exception("Cannot take diagonal of N-dimensional array."); int rows = dimensions().getRows(); int cols = dimensions().getColumns(); int outLen; Dimensions outDims; int i; int srcIndex; if (diagonalOrder < 0) { outLen = (rows+diagonalOrder) < cols ? (rows+diagonalOrder) : cols; outLen = (outLen < 0) ? 0 : outLen; if (outLen == 0) return Array::emptyConstructor(); outDims = Dimensions(outLen,1); void *qp; if (sparse()) { qp = GetSparseDiagonal(dataClass(), rows, cols, data(), diagonalOrder); return Array(dataClass(),outDims,qp,true); } else { qp = allocateArray(dataClass(),outLen,fieldNames()); for (i=0;i getLength()) throw Exception("Array index exceeds bounds of cell-array"); // Get a pointer to our data const Array* qp = (const Array*) data(); return qp[index_p[0]-1]; } /** * Return a subset of a cell array as a list. */ ArrayVector Array::getVectorContentsAsList(Array& index, Interpreter* m_eval) { ArrayVector m; if (dataClass() != FM_CELL_ARRAY) throw Exception("Attempt to apply contents-indexing to non cell-array object."); if (sparse()) throw Exception("getVectorContentsAsList not supported for sparse arrays."); if (index.isEmpty()) return ArrayVector(); if (isEmpty()) return ArrayVector(); if (isColonOperator(index)) { int cnt = getLength(); // Get a pointer to our data const Array* qp = (const Array*) data(); for (int i=0;i bound) throw Exception("Array index exceeds bounds of cell-array"); // Get the length of the index object int index_length = index.getLength(); // Get a pointer to the index data set constIndexPtr index_p = (constIndexPtr) index.data(); // Get a pointer to our data const Array* qp = (const Array*) data(); // Now we copy data from dp to m for (int i=0;i fieldNames().size()) promoteType(FM_STRUCT_ARRAY,rdata.fieldNames()); else rdata.promoteType(FM_STRUCT_ARRAY,fieldNames()); } else { if (isEmpty() || rdata.dataClass() > dataClass()) promoteType(rdata.dataClass(),rdata.fieldNames()); // If our type is superior to the RHS, we convert // the RHS to our type else if (rdata.dataClass() <= dataClass()) rdata.promoteType(dataClass(),fieldNames()); } // If the max index is larger than our current length, then // we have to resize ourselves - but this is only legal if we are // a vector. if (sparse()) { if (dataClass() == FM_LOGICAL) rdata.promoteType(FM_UINT32); int rows = getDimensionLength(0); int cols = getDimensionLength(1); void *qp = SetSparseVectorSubsets(dataClass(),rows,cols,data(), (const indexType*) index.data(), index.getDimensionLength(0), index.getDimensionLength(1), rdata.getDataPointer(), advance); setData(dataClass(),Dimensions(rows,cols),qp,true); return; } vectorResize(max_index); // Get a writable data pointer void *qp = getReadWriteDataPointer(); // Now, we copy data from the RHS to our real part, // computing indices along the way. indexType srcIndex = 0; indexType j; for (int i=0;i 0) && (colonDim < rdata.dimensions().getLength())) throw Exception("Assignment A(...) = b is not legal"); // Test for colonDim < rdata.get } } try { int L = index.size(); Dimensions myDims(dimensions()); Dimensions outDims; bool anyEmpty; int colonIndex; indx = ProcessNDimIndexes(true,myDims,index,anyEmpty,colonIndex,outDims,false,m_eval); if (anyEmpty) { Free(indx); return; } Dimensions a(L); // First, we compute the maximum along each dimension. int dataCount = 1; for (int i=0;i fieldNames().size()) promoteType(FM_STRUCT_ARRAY,rdata.fieldNames()); else rdata.promoteType(FM_STRUCT_ARRAY,fieldNames()); } else { if (isEmpty() || rdata.dataClass() > dataClass()) promoteType(rdata.dataClass(),rdata.fieldNames()); // If our type is superior to the RHS, we convert // the RHS to our type else if (rdata.dataClass() <= dataClass()) rdata.promoteType(dataClass(),fieldNames()); } // Now, resize us to fit this data resize(a); if (sparse()) { if (dataClass() == FM_LOGICAL) rdata.promoteType(FM_UINT32); if (L > 2) throw Exception("multidimensional indexing (more than 2 dimensions) not legal for sparse arrays in assignment A(I1,I2,...,IN) = B"); SetSparseNDimSubsets(dataClass(), getDimensionLength(0), dp->getWriteableData(), (const indexType*) indx[0], outDims.get(0), (const indexType*) indx[1], outDims.get(1), rdata.getDataPointer(),advance); return; } myDims = dimensions(); // Get a writable data pointer void *qp = getReadWriteDataPointer(); int outDimsInt[maxDims]; int srcDimsInt[maxDims]; for (int i=0;i(colonIndex,(float*) qp, (const float*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L, 2,advance); break; case FM_DCOMPLEX: setNDimSubsetNumericDispatchBurst(colonIndex,(double*) qp, (const double*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L, 2,advance); break; case FM_LOGICAL: setNDimSubsetNumericDispatchReal(colonIndex,(logical*) qp, (const logical*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_FLOAT: setNDimSubsetNumericDispatchReal(colonIndex,(float*) qp, (const float*) rdata.getDataPointer() ,outDimsInt,srcDimsInt, indx, L,advance); break; case FM_DOUBLE: setNDimSubsetNumericDispatchReal(colonIndex,(double*) qp, (const double*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_INT8: setNDimSubsetNumericDispatchReal(colonIndex,(int8*) qp, (const int8*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_UINT8: setNDimSubsetNumericDispatchReal(colonIndex,(uint8*) qp, (const uint8*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_INT16: setNDimSubsetNumericDispatchReal(colonIndex,(int16*) qp, (const int16*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_UINT16: setNDimSubsetNumericDispatchReal(colonIndex,(uint16*) qp, (const uint16*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_INT32: setNDimSubsetNumericDispatchReal(colonIndex,(int32*) qp, (const int32*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_UINT32: setNDimSubsetNumericDispatchReal(colonIndex,(uint32*) qp, (const uint32*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_INT64: setNDimSubsetNumericDispatchReal(colonIndex,(int64*) qp, (const int64*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_UINT64: setNDimSubsetNumericDispatchReal(colonIndex,(uint64*) qp, (const uint64*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_STRING: setNDimSubsetNumericDispatchReal(colonIndex,(char*) qp, (const char*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_FUNCPTR_ARRAY: setNDimSubsetNumericDispatchReal(colonIndex, (FuncPtr*) qp, (const FuncPtr*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_CELL_ARRAY: setNDimSubsetNumericDispatchReal(colonIndex,(Array*) qp, (const Array*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L,advance); break; case FM_STRUCT_ARRAY: setNDimSubsetNumericDispatchBurst(colonIndex,(Array*) qp, (const Array*) rdata.getDataPointer(), outDimsInt,srcDimsInt, indx, L, fieldNames().size(),advance); break; } Free(indx); Dimensions q(dimensions()); q.simplify(); dp->setDimensions(q); } catch (Exception &e) { Free(indx); throw e; } } /** * This is the vector version of the multidimensional cell-replacement function. * This is for content-based indexing (curly brackets). Two points that make * this function different than replaceData are * 1. If the index is larger than the size, we resize to a vector of sufficient * length. * 2. Deletions do not occur. */ void Array::setVectorContentsAsList(Array& index, ArrayVector& rdata, Interpreter* m_eval) { if (sparse()) throw Exception("setVectorContentsAsList not supported for sparse arrays."); promoteType(FM_CELL_ARRAY); if (isColonOperator(index)) { if (getLength() > rdata.size()) throw Exception("Not enough right hand side values to satisy left hand side expression."); Array *qp = (Array*) getReadWriteDataPointer(); for (int i=0;isetDimensions(q); return; } index.toOrdinalType(m_eval); if (rdata.size() < index.getLength()) throw Exception("Not enough right hand side values to satisy left hand side expression."); // Get the maximum index indexType max_index = index.getMaxAsIndex(); // Resize us as necessary. vectorResize(max_index); // Get a pointer to the dataset Array *qp = (Array*) getReadWriteDataPointer(); // Get a pointer to the index data set constIndexPtr index_p = (constIndexPtr) index.data(); // Get the length of the index object int index_length = index.getLength(); // Copy in the data for (int i=0;isetDimensions(q); } /** * This is the multidimensional cell-replacement function. * This is for content-based indexing (curly brackets). */ void Array::setNDimContentsAsList(ArrayVector& index, ArrayVector& rdata, Interpreter* m_eval) { if (sparse()) throw Exception("setNDimContentsAsList not supported for sparse arrays."); promoteType(FM_CELL_ARRAY); Dimensions myDims(dimensions()); Dimensions outDims; bool anyEmpty; int colonIndex; // Convert the indexing variables into an ordinal type. constIndexPtr* indx = ProcessNDimIndexes(false,myDims,index,anyEmpty,colonIndex,outDims,false,m_eval); int L = index.size(); try { Dimensions a(L); // First, we compute the maximum along each dimension. // We also get pointers to each of the index pointers. int i; for (i=0;isetDimensions(q); } catch (Exception &e) { Free(indx); throw e; } } /** * Set the contents of a field in a structure. */ void Array::setFieldAsList(std::string fieldName, ArrayVector& rdata) { Array *rp = NULL; if (isEmpty()) { rvstring names(fieldNames()); names.push_back(fieldName); promoteType(FM_STRUCT_ARRAY,names); Dimensions a(1,1); resize(a); // setData(FM_STRUCT_ARRAY,dimensions(),NULL,names); // return; } if (sparse()) throw Exception("setFieldAsList not supported for sparse arrays."); if (dataClass() != FM_STRUCT_ARRAY) throw Exception("Cannot apply A.field_name = B to non struct-array object A."); if (rdata.size() < getLength()) throw Exception("Not enough right hand values to satisfy left hand side expression."); int indexLength = getLength(); int field_ndx = getFieldIndex(fieldName); if (field_ndx == -1) field_ndx = insertFieldName(fieldName); int fieldCount = fieldNames().size(); Array *qp = (Array*) getReadWriteDataPointer(); for (int i=0;isetDimensions(q); } /** * Add another fieldname to our structure array. */ int Array::insertFieldName(std::string fieldName) { if (sparse()) throw Exception("insertFieldName not supported for sparse arrays."); rvstring names(fieldNames()); names.push_back(fieldName); const Array* qp = (const Array*) data(); Array *rp = (Array*) allocateArray(dataClass(),getLength(),names); int fN = names.size(); for (int i=0;isetSparse(false); return; } setData(dataClass(),dimensions(), makeDenseArray(dataClass(), dimensions().get(0), dimensions().get(1), data()), false, fieldNames(), className()); } /** * Delete a subset of a variable. */ void Array::deleteNDimSubset(ArrayVector& args, Interpreter* m_eval) { int singletonReferences = 0; int singletonDimension = 0; int i; Array qp; bool *indxCovered = NULL; bool *deletionMap = NULL; void *cp = NULL; if (isEmpty()) return; try { // Our strategy is as follows. To make the deletion, we need // one piece of information: the dimension to delete. // To do so, we first make a pass through the set of arguments, // checking each one to see if it "covers" its index set. // // However, to simplify the testing of // conditions later on, we must make sure that the length of // the index list matches our number of dimensions. We extend // it using 1 references, and throw an exception if there are // more indices than our dimension set. for (i=0;i 1) throw Exception("Statement A(...) = [] can only contain one non-colon index.\n"); if (singletonReferences == 0) singletonDimension = -1; // If we got this far, the user either entered an expression like // A(:,:,...,:,s,:,...,:) = [], or something numerically equivalent, // or the user entered something like A(:,...,:) = []. // In the latter case (indicated by singletonReferences = 0), we simply // delete the entire variable, and make it an empty type. // In the former case, we will have more work to do... if (singletonReferences != 0) { // We have to rescan our (now-identified) singleton // dimension to build a deletion map. The map is // marked true for each plane we wish to delete. // The map is the size of the _data_'s dimension. int M = dimensions().get(singletonDimension); deletionMap = args[singletonDimension].getBinaryMap(M); // We can now calculate the new size of the variable in the singletonDimension // by counting the number of "false" entries in deletionMap. int newSize = 0; for (i=0;i int32 DoCountNNZReal(const void* dp, int len) { int32 accum = 0; const T* cp = (const T*) dp; for (int i=0;i int32 DoCountNNZComplex(const void* dp, int len) { int32 accum = 0; const T* cp = (const T*) dp; for (int i=0;i(data(),getLength()); case FM_INT8: return DoCountNNZReal(data(),getLength()); case FM_UINT8: case FM_STRING: return DoCountNNZReal(data(),getLength()); case FM_INT16: return DoCountNNZReal(data(),getLength()); case FM_UINT16: return DoCountNNZReal(data(),getLength()); case FM_INT32: return DoCountNNZReal(data(),getLength()); case FM_UINT32: return DoCountNNZReal(data(),getLength()); case FM_INT64: return DoCountNNZReal(data(),getLength()); case FM_UINT64: return DoCountNNZReal(data(),getLength()); case FM_FLOAT: return DoCountNNZReal(data(),getLength()); case FM_DOUBLE: return DoCountNNZReal(data(),getLength()); case FM_COMPLEX: return DoCountNNZComplex(data(),getLength()); case FM_DCOMPLEX: return DoCountNNZComplex(data(),getLength()); case FM_CELL_ARRAY: return DoCountNNZReal(data(),getLength()); case FM_STRUCT_ARRAY: return DoCountNNZReal(data(),getLength()*fieldNames().size()); case FM_FUNCPTR_ARRAY: return DoCountNNZReal(data(),getLength()); } } bool Array::anyNotFinite() { if (sparse()) return SparseAnyNotFinite(dataClass(), getDimensionLength(1), data()); switch(dataClass()) { case FM_FLOAT: { const float *sp = (const float *) data(); int len = getLength(); int i=0; while (i0) return a.front(); else return Array::emptyConstructor(); } double ArrayToDouble(const Array& a) { Array b(a); return b.getContentsAsDoubleScalar(); } uint32 TypeSize(Class cls) { switch (cls) { case FM_LOGICAL: return sizeof(logical); case FM_UINT8: case FM_INT8: return sizeof(int8); case FM_UINT16: case FM_INT16: return sizeof(int16); case FM_UINT32: case FM_INT32: return sizeof(int32); case FM_UINT64: case FM_INT64: return sizeof(int64); case FM_FLOAT: return sizeof(float); case FM_DOUBLE: return sizeof(double); } throw Exception("Unsupported class as argument to TypeSize"); } string operator+(string a, int d) { char buf[256]; sprintf(buf,"%d",d); return a+string(buf); } string operator+(int d, string a) { char buf[256]; sprintf(buf,"%d",d); return a+string(buf); } stringVector operator+(stringVector a, stringVector b) { for (int i=0;i p) { Array ret(Array::uint32VectorConstructor(p.size())); for (int i=0;i