LTI-Lib latest version v1.9 - last update 10 Apr 2010

ltiTensor.h

00001 /*
00002  * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006
00003  * Lehrstuhl fuer Technische Informatik, RWTH-Aachen, Germany
00004  *
00005  * This file is part of the LTI-Computer Vision Library (LTI-Lib)
00006  *
00007  * The LTI-Lib is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License (LGPL)
00009  * as published by the Free Software Foundation; either version 2.1 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * The LTI-Lib is distributed in the hope that it will be
00013  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
00014  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with the LTI-Lib; see the file LICENSE.  If
00019  * not, write to the Free Software Foundation, Inc., 59 Temple Place -
00020  * Suite 330, Boston, MA 02111-1307, USA.
00021  */
00022 
00023 
00024 /*----------------------------------------------------------------
00025  * project ....: LTI Digital Image/Signal Processing Library
00026  * file .......: ltiTensor.h
00027  * authors ....: Pablo Alvarado
00028  * organization: LTI, RWTH Aachen
00029  * creation ...: 29.06.00
00030  * revisions ..: $Id: ltiTensor.h,v 1.6 2006/02/08 12:48:16 ltilib Exp $
00031  */
00032 
00033 #ifndef _LTI_TENSOR_H_
00034 #define _LTI_TENSOR_H_
00035 
00036 #include "ltiVector.h"
00037 #include "ltiMatrix.h"
00038 
00039 namespace lti {
00040   /**
00041    *  Tensor template class.
00042    *
00043    * The lti::tensor template container class allows the generation
00044    * multidimensional tables.  If you only need first or second order
00045    * tensors, please use the lti::vector and lti::matrix instead (they
00046    * are more efficient!).
00047    *
00048    * The elements of the tensor will be indexed in each dimension \e i
00049    * between 0 an \f$n_i-1\f$.
00050    *
00051    *  Example:
00052    *  \code
00053    *    lti::tensor<float> hist(3,10); // creates a 3rd order tensor with 10
00054    *                                   // elements pro dimension.  The total
00055    *                                   // number of elements is 10^3=1000
00056    *  \endcode
00057    *
00058    * To read the content of a tensor element use the access operator
00059    * at().
00060    *
00061    * For tensors with order 0, the number of elements per dimension will be
00062    * ignored.
00063    *
00064    * @ingroup gAggregate
00065    */
00066   template<class T>
00067   class tensor : public mathObject {
00068   public:
00069     /**
00070      *  The iterator is equivalent to a lti::vector<T>::iterator
00071      */
00072     typedef typename vector<T>::iterator iterator;
00073 
00074     /**
00075      *  The const_iterator is equivalent to a lti::vector<T>::const_iterator
00076      */
00077     typedef typename vector<T>::const_iterator const_iterator;
00078 
00079     /**
00080      * default constructor creates an empty tensor.
00081      */
00082     tensor();
00083 
00084     /**
00085      * create a tensor of the given order.
00086      *
00087      * Each "dimension" will have the given number of elements, i.e. the tensor
00088      * will have \f$elements^{dimensions}\f$ elements.
00089      *
00090      * For tensors with order 0, the number of elements per dimension will be
00091      * ignored.
00092      *
00093      * @param order the order of the tensor.
00094      * @param elements the number of elements per dimension.
00095      */
00096     tensor(const int& order,const int& elements);
00097 
00098     /**
00099      * create a tensor of the given order.
00100      *
00101      * Each "dimension" \e i will have the number of elements indicated in
00102      * the \e i-th element of the vector <code>elements</code>.
00103      *
00104      * If the order differs from the size of the given vector, the
00105      * number of elements for the dimension \e i will be given by
00106      * \f$dim_i = elements[i \, mod \, elements.size()]\f$.
00107      *
00108      * This means, if you want a 6th-order tensor, and your elements-vector
00109      * has only three elements [10,15,5], the number of elements per dimension
00110      * will be [10,15,5,10,15,5]
00111      *
00112      * For tensors with order 0, the number of elements per dimension will be
00113      * ignored.
00114      *
00115      * @param order the order of the tensor
00116      * @param elements a vector with the number of elements per dimension
00117      */
00118     tensor(const int& order,const ivector& elements);
00119 
00120     /**
00121      * create this tensor as a copy of another tensor
00122      * @param other the tensor to be copied.
00123      */
00124     tensor(const tensor<T>& other);
00125 
00126     /**
00127      * destructor
00128      */
00129     virtual ~tensor();
00130 
00131     /**
00132      * returns the name of this class: "tensor"
00133      */
00134     const char* getTypeName() const {return "tensor";};
00135 
00136     /**
00137      * returns the order of this tensor
00138      */
00139     inline int order() const;
00140 
00141     /**
00142      * get the number of elements of the dimension <code>dim</code>
00143      * @param dimension the index of the dimension to be checked
00144      * @return the number of elements of the dimension specified by
00145      *         <code>dim</code>
00146      */
00147     inline const int& elementsInDimension(const int& dimension) const;
00148 
00149     /**
00150      * get the number of elements per dimension
00151      */
00152     inline const ivector& elementsPerDimension() const;
00153 
00154     /**
00155      * returns a vector with the index of the first element of the tensor
00156      * (usually every element of the vector is 0)
00157      */
00158     inline const ivector& getFirstIndex() const;
00159 
00160     /**
00161      * returns a vector with the index to the last element of the tensor
00162      * (usually is the value returned by elementsPerDimension() minus (1,1,..1)
00163      */
00164     inline const ivector& getLastIndex() const;
00165 
00166     /**
00167      * returns an iterator pointing to the first element.
00168      * Note that you can not change the values of the tensor
00169      * elements when you use a const_iterator. See also begin()
00170      */
00171     inline const_iterator begin() const {
00172       return theElements.begin();
00173     };
00174 
00175     /**
00176      * returns an iterator pointing to the first element of the tensor.
00177      * The use of the interators is similar to the iterators of the
00178      * Standard Template Library (STL).
00179      *
00180      * If you need to iterate on all elements of the tensor, you can
00181      * use following code:
00182      * \code
00183      *   int tmp,accu;                      // a temporal variable
00184      *   lti::tensor<float> myTensor(3,10); // a 3rd order tensor with
00185      *                                      // 10 elements/dim. (of type float)
00186      *   lti::tensor<float>::iterator it;   // an iterator
00187      *
00188      *   for (it=myTensor.begin();it!=myTensor.end();it++) {
00189      *     tmp = *it;                       // tmp has value of element pointed
00190      *                                      // by the iterator.
00191      *     accu += tmp;
00192      *     (*it) = accu;                    // change the value in the tensor.
00193      *   }
00194      * \endcode
00195      *
00196      * Please note that if you define <code>it</code> as a const_iterator,
00197      * you can not make something like <code>*it=accu</code>.
00198      */
00199     inline iterator begin() {
00200       return theElements.begin();
00201     }
00202 
00203     /**
00204      * returns last index as a const iterator.
00205      * For an example see begin()
00206      */
00207     inline const_iterator end() const {
00208       return theElements.end();
00209     }
00210 
00211     /**
00212      * returns last index as an iterator
00213      * For an example see begin()
00214      */
00215     inline iterator end() {
00216       return theElements.end();
00217     };
00218 
00219     /**
00220      * change the order and number of elements per dimension of the
00221      * tensor.  All data will be lost!
00222      *
00223      * For tensors with order 0, the number of elements per dimension will be
00224      * ignored.
00225      *
00226      * @param order the new order of the tensor
00227      * @param elements the number of elements per dimension
00228      * @param initValue value to be copied at each element of the tensor
00229      *        if the initData parameter is true
00230      * @param copyData copy the old data into the new tensor (WARNING: Not
00231      *        implemented yet! (i.e. only false is correctly interpreted))
00232      * @param initData initialize all data in the tensor with the value
00233      *        specified in initValue.
00234      */
00235     void resize(const int& order,
00236                 const int& elements,
00237                 const T& initValue = T(),
00238                 const bool& copyData = false,
00239                 const bool& initData = true);
00240 
00241     /**
00242      * change the order and number of elements per dimension of the
00243      * tensor.  All data will be lost!
00244      *
00245      * For tensors with order 0, the number of elements per dimension will be
00246      * ignored.
00247      *
00248      * @param order the new order of the tensor
00249      * @param elements the number of elements per dimension
00250      * @param initValue value to be copied at each element of the tensor
00251      *        if the initData parameter is true
00252      * @param copyData copy the old data into the new tensor (WARNING: Not
00253      *        implemented yet! (i.e. only false is correctly interpreted)
00254      * @param initData initialize all data in the tensor with the value
00255      *        specified in initValue.
00256      */
00257     void resize(const int& order,
00258                 const ivector& elements,
00259                 const T& initValue = T(),
00260                 const bool& copyData = false,
00261                 const bool& initData = true);
00262 
00263     /**
00264      * transfer the data of this object into the receiver, leaving this
00265      * vector empty and the receiver as if a copy were done.
00266      *
00267      * This function makes a "memory block transfusion" to another
00268      * tensor. It is a very efficient way to make a copy of this
00269      * tensor, if you don't need the source data anymore!
00270      *
00271      * At the end of the detachment, this tensor will be empty.
00272      *
00273      * @param receiver the tensor that will receive the data
00274      */
00275     void detach(tensor<T>& receiver);
00276 
00277     /**
00278      * equivalent to resize(-1,0);
00279      */
00280     void clear();
00281 
00282     /**
00283      * initialize all elements of the tensor with 0 (or another specified
00284      * number).
00285      */
00286     void fill(const T& value = T(0));
00287 
00288     /**
00289      * fills the tensor elements with <code>iniValue</code> between
00290      * the n-dimensional points <code>from</code> and <code>to</code>.
00291      * @param iniValue the elements will be initialized with this
00292      *                 value.
00293      * @param from     first element index
00294      * @param to       last element index
00295      *
00296      * If <code>from</code> or <code>to</code> are out of bounds,
00297      * they will be (internaly) adjusted to a correct value.
00298      *
00299      * Example:
00300      * \code
00301      *   lti::tensor<float> t(1,10);    // first order tensor with 10 elements
00302      *   t.clear();
00303      *   t.fill(9.0f,ivector(1,1),ivector(1,3)); // hist=[0,9,9,9,0,0,0,0,0,0]
00304      * \endcode
00305      */
00306     void fill(const T& iniValue,
00307               const ivector& from,
00308               const ivector& to);
00309 
00310     /**
00311      * read-only access to the element x of the tensor
00312      * @param x index of the tensor element to be accessed.  It should
00313      *          be between getFirstIndex() and getLastIndex()
00314      * @return a read-only reference to the element at index <code>x</code>.
00315      */
00316     const T& at(const ivector& x) const;
00317 
00318     /**
00319      * access element x of the tensor
00320      * @param x index of the tensor element to be accessed.  It should
00321      *          be between getFirstIndex() and getLastIndex()
00322      * @return a reference to the element at index <code>x</code>.
00323      */
00324     T& at(const ivector& x);
00325 
00326     /**
00327      * overload of the at() operator for zero-order tensors.
00328      *
00329      * This method is provided as a shortcut to avoid creating an empty vector
00330      * to access the zero-order tensor.  If used with tensors of orders other
00331      * than zero, an assertion will be thrown (in debug mode) or an undefined
00332      * behaviour must be expected (in release mode).
00333      */
00334     T& at();
00335 
00336     /**
00337      * overload of the at() operator for zero-order tensors.
00338      *
00339      * This method is provided as a shortcut to avoid creating an empty vector
00340      * to access the zero-order tensor.  If used with tensors of orders other
00341      * than zero, an assertion will be thrown (in debug mode) or an undefined
00342      * behaviour must be expected (in release mode).
00343      */
00344     const T& at() const;
00345 
00346     /**
00347      * overload of the at() operator for first-order tensors.
00348      *
00349      * This method is provided as a shortcut to avoid creating a vector
00350      * with only one element to access the first-order tensor.  If used
00351      * with tensors of orders other than one, an assertion will be
00352      * thrown (in debug mode) or an undefined behaviour must be
00353      * expected (in release mode).
00354      *
00355      * @param x equivalent to the first element of an index vector.
00356      */
00357     T& at(const int& x);
00358 
00359     /**
00360      * overload of the at() operator for first-order tensors.
00361      *
00362      * This method is provided as a shortcut to avoid creating a vector
00363      * with only one element to access the first-order tensor.  If used
00364      * with tensors of orders other than one, an assertion will be
00365      * thrown (in debug mode) or an undefined behaviour must be
00366      * expected (in release mode).
00367      *
00368      * @param x equivalent to the first element of an index vector.
00369      */
00370     const T& at(const int& x) const;
00371 
00372     /**
00373      * overload of the at() operator for second-order tensors.
00374      *
00375      * This method is provided as a shortcut to avoid creating a vector
00376      * with only two elements to access the second-order tensor.  If used
00377      * with tensors of orders other than two, an assertion will be
00378      * thrown (in debug mode) or an undefined behaviour must be
00379      * expected (in release mode).
00380      *
00381      * @param x equivalent to the first element of an index vector.
00382      * @param y equivalent to the second element of an index vector.
00383      */
00384     T& at(const int& x,const int& y);
00385 
00386     /**
00387      * overload of the at() operator for second-order tensors.
00388      *
00389      * This method is provided as a shortcut to avoid creating a vector
00390      * with only two elements to access the second-order tensor.  If used
00391      * with tensors of orders other than two, an assertion will be
00392      * thrown (in debug mode) or an undefined behaviour must be
00393      * expected (in release mode).
00394      *
00395      * @param x equivalent to the first element of an index vector.
00396      * @param y equivalent to the second element of an index vector.
00397      */
00398     const T& at(const int& x,const int& y) const;
00399 
00400     /**
00401      * overload of the at() operator for third-order tensors.
00402      *
00403      * This method is provided as a shortcut to avoid creating a vector
00404      * with only three elements to access the third-order tensor.  If used
00405      * with tensors of orders other than three, an assertion will be
00406      * thrown (in debug mode) or an undefined behaviour must be
00407      * expected (in release mode).
00408      *
00409      * @param x equivalent to the first element of an index vector.
00410      * @param y equivalent to the second element of an index vector.
00411      * @param z equivalent to the third element of an index vector.
00412      */
00413     T& at(const int& x,const int& y,const int& z);
00414 
00415     /**
00416      * overload of the at() operator for third-order tensors.
00417      *
00418      * This method is provided as a shortcut to avoid creating a vector
00419      * with only three elements to access the third-order tensor.  If used
00420      * with tensors of orders other than three, an assertion will be
00421      * thrown (in debug mode) or an undefined behaviour must be
00422      * expected (in release mode).
00423      *
00424      * @param x equivalent to the first element of an index vector.
00425      * @param y equivalent to the second element of an index vector.
00426      * @param z equivalent to the third element of an index vector.
00427      */
00428     const T& at(const int& x,const int& y,const int& z) const;
00429 
00430     /**
00431      * assigment operator.
00432      * copy the contents of <code>other</code> in this %object.
00433      * @param other the source tensor to be copied.
00434      * @return a reference to this object
00435      */
00436     tensor<T>& copy(const tensor<T>& other);
00437 
00438     /**
00439      * copy the <code>other</code> tensor by casting each of its elements
00440      * @param other The tensor to be casted
00441      */
00442     template<class U>
00443     tensor<T>& castFrom(const tensor<U>& other) {
00444       resize(other.dimensions(), other.elementsPerDimension());
00445 
00446       typename tensor<U>::const_iterator otherIt = other.begin();
00447       typename tensor<U>::const_iterator endIt = other.end();
00448       iterator thisIt = begin();
00449 
00450       for(;otherIt != endIt;++otherIt,++thisIt) {
00451         *thisIt = static_cast<T>(*otherIt);
00452       }
00453 
00454       return (*this);
00455     };
00456 
00457     /**
00458      * create a clone of this tensor
00459      * @return a pointer to a copy of this tensor
00460      */
00461     virtual mathObject* clone() const;
00462 
00463     /**
00464      * compare this tensor with another one.
00465      *
00466      * @param other the other tensor to be compared with
00467      * @return true if both tensors have the same elements and same size
00468      */
00469     bool equals(const tensor<T>& other) const;
00470 
00471     /** compare this tensor with other
00472      *
00473      * @param other the other tensor to be compared with
00474      * @return true if both tensors have the same elements and same size
00475      */
00476     inline bool operator==(const tensor<T>& other) const {
00477       return equals(other);
00478     };
00479 
00480     /**
00481      * compare this tensor with another one, and use the given tolerance to
00482      * determine if the value of each element of the other tensor
00483      * approximately equals the values of the actual tensor elements.
00484      *
00485      * An element \e x is approximately equal to another element \e y
00486      * with a tolerance \e t, if following equation holds:
00487      * <i>x</i>-t < <i>y</i> < <i>x</i>+t
00488      *
00489      * @param other the other tensor to be compared with
00490      * @param tolerance the tolerance to be used
00491      *
00492      * @return true if both tensors are approximatly equal
00493      */
00494     bool prettyCloseTo(const tensor<T>& other,
00495                        const T& tolerance) const;
00496 
00497     /**
00498      * assigment operator (alias for copy(other)).
00499      * @param other the tensor to be copied
00500      * @return a reference to the actual tensor
00501      */
00502     inline tensor<T>& operator=(const tensor<T>& other) {return copy(other);};
00503 
00504     /**
00505      * applies a C-function to each element of the tensor.
00506      * @param function a pointer to a C-function
00507      * @return a reference to the actual tensor
00508      */
00509     tensor<T>& apply(T (*function)(T));
00510 
00511     /**
00512      * applies a C-function to each element of the tensor.
00513      * @param function a pointer to a C-function
00514      * @return a reference to the actual tensor
00515      */
00516     tensor<T>& apply(T (*function)(const T&));
00517 
00518     /**
00519      * Elementwise multiplication.
00520      * Each element of this tensor will be multiplied with the elements
00521      * of the other tensor and the result will be left in this %object!
00522      *
00523      * @param other the other tensor to be multiplied with
00524      * @return a reference to the actual tensor
00525      */
00526     tensor<T>& emultiply(const tensor<T>& other);
00527 
00528     /**
00529      * Elementwise multiplication.
00530      * This tensor will contain the elementwise multiplication of the
00531      * elements in <code>first</code> and <code>second</code>.
00532      *
00533      * @param first the first tensor
00534      * @param second the second tensor will be multiplied with the
00535      *               first tensor
00536      * @return a reference to the actual tensor
00537      */
00538     tensor<T>& emultiply(const tensor<T>& first,
00539                          const tensor<T>& second);
00540 
00541     /**
00542      * Add another tensor of the same type and same order and
00543      * leave the result in this %object.
00544      *
00545      * @param other the other tensor to be added with
00546      * @return a reference to the actual tensor
00547      */
00548     tensor<T>& add(const tensor<T>& other);
00549 
00550     /**
00551      * Add two tensor and leave the result in this %object.
00552      *
00553      * @param first the first tensor.
00554      * @param second the second tensor will be added with the first
00555      *               tensor
00556      * @return a reference to the actual tensor
00557      */
00558     tensor<T>& add(const tensor<T>& first,const tensor<T>& second);
00559 
00560     /**
00561      * Alias for add(const tensor& other)
00562      */
00563     inline tensor<T>& operator+=(const tensor<T>& other) {
00564       return add(other);
00565     };
00566 
00567     /**
00568      * Subtracts another tensor of the same type and same order
00569      * and leaves the result in this %object
00570      *
00571      * @param other will be substracted from this tensor
00572      * @return a reference to the actual tensor
00573      */
00574     tensor<T>& subtract(const tensor<T>& other);
00575 
00576     /**
00577      * Subtracts two tensors and leaves the result in this %object.
00578      * @param first the first tensor
00579      * @param second the second tensor will be substracted from the
00580      *                   first tensor
00581      * @return a reference to the actual tensor
00582      */
00583     tensor<T>& subtract(const tensor<T>& first,
00584                         const tensor<T>& second);
00585 
00586     /**
00587      * Alias for substract(const tensor& other)
00588      */
00589     inline tensor<T>& operator-=(const tensor<T>& other) {
00590       return subtract(other);
00591     };
00592 
00593     /**
00594      * Multiply this tensor with a constant.
00595      * Returns this tensor.
00596      *
00597      * @param cst constant scalar to be multiplied with
00598      * @return a reference to the actual tensor
00599      */
00600     tensor<T>& multiply(const T& cst);
00601 
00602     /**
00603      * Multiply the other %tensor with a constant and leave the result here.
00604      * Returns a reference to this tensor.
00605      *
00606      * @param other the other tensor to be multiplied with the
00607      *              constant value
00608      * @param cst constant scalar to be multiplied with the other tensor.
00609      * @return a reference to the actual tensor
00610      */
00611     tensor<T>& multiply(const tensor<T>& other,const T& cst);
00612 
00613     /**
00614      * alias for multiply(const T& cst)
00615      * @param cst constant scalar to be multiplied with
00616      * @return a reference to the actual tensor
00617      */
00618     inline tensor<T>& operator*=(const T& cst) {
00619       return multiply(cst);
00620     };
00621 
00622     /**
00623      * Divide this tensor with a constant.
00624      * Returns this tensor.
00625      *
00626      * @param cst the elements of the tensor will be divided with this
00627      *            constant
00628      * @return a reference to the actual tensor
00629      */
00630     tensor<T>& divide(const T& cst);
00631 
00632     /**
00633      * Divide the other tensor with a constant and leave the result here.
00634      * Returns a reference to this tensor.
00635      *
00636      * @param other the tensor to be divide by the constant value
00637      * @param cst the elements of the tensor will be divided with this
00638      *            constant
00639      * @return a reference to the actual tensor
00640     */
00641     tensor<T>& divide(const tensor<T>& other,const T& cst);
00642 
00643     /**
00644      * Add constant to this tensor.  This tensor is changed.
00645      * Returns this tensor.
00646      * @param cst constant scala to be added with each element
00647      * @return a reference to the actual tensor
00648      */
00649     tensor<T>& add(const T& cst);
00650 
00651     /**
00652      * Alias for add(const T& cst)
00653      */
00654     tensor<T>& operator+=(const T& cst) {
00655       return add(cst);
00656     }
00657 
00658     /**
00659      * Add constant to the other tensor and leave the result here.
00660      * Returns a reference to this tensor.
00661      * @param other the oder tensor
00662      * @param cst constant scala to be added with each element of the other
00663      *            tensor
00664      * @return a reference to the actual tensor
00665      */
00666     tensor<T>& add(const tensor<T>& other,const T& cst);
00667 
00668     /**
00669      * write the object in the given ioHandler
00670      */
00671     virtual bool write(ioHandler& handler,const bool complete = true) const;
00672 
00673     /**
00674      * read the object from the given ioHandler
00675      */
00676     virtual bool read(ioHandler& handler,const bool complete = true);
00677 
00678   protected:
00679     /**
00680      * the total number of elements
00681      */
00682     int totalNumberOfElements;
00683 
00684     /**
00685      * the dimensionality of this tensor
00686      */
00687     int theOrder;
00688 
00689     /**
00690      * the data of the tensor
00691      */
00692     vector<T> theElements;
00693 
00694     /**
00695      * number of elements
00696      */
00697     ivector theElementsPerDimension;
00698 
00699     /**
00700      * a vector with the right dimension initialized with 0
00701      */
00702     ivector firstIndex;
00703 
00704     /**
00705      * a vector with the right dimension initialized with the
00706      * number of elements - 1 per dimension
00707      */
00708     ivector lastIndex;
00709 
00710   };
00711 
00712   /** @name Storable interface
00713    *  Members for the storable interface
00714    */
00715   //@{
00716   /**
00717    * read the matrix from the given ioHandler.  The complete flag indicates
00718    * if the enclosing begin and end should be also be read
00719    */
00720   template <class T>
00721   bool read(ioHandler& handler,tensor<T>& hist,const bool complete=true) {
00722     return hist.read(handler,complete);
00723   } // immediate implementation to avoid MSVC++ bug!!
00724 
00725   /**
00726    * write the matrix in the given ioHandler.  The complete flag indicates
00727    * if the enclosing begin and end should be also be written or not
00728    */
00729   template <class T>
00730   bool write(ioHandler& handler,
00731              const tensor<T>& hist,
00732              const bool complete=true) {
00733     return hist.write(handler,complete);
00734   } // immediate implementation to avoid MSVC++ bug!!
00735   //@}
00736 
00737 }
00738 
00739 namespace std {
00740   /// outputs the elements of the tensor on a stream
00741   template <class T>
00742     ostream& operator<<(ostream& s,const lti::tensor<T>& v);
00743 }
00744 
00745 #include "ltiTensor_inline.h"
00746 
00747 
00748 #endif

Generated on Sat Apr 10 15:26:17 2010 for LTI-Lib by Doxygen 1.6.1