libidx
/home/rex/ebltrunk/core/libidx/include/idxIO.hpp
00001 /***************************************************************************
00002  *   Copyright (C) 2008 by Pierre Sermanet *
00003  *   pierre.sermanet@gmail.com   *
00004  *   All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Redistribution under a license not approved by the Open Source
00014  *       Initiative (http://www.opensource.org) must display the
00015  *       following acknowledgement in all advertising material:
00016  *        This product includes software developed at the Courant
00017  *        Institute of Mathematical Sciences (http://cims.nyu.edu).
00018  *     * The names of the authors may not be used to endorse or promote products
00019  *       derived from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
00022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00023  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00024  * DISCLAIMED. IN NO EVENT SHALL ThE AUTHORS BE LIABLE FOR ANY
00025  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00026  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00028  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00029  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00030  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031  ***************************************************************************/
00032 
00033 #ifndef IDXIO_HPP_
00034 #define IDXIO_HPP_
00035 
00036 #include "idxops.h"
00037 
00038 namespace ebl {
00039 
00041   // helper functions
00042 
00043   // template functions returning the magic number associated with a 
00044   // particular type
00045   template <class T> inline int get_magic() {
00046     eblerror("matrix type not implemented."); return 0; }
00047   template <> inline int get_magic<ubyte>()       {return MAGIC_BYTE_MATRIX; }
00048   template <> inline int get_magic<int>()         {return MAGIC_INTEGER_MATRIX;}
00049   template <> inline int get_magic<float>()       {return MAGIC_FLOAT_MATRIX; }
00050   template <> inline int get_magic<double>()      {return MAGIC_DOUBLE_MATRIX; }
00051   template <> inline int get_magic<long>()        {return MAGIC_LONG_MATRIX; }
00052   template <> inline int get_magic<uint>()        {return MAGIC_UINT_MATRIX; }
00053   template <> inline int get_magic<uint64>()      {return MAGIC_UINT64_MATRIX; }
00054 
00055   // Pascal Vincent type
00056   template <class T> inline int get_magic_vincent() {
00057     eblerror("matrix type not implemented"); return 0; }
00058   template <> inline int get_magic_vincent<ubyte>(){return MAGIC_UBYTE_VINCENT;}
00059   template <> inline int get_magic_vincent<int>()  {return MAGIC_INT_VINCENT;}
00060   template <> inline int get_magic_vincent<float>(){return MAGIC_FLOAT_VINCENT;}
00061   template <> inline int get_magic_vincent<double>() {
00062     return MAGIC_DOUBLE_VINCENT; }
00063   template <> inline int get_magic_vincent<long>() {return 0x0000; }
00064 
00065   // type to string function for debug message.
00066   inline std::string get_magic_str(int magic) {
00067     switch (magic) {
00068       // standard format
00069     case MAGIC_BYTE_MATRIX:     return "ubyte";
00070     case MAGIC_PACKED_MATRIX:   return "packed";
00071     case MAGIC_SHORT_MATRIX:    return "short";
00072     case MAGIC_SHORT8_MATRIX:   return "short8";
00073     case MAGIC_ASCII_MATRIX:    return "ascii";
00074     case MAGIC_INTEGER_MATRIX:  return "int";
00075     case MAGIC_FLOAT_MATRIX:    return "float";
00076     case MAGIC_DOUBLE_MATRIX:   return "double";
00077     case MAGIC_LONG_MATRIX:     return "long";
00078       // non standard
00079     case MAGIC_UINT_MATRIX:     return "uint";
00080     case MAGIC_UINT64_MATRIX:   return "uint64";
00081     case MAGIC_INT64_MATRIX:    return "int64";
00082       // pascal vincent format
00083     case MAGIC_BYTE_VINCENT:    return "byte (pascal vincent)";
00084     case MAGIC_UBYTE_VINCENT:   return "ubyte (pascal vincent)";
00085     case MAGIC_SHORT_VINCENT:   return "short (pascal vincent)";
00086     case MAGIC_INT_VINCENT:     return "int (pascal vincent)";
00087     case MAGIC_FLOAT_VINCENT:   return "float (pascal vincent)";
00088     case MAGIC_DOUBLE_VINCENT:  return "double (pascal vincent)";
00089     default: 
00090       string s;
00091       s << "unknown type (magic: " << reinterpret_cast<void*>(magic) << ")";
00092       return s;
00093     }
00094   }
00095 
00105   template <class T> inline void reverse_n(T *ptr, int n) {
00106     char *mptr = (char *) ptr;
00107     while(n--)
00108       {
00109         char tmp;
00110         char *uptr = mptr + sizeof (T);
00111         if (sizeof (T) >= 2)
00112           { tmp = mptr[0]; mptr[0]=uptr[-1]; uptr[-1]=tmp; }
00113         if (sizeof (T) >= 4)
00114           { tmp = mptr[1]; mptr[1]=uptr[-2]; uptr[-2]=tmp; }
00115         if (sizeof (T) >= 6)
00116           { tmp = mptr[2]; mptr[2]=uptr[-3]; uptr[-3]=tmp; }
00117         if (sizeof (T) >= 8)
00118           { tmp = mptr[3]; mptr[3]=uptr[-4]; uptr[-4]=tmp; }
00119         mptr = uptr;
00120       }
00121   }
00122 
00123   template <class T> inline T endian(T ptr) {
00124     T v = ptr;
00125     // endianess test
00126     int endiantest = 1;
00127     if (*(char*)&endiantest) reverse_n(&v, 1);
00128     return v;
00129   }
00130 
00131   template <typename T, typename T2>
00132   void read_cast_matrix(FILE *fp, idx<T2> &out) {
00133     idx<T> m(out.get_idxdim());
00134     read_matrix_body(fp, m);
00135     idx_copy(m, out);
00136   }
00137 
00138   template <typename T>
00139   void read_matrix_body(FILE *fp, idx<T> &m) {
00140     int res;
00141     idx_aloop1(i, m, T) {
00142       res = fread(&(*i), sizeof (T), 1, fp);
00143     }
00144     res = 0; // just to avoid warning
00145   }
00146   
00148   // loading
00149 
00150   template <typename T> idx<T> load_matrix(const std::string &filename) {
00151     return load_matrix<T>(filename.c_str());
00152   }
00153 
00154   template <typename T> idx<T> load_matrix(const char *filename) {
00155     // open file
00156     FILE *fp = fopen(filename, "rb");
00157     if (!fp)
00158       eblthrow("load_matrix failed to open " << filename);
00159     // read it
00160     idx<T> m;
00161     try {
00162       m = load_matrix<T>(fp);
00163       fclose(fp);
00164     } catch(eblexception &e) {
00165       e << " while loading " << filename;
00166       throw e;
00167     }
00168     return m;
00169   }
00170 
00171   template <typename T>
00172   void load_matrix(idx<T>& m, const std::string &filename) {
00173     load_matrix(m, filename.c_str());
00174   }
00175 
00176   template <typename T> void load_matrix(idx<T>& m, const char *filename) {
00177     // open file
00178     FILE *fp = fopen(filename, "rb");
00179     if (!fp)
00180       eblthrow("load_matrix failed to open " << filename);
00181     // read it
00182     m = load_matrix<T>(fp, &m);
00183     fclose(fp);
00184   }
00185 
00186   template <typename T>
00187   idx<T> load_matrix(FILE *fp, idx<T> *out_) {
00188     int magic;
00189     idxdim dims = read_matrix_header(fp, magic);
00190     idx<T> out;
00191     idx<T> *pout = &out;
00192     if (!out_) // if no input matrix, allocate new one
00193       out = idx<T>(dims);
00194     else // otherwise use given one
00195       pout = out_;
00196 
00197     // resize out if necessary
00198     if (pout->get_idxdim() != dims) { // different order/dimensions
00199       // if order is different, it's from the input matrix, error
00200       if (pout->order() != dims.order())
00201         eblerror("error: different orders: " << *pout << " " << dims);
00202       // resize output idx
00203       pout->resize(dims);
00204     }
00206     if ((magic == get_magic<T>()) || (magic == get_magic_vincent<T>())) {
00207       // read
00208       read_matrix_body(fp, *pout);
00209     } else { // different type, read original type, then copy/cast into out
00210       switch (magic) {
00211       case MAGIC_BYTE_MATRIX:
00212       case MAGIC_UBYTE_VINCENT:
00213         read_cast_matrix<ubyte>(fp, *pout);
00214         break ;
00215       case MAGIC_INTEGER_MATRIX:
00216       case MAGIC_INT_VINCENT:
00217         read_cast_matrix<int>(fp, *pout);
00218         break ;
00219       case MAGIC_FLOAT_MATRIX:
00220       case MAGIC_FLOAT_VINCENT:
00221         read_cast_matrix<float>(fp, *pout);
00222         break ;
00223       case MAGIC_DOUBLE_MATRIX:
00224       case MAGIC_DOUBLE_VINCENT:
00225         read_cast_matrix<double>(fp, *pout);
00226         break ;
00227       case MAGIC_LONG_MATRIX:
00228         read_cast_matrix<long>(fp, *pout);
00229         break ;
00230       case MAGIC_UINT_MATRIX:
00231         read_cast_matrix<uint>(fp, *pout);
00232         break ;
00233       case MAGIC_UINT64_MATRIX:
00234         read_cast_matrix<uint64>(fp, *pout);
00235         break ;
00236       case MAGIC_INT64_MATRIX:
00237         read_cast_matrix<int64>(fp, *pout);
00238         break ;
00239       default:
00240         eblerror("unknown magic number");
00241       }
00242     }
00243     return *pout;
00244   }
00245 
00246   template <typename T>
00247   midx<T> load_matrices(const std::string &filename, bool ondemand){
00248     // open file
00249     file *f = new file(filename.c_str(), "rb");
00250     FILE *fp = f->get_fp();
00251     // first read offsets matrix
00252     idx<int64> p = load_matrix<int64>(fp);
00253     // switch loading mode
00254     if (ondemand) { // do not load data now
00255       fseek(fp, 0, SEEK_SET); // reset fp to beginning
00256       midx<T> all(p.get_idxdim(), f, &p);
00257       return all;
00258     } else { // load all data now
00259       cout << "Loading the whole dataset into memory" <<endl;
00260       // read all present matrices
00261       midx<T> all(p.dim(0));
00262       for (uint i = 0; i < p.dim(0); ++i) {
00263         if (p.get(i) > 0) { // matrix is present, get it
00264           if (fseek(fp, p.get(i), SEEK_SET)) {
00265             fseek(fp, 0, SEEK_END);
00266             fpos_t fppos;
00267             fgetpos(fp, &fppos);
00268 #if defined(__WINDOWS__) || defined(__MAC__)
00269             eblerror("fseek to position " << p.get(i) << " failed, "
00270                      << "file is " << (intg) fppos << " big");
00271 #else
00272             eblerror("fseek to position " << p.get(i) << " failed, "
00273                      << "file is " << (intg) fppos.__pos << " big");
00274 #endif
00275           }
00276           // move fp to matrix beginning
00277           idx<T> m = load_matrix<T>(fp);
00278           all.set(m, i);
00279         }
00280       }
00281       delete f;
00282       return all;
00283     }
00284   }
00285   
00287   // saving
00288   
00289   template <typename T>
00290   bool save_matrix(idx<T>& m, const std::string &filename) {
00291     return save_matrix(m, filename.c_str());
00292   }
00293 
00294   template <typename T2, typename T>
00295   bool save_matrix(idx<T>& m, const std::string &filename) {
00296     idx<T2> m2(m.get_idxdim());
00297     idx_copy(m, m2);
00298     return save_matrix(m2, filename.c_str());
00299   }
00300 
00301   // TODO: intg support
00302   template <typename T> bool save_matrix(idx<T>& m, const char *filename) {
00303     FILE *fp = fopen(filename, "wb");
00304     if (!fp) {
00305       cerr << "save_matrix failed (" << filename << "): ";
00306       perror("");
00307       return false;
00308     }
00309     bool ret = save_matrix(m, fp);
00310     if (!ret)
00311       cerr << "failed to write matrix " << m << " to " << filename << "."
00312            << endl;
00313     fclose(fp);
00314     return ret;
00315   }
00316   
00317   // TODO: intg support
00318   template <typename T> bool save_matrix(idx<T>& m, FILE *fp) {
00319     int v, i;
00320     // write header
00321     v = get_magic<T>();
00322     if (fwrite(&v, sizeof (int), 1, fp) != 1) return false;
00323     v = m.order();
00324     if (fwrite(&v, sizeof (int), 1, fp) != 1) return false;
00325     for (i = 0; (i < m.order()) || (i < 3); ++i) {
00326       if (i < m.order())
00327         v = m.dim(i);
00328       else
00329         v = 1;
00330       if (fwrite(&v, sizeof (int), 1, fp) != 1) return false;
00331     }
00332     // write body
00333     int res;
00334     idx_aloop1(k, m, T) 
00335       res = fwrite(&(*k), sizeof (T), 1, fp);
00336     res = 0; // just to avoid compilation warning
00337     return true;
00338   }
00339 
00340   template <typename T>
00341   bool save_matrices(midx<T>& m, const std::string &filename) {
00342     FILE *fp = fopen(filename.c_str(), "wb+");
00343     if (!fp) {
00344       cerr << "save_matrix failed (" << filename << "): ";
00345       perror("");
00346       return false;
00347     }
00348     // allocate offset matrix
00349     idx<int64> offsets(m.get_idxdim());
00350     idx_clear(offsets);
00351     // save offsets matrix first    
00352     bool ret = save_matrix(offsets, fp);
00353     if (!ret) {
00354       cerr << "failed to write matrix " << offsets << " to " << filename << "."
00355            << endl;
00356       fclose(fp);
00357       return false;
00358     }
00359     // then save all matrices contained in m
00360     fpos_t pos;
00361     // TODO: implement generic looping for midx
00362     if (m.order() == 1) {
00363       for (uint i = 0; i < m.dim(0); ++i) {
00364         if (m.exists(i)) {
00365           // save offset of matrix
00366           fgetpos(fp, &pos);
00367 #if defined(__WINDOWS__) || defined(__MAC__)
00368           offsets.set((int64) pos, i);
00369 #else
00370           offsets.set((int64) pos.__pos, i);
00371 #endif
00372           // save matrix to file
00373           idx<T> e = m.get(i);
00374           ret = save_matrix(e, fp);
00375           if (!ret) {
00376             cerr << "failed to write matrix " << e << " to " << filename << "."
00377                  << endl;
00378             fclose(fp);
00379             return false;
00380           }
00381         }
00382       }
00383     } else if (m.order() == 2) {
00384       for (uint i = 0; i < m.dim(0); ++i) {
00385         for (uint j = 0; j < m.dim(1); ++j) {
00386           if (m.exists(i, j)) {
00387             // save offset of matrix
00388             fgetpos(fp, &pos);
00389 #if defined(__WINDOWS__) || defined(__MAC__)
00390             offsets.set((int64) pos, i, j);
00391 #else
00392             offsets.set((int64) pos.__pos, i, j);
00393 #endif
00394             // save matrix to file
00395             idx<T> e = m.get(i, j);
00396             ret = save_matrix(e, fp);
00397             if (!ret) {
00398               cerr << "failed to write matrix " << e << " to "
00399                    << filename << "." << endl;
00400               fclose(fp);
00401               return false;
00402             }
00403           }
00404         }
00405       }
00406     }
00407     fclose(fp);
00408     // finally rewrite offset matrix at beginning of file
00409     fp = fopen(filename.c_str(), "rb+");
00410     if (!fp) {
00411       cerr << "save_matrix failed (" << filename << "): ";
00412       perror("");
00413       return false;
00414     }
00415     ret = save_matrix(offsets, fp);
00416     if (!ret) {
00417       cerr << "failed to write matrix " << offsets << " to " << filename << "."
00418            << endl;
00419       fclose(fp);
00420       return false;
00421     }    
00422     fclose(fp);
00423     return ret;
00424   }
00425 
00426   template <typename T>
00427   bool save_matrices_individually(midx<T>& m, const std::string &root,
00428                                   bool print) {
00429     intg id = 0;
00430     // TODO: implement generic looping for midx
00431     if (m.order() == 1) {
00432       for (uint i = 0; i < m.dim(0); ++i) {
00433         if (m.exists(i)) {
00434           // save matrix to file
00435           string fname; fname << root << "_" << id++ << ".mat";
00436           idx<T> e = m.get(i);
00437           if (!save_matrix(e, fname)) return false;
00438           if (print) std::cout << "saved " << fname << std::endl;
00439         }
00440       }
00441     } else if (m.order() == 2) {
00442       for (uint i = 0; i < m.dim(0); ++i) {
00443         for (uint j = 0; j < m.dim(1); ++j) {
00444           if (m.exists(i, j)) {
00445             // save matrix to file
00446             string fname; fname << root << "_" << id++ << ".mat";
00447             idx<T> e = m.get(i, j);
00448             if (!save_matrix(e, fname)) return false;
00449             if (print) std::cout << "saved " << fname << std::endl;
00450           }
00451         }
00452       }
00453     }
00454     return true;
00455   }
00456   
00457   template <typename T>
00458   bool save_matrices(midx<T>& m1, midx<T> &m2, const std::string &filename) {
00459     FILE *fp = fopen(filename.c_str(), "wb+");
00460     if (!fp) {
00461       cerr << "save_matrix failed (" << filename << "): ";
00462       perror("");
00463       return false;
00464     }
00465     // target dimensions
00466     idxdim d1 = m1, d2 = m2, dd1 = m1, dd2 = m2;
00467     dd1.remove_dim(0); dd2.remove_dim(0);
00468     if (dd1 != dd2)
00469       eblerror("expected same dimensions but got " << dd1 << " and " << dd2);
00470     idxdim dall(d1);
00471     dall.setdim(0, d1.dim(0) + d2.dim(0));
00472     // allocate offset matrix
00473     idx<int64> offsets(dall);
00474     idx_clear(offsets);
00475     // save offsets matrix first    
00476     bool ret = save_matrix(offsets, fp);
00477     if (!ret) {
00478       cerr << "failed to write matrix " << offsets << " to " << filename << "."
00479            << endl;
00480       fclose(fp);
00481       return false;
00482     }
00483     // then save all matrices contained in m
00484     fpos_t pos;
00485     // TODO: implement generic looping for midx
00486     midx<T> *m = &m1;
00487     if (dall.order() == 1) {
00488       for (uint i = 0, k = 0; i < dall.dim(0); ++i, ++k) {
00489         // switch to second matrix
00490         if (i == m1.dim(0)) {
00491           m = &m2;
00492           k = 0; // reset iterator
00493         }
00494         if (m->exists(k)) {
00495           // save offset of matrix
00496           fgetpos(fp, &pos);
00497 #if defined(__WINDOWS__) || defined(__MAC__)
00498           offsets.set((int64) pos, i);
00499 #else
00500           offsets.set((int64) pos.__pos, i);
00501 #endif
00502           // save matrix to file
00503           idx<T> e = m->get(k);
00504           ret = save_matrix(e, fp);
00505           if (!ret) {
00506             cerr << "failed to write matrix " << e << " to " << filename << "."
00507                  << endl;
00508             fclose(fp);
00509             return false;
00510           }
00511         }
00512       }
00513     } else if (m->order() == 2) {
00514       for (uint i = 0, k = 0; i < dall.dim(0); ++i, ++k) {
00515         // switch to second matrix
00516         if (i == m1.dim(0)) {
00517           m = &m2;
00518           k = 0; // reset iterator
00519         }
00520         for (uint j = 0; j < m->dim(1); ++j) {
00521           if (m->exists(k, j)) {
00522             // save offset of matrix
00523             fgetpos(fp, &pos);
00524 #if defined(__WINDOWS__) || defined(__MAC__)
00525             offsets.set((int64) pos, i, j);
00526 #else
00527             offsets.set((int64) pos.__pos, i, j);
00528 #endif
00529             // save matrix to file
00530             idx<T> e = m->get(k, j);
00531             ret = save_matrix(e, fp);
00532             if (!ret) {
00533               cerr << "failed to write matrix " << e << " to "
00534                    << filename << "." << endl;
00535               fclose(fp);
00536               return false;
00537             }
00538           }
00539         }
00540       }
00541     }
00542     fclose(fp);
00543     // finally rewrite offset matrix at beginning of file
00544     fp = fopen(filename.c_str(), "rb+");
00545     if (!fp) {
00546       cerr << "save_matrix failed (" << filename << "): ";
00547       perror("");
00548       return false;
00549     }
00550     ret = save_matrix(offsets, fp);
00551     if (!ret) {
00552       cerr << "failed to write matrix " << offsets << " to " << filename << "."
00553            << endl;
00554       fclose(fp);
00555       return false;
00556     }    
00557     fclose(fp);
00558     return ret;
00559   }
00560   
00561   template <typename T>
00562   bool save_matrices(std::list<std::string> &filenames,
00563                      const std::string &filename) {
00564     if (filenames.size() == 0) eblerror("expected a non-empty list");
00565     FILE *fp = fopen(filename.c_str(), "wb+");
00566     if (!fp) {
00567       cerr << "save_matrix failed (" << filename << "): ";
00568       perror("");
00569       return false;
00570     }
00571     // determine matrices dims
00572     idxdim alld(filenames.size());
00573     std::string s = filenames.front();
00574     bool multi = has_multiple_matrices(s.c_str());
00575     if (multi) {
00576       midx<T> e = load_matrices<T>(s);
00577       idxdim d = e.get_idxdim();
00578       for (uint i = 0; i < d.order(); ++i)
00579         alld.insert_dim(d.order(), d.dim(i));
00580     }
00581     // allocate offsets matrix
00582     idx<int64> offsets(alld);
00583     idx_clear(offsets);
00584     // put this matrix first in the file
00585     bool ret = save_matrix(offsets, fp);
00586     if (!ret) {
00587       cerr << "failed to write matrix " << offsets << " to " << filename << "."
00588            << endl;
00589       fclose(fp);
00590       return false;
00591     }
00592     // then save all matrices
00593     fpos_t pos;
00594     intg j = 0;
00595     for (std::list<std::string>::iterator i = filenames.begin();
00596          i != filenames.end(); ++i, ++j) {
00597       if (multi) { // each matrix is composed of multiple matrices
00598         midx<T> e = load_matrices<T>(*i, false);
00599         for (uint k = 0; k < e.dim(0); ++k) {
00600           // save offset of matrix
00601           fgetpos(fp, &pos);
00602 #if defined(__WINDOWS__) || defined(__MAC__)
00603           offsets.set((int64) pos, j, k);
00604 #else
00605           offsets.set((int64) pos.__pos, j, k);
00606 #endif
00607           // save matrix into file
00608           idx<T> ee = e.get(k);
00609           ret = save_matrix(ee, fp);
00610           if (!ret) {
00611             cerr << "failed to write matrix " << k << " to " << filename << "."
00612                  << endl;
00613             fclose(fp);
00614             return false;
00615           }
00616         }
00617       } else {
00618         idx<T> e = load_matrix<T>(*i);
00619         // save offset of matrix
00620         fgetpos(fp, &pos);
00621 #if defined(__WINDOWS__) || defined(__MAC__)
00622         offsets.set((int64) pos, j);
00623 #else
00624         offsets.set((int64) pos.__pos, j);
00625 #endif
00626         // save matrix into file
00627         ret = save_matrix(e, fp);
00628         if (!ret) {
00629           cerr << "failed to write matrix " << e << " to " << filename << "."
00630                << endl;
00631           fclose(fp);
00632           return false;
00633         }
00634       }
00635     }
00636     fclose(fp);
00637     // finally rewrite offset matrix at beginning of file
00638     fp = fopen(filename.c_str(), "rb+");
00639     if (!fp) {
00640       cerr << "save_matrix failed (" << filename << "): ";
00641       perror("");
00642       return false;
00643     }
00644     ret = save_matrix(offsets, fp);
00645     if (!ret) {
00646       cerr << "failed to write matrix " << offsets << " to " << filename << "."
00647            << endl;
00648       fclose(fp);
00649       return false;
00650     }
00651     fclose(fp);
00652     return ret;
00653   }
00654   
00655   template <typename T>
00656   bool save_matrix(std::list<std::string> &filenames,
00657                    const std::string &filename) {
00658     if (filenames.size() == 0) eblerror("expected a non-empty list");
00659     FILE *fp = fopen(filename.c_str(), "wb+");
00660     if (!fp) {
00661       cerr << "save_matrix failed (" << filename << "): ";
00662       perror("");
00663       return false;
00664     }
00665     // determine matrices dims
00666     idxdim alld(filenames.size());
00667     std::string s = filenames.front();
00668     bool multi = has_multiple_matrices(s.c_str());
00669     if (multi)
00670       eblerror("expected a single-matrix file in " << s.c_str());
00671     idx<T> e = load_matrix<T>(s);
00672     idxdim d = e.get_idxdim();
00673     for (uint i = 0; i < d.order(); ++i)
00674       alld.insert_dim(alld.order(), d.dim(i));
00675     // allocate matrix
00676     idx<T> all(alld);
00677     idx_clear(all);
00678     // add all matrices
00679     intg j = 0;
00680     for (std::list<std::string>::iterator i = filenames.begin();
00681          i != filenames.end(); ++i, ++j) {
00682       if (multi)
00683         eblerror("expected a single-matrix file in " << s.c_str());      
00684       idx<T> e = all.select(0, j);
00685       load_matrix<T>(e, *i);
00686     }
00687     // save matrix
00688     return save_matrix(all, filename);
00689   }
00690   
00691 } // end namespace ebl
00692 
00693 #endif /* IDXIO_HPP_ */