// -*- C++ -*- 
// 
// xntuple.cc 
// N. Regnault   -      98-99 
// Extended NTuples 
// 
#include <string.h>
#include "sopnamsp.h"
#include "ppersist.h"
#include "perrors.h"
#include "xntuple.h"
#ifdef OS_MACOSX
#include <unistd.h>
#endif

#define LENNAME 31 
#define MAXLEN 128
#define BADVAL -1.e19

/*!
   \class SOPHYA::XNTuple
   \ingroup HiStats
   NTuple class (a Table or 2-D data set) with the possibility of having
   columns with int, float or string type content. In addition, this class
   can handle large data sets, using swap space on disk.

   \sa SOPHYA::BaseDataTable SOPHYA::DataTable SOPHYA::SwPPFDataTable

   \deprecated 
   This class is being deprecated. Use classes derived from SOPHYA::BaseDataTable 
   whenever possible


   \code
   #include "xntuple.h"
   // ...
   char * names[4] = {"X", "X2", "XInt","XStr"};
   // XNTuple (Table) creation with 4 columns, of integer, 
   // double(2) and string type
   XNTuple  xnt(2,0,1,1, names);
   // Filling the NTuple
   r_8 xd[2];
   int_4 xi[2];
   char xss[2][32];
   char * xs[2] = {xss[0], xss[1]} ;
   for(int i=0; i<50; i++) {
     xi[0] = i;  xd[0] = i+0.5;  xd[1] = xd[0]*xd[0];
     sprintf(xs[0],"X=%g", xd[0]);
     xnt.Fill(xd, NULL, xi, xs);
   }
   // Printing table info
   cout << xnt ;
   // Saving object into a PPF file
   POutPersist po("xnt.ppf");
   po << xnt ;
   \endcode
   
*/

//++
// Class	XNTuple
// Lib		Outils++ 
// include	xntuple.h
//
//	Classe de ntuples
//--
//++
// Links	Parents
// PPersist
// NTupleInterface
//--
//++
// Links	Voir aussi
// NTuple
//--


char* XNTuple::glob_swp = NULL ; 
long int  XNTuple::NbNT = 0 ; 

#ifdef __MWERKS__
// mktemp() non connu sur Mac (?)  - Dominique Yvon / R. Ansari
char * mktemp(char * Filename);
#endif

NTBlk::NTBlk(int ndvar, int nfvar, int nivar, int nsvar, int strsz, int sz) 
    : sw(0), // pas swappe 
      swoff(-1), 
      ddata(NULL), fdata(NULL), 
      idata(NULL), sdata(NULL)
{
    if(ndvar) ddata = new r_8[ndvar*sz] ; 
    if(nfvar) fdata = new r_4[nfvar*sz] ; 
    if(nivar) idata = new int_4[nivar*sz] ; 
    if(nsvar) sdata = new char[nsvar*(strsz+1)*sz] ; 
}

NTBlk::~NTBlk()
{
    free() ; 
}

void NTBlk::free() 
{
    if(ddata) delete[] ddata ; ddata = NULL ; 
    if(fdata) delete[] fdata ; fdata = NULL ; 
    if(idata) delete[] idata ; idata = NULL ; 
    if(sdata) delete[] sdata ; sdata = NULL ; 
}

// 
// relit bloc ds fichier swap 
// (seulement si bloc n'est plus en memoire) 
// 
void XNTuple::read_blk(NTBlk* blk) const 
{
    // deja en memoire ? 
    if( !blk->sw ) return ;  
    
    blk->free() ; 
    
    // On va --> blk 
    fseek(swf, blk->swoff, SEEK_SET) ; 
    
    if(mD) { 
	blk->ddata = new r_8[mD*mBlkSz] ; 
	fread(blk->ddata, sizeof(r_8), mD*mBlkSz, swf) ; 
    }
    if(mF) { 
	blk->fdata = new r_4[mF*mBlkSz] ; 
	fread(blk->fdata, sizeof(r_4), mF*mBlkSz, swf) ; 
    }
    if(mI) { 
	blk->idata = new int_4[mI*mBlkSz] ; 
	fread(blk->idata, sizeof(int_4), mI*mBlkSz, swf) ; 
    }
    if(mS) { 
	blk->sdata = new char[mS*(mStrSz+1)*mBlkSz] ; 
	fread(blk->sdata, sizeof(char), mS*mBlkSz*(mStrSz+1), swf) ; 
    }
}

// 
// swappe le bloc present en memoire 
// depuis le + longtemps 
// 
void XNTuple::swap() const 
{
    // fichier de swap ouvert ? 
    if(!swf) { 
	swf_name = new char[swp.length()+64] ; 
	strncpy(swf_name, swp.c_str(), swp.length()+1) ; 
	strcat(swf_name, "NTupleXXXXXX") ; 
	//	swf_name = strdup("NTupleXXXXXX") ; 
	mktemp(swf_name) ; 
	swf = fopen(swf_name, "w+") ; 
	if(!swf) throw IOExc("XNTuple::swap() - Error opening swap File");
    }
    
    // bloc a swapper 
    NTBlk* blk = sw.front() ; 
    
    // bloc dans fichiers de swap ? 
    //    if( blk->swoff > 0 ) 
    write_blk(blk) ; 
    blk->free() ; 
    
    blk->sw = 1 ; // on marque le bloc swappe 
    sw.pop_front() ; // on le vire de la liste de swap 
    mNSwBlk++ ; 
}


// 
// unswap() 
// 
void XNTuple::get_blk(int i) const 
{
    if(i<0 || i>=mNBlk) return ; 
    read_blk(ptr[i]) ; 
    ptr[i]->sw = 0 ; 
    sw.push_back(ptr[i]) ; 
    mNSwBlk-- ; 
}


// 
// ecrit le bloc en bout de fichier 
// (seulement si swoff < 0 ) 
// 
void XNTuple::write_blk(NTBlk* blk) const 
{
    // deja swappe ? 
    if( blk->swoff >= 0 ) return ; 
    fseek(swf, 0, SEEK_END) ; 
    // position debut du bloc 
    blk->swoff = ftell(swf) ; 
    if(blk->ddata) fwrite(blk->ddata, sizeof(r_8), mD*mBlkSz, swf) ; 
    if(blk->fdata) fwrite(blk->fdata, sizeof(r_4),  mF*mBlkSz, swf) ; 
    if(blk->idata) fwrite(blk->idata, sizeof(int_4),  mI*mBlkSz, swf) ; 
    if(blk->sdata) fwrite(blk->sdata, sizeof(char),   mS*mBlkSz*(mStrSz+1), swf) ; 
}


void  XNTuple::add_blk() 
{
    // l'ancien bloc en ecriture peut maintenant 
    // etre swappe (s'il existe) 
    if(mBlk>=0) sw.push_back(ptr[mBlk]) ; 
    
    // si pas de place, on swappe 
    if( mNBlk+1 >= mMaxBlk ) swap() ; 
    
    // nouveau bloc 
    NTBlk* tmp = new NTBlk(mD, mF, mI, mS, mStrSz, mBlkSz) ; 
    ptr.push_back(tmp) ; 
    
    //    mNBlk++ ; 
    //    mOff = 0 ; 
    //    mBlk++ ; 
}


XNTuple::XNTuple()
    : mNEnt(0), mNBlk(0), mNSwBlk(0), 
      mBlkSz(0), mBlk(-1), mOff(0), 
      mMaxBlk(0), mStrSz(0), 
      mD(0), mF(0), mI(0), mS(0), 
      mVarD(NULL), mMin(NULL), mMax(NULL), 
      mNVars(0), mNames(NULL), 
  swf(NULL), swf_name(NULL), mInfo(NULL)
{
    if(!glob_swp) SetSwapPath() ; 
    swp = glob_swp ; 
    NbNT++ ; 
}

//++
// Titre	Constructeurs
//--
//++
// XNTuple(int ndvar, int nfvar, int nivar, int nsvar, char** vnames, -
//	   int blk=512, int maxblk=100, int strsz=30)
//	Constructeur - Création d'un XNTuple de "ndvar" variables de type "r_8", -
//                      "nfvar" de type "r_4", "nivar" de type "int" et "nsvar" de type -
//                      "char*". 
//|            * "blk"    = taille blocs de données (en nombre d'entrées) 
//|            * "maxblk" = nombre max de blocs présents en mémoire (non swappés) 
//|            * "strsz"  = taille des données de type char* 
// XNTuple(XNTuple const& nt)
//	Constructeur de copie.
// XNTuple(string const& flnm)
//	Constructeur - lecture à partir d'un fichier PPersist.
//--

//! Constructor with specification colum types and names
/*!
  \param ndvar : Number of columns with type double (r_8)
  \param nfvar : Number of columns with type float (r_4)
  \param nivar : Number of columns with type integer (int_4)
  \param nsvar : Number of columns with type string
  \param vnames : Column names (in the order r_8 r_4 int_4 string)
  (colum names are limited to 31 characters)
  \param blk : data block size (in number of table lines)
  \param maxblk : Maximum number of data block in memory
  \param strsz : string length (for string type columns)
 */
XNTuple::XNTuple(int ndvar, int nfvar, int nivar, int nsvar, 
		   char** vnames, 
		   int blk, int maxblk, int strsz)
    : mNEnt(0), mNBlk(0), mNSwBlk(0), 
      mBlkSz(blk), mBlk(-1), mOff(0), 
      mMaxBlk(maxblk), mStrSz(strsz), 
      mD(ndvar), mF(nfvar), mI(nivar), mS(nsvar), 
      mVarD(NULL), mMin(NULL), mMax(NULL), 
      mNVars(ndvar+nfvar+nivar+nsvar), mNames(NULL), 
      swf(NULL), swf_name(NULL), mInfo(NULL)
{
    if(!glob_swp) SetSwapPath() ; 
    swp = glob_swp ; 
    
    mVarD = new r_8[mNVars] ; 
    mMin  = new r_8[mD+mF+mI] ; 
    int i;
    for(i = 0 ; i < (mD+mF+mI) ; i++) mMin[i] =  9E19 ; 
    mMax  = new r_8[mD+mF+mI] ; 
    for(i = 0 ; i < (mD+mF+mI) ; i++) mMax[i] = -9E19 ; 
    
    if(mNVars) mNames = new char[mNVars*(LENNAME+1)] ; 
    memset(mNames, 0, mNVars*(LENNAME+1));
    for(i = 0 ; i < mNVars ; i++)
      strncpy(mNames+i*(LENNAME+1), vnames[i], LENNAME);

    NbNT++ ; 
}



//! Constructor with table initialized from a PPF file
XNTuple::XNTuple(string const& flnm) 
    : mNEnt(0), mNBlk(0), mNSwBlk(0), 
      mBlkSz(0), mBlk(-1), mOff(0), 
      mMaxBlk(0), mStrSz(0), 
      mD(0), mF(0), mI(0), mS(0), 
      mVarD(NULL), mMin(NULL), mMax(NULL), 
      mNVars(0), mNames(NULL), 
      swf(NULL), swf_name(NULL), mInfo(NULL)
{
    if(!glob_swp) SetSwapPath() ; 
    swp = glob_swp ; 
    
    PInPersist s(flnm);
    ObjFileIO<XNTuple> fiont(this);
    fiont.Read(s);
    NbNT++ ; 
}


//! Copy constructor - Copies the table definition and associated data 
XNTuple::XNTuple(XNTuple const& nt) 
    : mNEnt(0), mNBlk(0), mNSwBlk(0), 
      mBlkSz(0), mBlk(-1), mOff(0), 
      mMaxBlk(0), mStrSz(0), 
      mD(0), mF(0), mI(0), mS(0), 
      mVarD(NULL), mMin(NULL), mMax(NULL), 
      mNVars(0), mNames(NULL), 
      swf(NULL), swf_name(NULL), mInfo(NULL) 
{
    if(!glob_swp) SetSwapPath() ; 
    swp = glob_swp ; 
    Copy(nt) ; 
    NbNT++ ; 
}



XNTuple::~XNTuple()
{
    clean() ; 
    NbNT-- ; 
    if(NbNT==0) {delete[] glob_swp ; glob_swp = NULL;} 
}



//! Clear the data table definition and deletes the associated data
void XNTuple::clean()
{
    // On libere tous les blocs 
    for(int i = 0 ; i < mNBlk ; i++) 
	if(!ptr[i]->sw) delete ptr[i] ; 
    ptr.erase(ptr.begin(), ptr.end()) ; 
    sw.erase(sw.begin(), sw.end()) ; 
    
    // on ferme le fichier de swap 
    if(swf) { 
      fclose(swf) ; swf = NULL ; 
      remove(swf_name) ; 
      delete swf_name ; swf_name = NULL ; 
    }
    // tout le monde remis a 0 
    mNEnt   = 0 ;     mNBlk   = 0 ; 
    mNSwBlk = 0 ;     mBlkSz  = 0 ;  
    mBlk    = -1 ;    mOff    = 0 ; 
    mMaxBlk = 0 ;     mStrSz  = 0 ; 
    mD      = 0 ;     mF      = 0 ; 
    mI      = 0 ;     mS      = 0 ; 
    mNVars  = 0 ; 
    
    
    if(mVarD) { delete[] mVarD ; mVarD = NULL ; } 
    if(mMin)  { delete[] mMin  ; mMin  = NULL ; } 
    if(mMax)  { delete[] mMax  ; mMax  = NULL ; } 

    if(mNames) {delete[] mNames ; mNames = NULL ; }
    if (mInfo) delete mInfo;

}


//++
// Titre	Méthodes
//--
//++
//  void Fill(r_8* d_data, r_4* f_data, int* i_data, char** s_data) 
//	Remplissage d'une ligne dans le NTuple
//  void Show(ostream& os) const 
//	Impression de la liste des variables avec min-max sur le flot "os"
//  void Show() const 
//	Identique à "Show(cout)"
//  XNTuple&   operator = (XNTuple const& nt) 
//	Opérateur égal (=) , copie "nt" dans le premier NTuple
//--
//! Appends an entry (line) to the table
/*!
  \param d_data : double (r_8) line elements
  \param f_data : float (r_4) line elements
  \param i_data : integer (int_4) line elements
  \param s_data : string line elements
 */
void XNTuple::Fill(r_8* d_data, r_4* f_data, int_4* i_data, char** s_data) 
{
    // place disponible dans bloc courant ? 
    if( mOff==mBlkSz || mOff==0 ) {
	add_blk() ; mOff = 0 ; mNBlk++ ; mBlk++ ; 
    }
    
    if( mD && !ptr[mBlk]->ddata || 
	mF && !ptr[mBlk]->fdata || 
	mI && !ptr[mBlk]->idata || 
	mS && !ptr[mBlk]->sdata )
          throw ParmError("XNTuple::Fill(...) Missing (NULL) argument ");
    
    r_8 x ; 
    // copie variables et update mMin, mMax 
    if(mD) {
	memcpy(ptr[mBlk]->ddata+mOff*mD, d_data, mD*sizeof(r_8)) ; 
	for(int i = 0 ; i < mD ; i++) {
	    x = d_data[i] ; 
	    if(x<mMin[i]) mMin[i] = x ; 
	    if(x>mMax[i]) mMax[i] = x ; 
	}
    }
    
    if(mF) {
	memcpy(ptr[mBlk]->fdata+mOff*mF, f_data, mF*sizeof(r_4)) ; 
	for(int i = 0 ; i < mF ; i++) {
	    x = f_data[i] ; 
	    if(x<mMin[i+mD]) mMin[i+mD] = x ; 
	    if(x>mMax[i+mD]) mMax[i+mD] = x ; 
	}
    }

    if(mI) {
	memcpy(ptr[mBlk]->idata+mOff*mI, i_data, mI*sizeof(int_4)) ; 
	for(int i = 0 ; i < mI ; i++) {
	    x = i_data[i] ; 
	    if(x<mMin[i+mD+mF]) mMin[i+mD+mF] = x ; 
	    if(x>mMax[i+mD+mF]) mMax[i+mD+mF] = x ; 
	}
    }
    for(int i = 0 ; i < mS ; i++) 
	memcpy(ptr[mBlk]->sdata+(mOff*mS+i)*(mStrSz+1), 
	       s_data[i], (strlen(s_data[i])+1)*sizeof(char)) ; 
    mOff++ ; 
    mNEnt++ ; 
}

// 
// A quel index correspond mon nom ? 
// 
int XNTuple::IndexNom(char const* nom) const 
{
    int i ; 
    for(i = 0 ; i < (mD+mF+mI+mS) ; i++)
	if( !strncmp( mNames+i*(LENNAME+1), nom, LENNAME+1) ) 
	    return i ; 
    return -1 ; 
}

string XNTuple::NomIndex(int k) const 
{
    if( k<0 || k>=mNVars ) return "" ; 
    return mNames + k*(LENNAME+1) ; 
}


// 
// 
// 
r_8 XNTuple::GetDVal(int i, int k) const 
{
    if( i<0 || i>=mNEnt || k<0 || k>=mD ) 
        throw RangeCheckError("XNTuple::GetDVal() Invalid line/column index");
    
    // Bloc ? 
    int blk = (int)(i/mBlkSz) ; 
    int off = i%mBlkSz ; 
    
    // bloc swappe ? 
    if( ptr[blk]->sw ) { get_blk(blk) ; swap() ; } 
    return ptr[blk]->ddata[off*mD+k] ; 
}

// 
// 
// 
r_4 XNTuple::GetFVal(int i, int k) const 
{
    if( i<0 || i>=mNEnt || k<mD || k>=(mD+mF) ) 
        throw RangeCheckError("XNTuple::GetFVal() Invalid line/column index");
    k -= mD ; 
    
    // Bloc ? 
    int blk = (int)(i/mBlkSz) ; 
    int off = i%mBlkSz ; 
    
    // bloc swappe ? 
    if( ptr[blk]->sw ) { get_blk(blk) ; swap() ; } 
    return ptr[blk]->fdata[off*mF+k] ; 
}

// 
// 
// 
int_4 XNTuple::GetIVal(int i, int k) const 
{
    if( i<0 || i>=mNEnt || k<(mD+mF) || k>=(mD+mF+mI) )
        throw RangeCheckError("XNTuple::GetIVal() Invalid line/column index");

    k -= (mD+mF) ; 

    // Bloc ? 
    int blk = (int)(i/mBlkSz) ; 
    int off = i%mBlkSz ; 
    
    // bloc swappe ? 
    if( ptr[blk]->sw ) { get_blk(blk) ; swap() ; } 

    return ptr[blk]->idata[off*mI+k] ; 
}

// 
// 
// 
string  XNTuple::GetSVal(int i, int k) const 
{
    if( i<0 || i>=mNEnt || k<(mD+mF+mI) || k>=(mD+mF+mI+mS) )
        throw RangeCheckError("XNTuple::GetSVal() Invalid line/column index");

    k -= (mD+mF+mI) ; 
    
    // Bloc ? 
    int blk = (int)(i/mBlkSz) ; 
    int off = i%mBlkSz ; 
    
    // bloc swappe ? 
    if( ptr[blk]->sw ) { get_blk(blk) ; swap() ; } 
    
    // copie de la chaine 
    // string ret = strdup(ptr[blk]->sdata + (off*mS+k)*(mStrSz+1)) ; // $CHECK$ EA fuite de memoire
       // attention, strdup fait un malloc et on ne fait jamais de free...
       // a quoi sert ce strdup ?????????
    string ret = ptr[blk]->sdata + (off*mS+k)*(mStrSz+1) ; 
    //    return ptr[blk]->sdata[k] ; 
    return ret ; 
}


// 
// Copie bloc a bloc, avec meme parametres 
//  
void XNTuple::Copy(XNTuple const& nt) 
{
    clean() ; 
    // Parametres 
    mNEnt = nt.mNEnt ; 
    mBlkSz = nt.mBlkSz ; 
    mOff = nt.mOff ; 
    mMaxBlk = nt.mMaxBlk ; 
    mStrSz = nt.mStrSz ; 
    mD = nt.mD ; 
    mF = nt.mF ; 
    mI = nt.mI ; 
    mS = nt.mS ; 
    mNVars = nt.mNVars ; 
    
    // noms 
    if(mNVars) {
	mNames = new char[mNVars*(LENNAME+1)] ; 
        mVarD = new r_8[mNVars];
	memcpy(mNames, nt.mNames, mNVars*(LENNAME+1)*sizeof(char)) ; 
    }
    // MinMax 
    if(nt.mMin) { 
	mMin = new r_8[(mD+mF+mI)] ; 
	memcpy(mMin, nt.mMin, (mD+mF+mI)*sizeof(r_8)) ; 
    }
    if(nt.mMax) { 
	mMax = new r_8[(mD+mF+mI)] ; 
	memcpy(mMax, nt.mMax, (mD+mF+mI)*sizeof(r_8)) ; 
    }
    
    //dup blocs 
    mNBlk = 0 ; 
    mBlk  = -1 ; 
    for(int i = 0 ; i < nt.mNBlk ; i++) {
	add_blk() ; mBlk++ ; mNBlk++ ; 
	// si nt.ptr[i] swappe, on le relit 
	if(nt.ptr[i]->sw) nt.read_blk(nt.ptr[i]) ; 
	if(mD) 
	    memcpy(ptr[i]->ddata, nt.ptr[i]->ddata, mD*mBlkSz*sizeof(r_8)) ; 
	if(mF)
	    memcpy(ptr[i]->fdata, nt.ptr[i]->fdata, mF*mBlkSz*sizeof(r_4)) ; 
	if(mI)
	    memcpy(ptr[i]->idata, nt.ptr[i]->idata, mF*mBlkSz*sizeof(int_4)) ; 
	if(mS)
	    memcpy(ptr[i]->sdata, nt.ptr[i]->sdata, mS*mBlkSz*sizeof(char)*(mStrSz+1)) ; 
	if(nt.ptr[i]->sw) nt.ptr[i]->free() ; 
    }

    // DVList Info block 
    if(nt.mInfo!=NULL) {mInfo = new DVList; *mInfo = *(nt.mInfo);}

}

//++
// DVList& Info()
//	Renvoie une référence sur l'objet DVList Associé
//--

//! Returns the associated DVList object
DVList&  XNTuple::Info()
{
  if (mInfo == NULL)  mInfo = new DVList;
  return(*mInfo);
}

void XNTuple::Print(int num, int nmax) const 
{
    printf("XNTuple::Print() : \n") ; 
    printf("  Entrees = %d, Blocs  = %d, Bloc Size = %d\n", 
	   mNEnt, mNBlk, mBlkSz) ; 
    int i,j;
    printf("  D_Vars  = %d : ", mD) ; 
    for(i = 0 ; i < mD ; i++) 
	printf("%s ", NomIndex(i).c_str() ) ; 
    printf("\n") ; 
    
    printf("  F_Vars  = %d : ", mF) ; 
    for(i = 0 ; i < mF ; i++) 
	printf("%s ", NomIndex(i+mD).c_str() ) ; 
    printf("\n") ; 
    
    printf("  I_Vars  = %d : ", mI) ; 
    for(i = 0 ; i < mI ; i++) 
	printf("%s ", NomIndex(i+mD+mF).c_str() ) ; 
    printf("\n") ; 
    
    printf("  S_Vars  = %d : ", mS) ; 
    for(i = 0 ; i < mS ; i++) 
	printf("%s ", NomIndex(i+mD+mF+mI).c_str() ) ; 
    printf("\n") ; 
    
    for(i = num ; i < num+nmax ; i++) {
	for(j = 0 ; j < mD ; j++) printf("%f ", GetDVal(i,j)) ; 
	printf(" -- ") ; 
	for(j = 0 ; j < mF ; j++) printf("%f ", GetFVal(i,j+mD)) ; 
	printf(" -- ") ; 
	for(j = 0 ; j < mI ; j++) printf("%d ", GetIVal(i,j+mD+mF)) ; 
	printf(" -- ") ; 
	for(j = 0 ; j < mS ; j++) printf("%s ", GetSVal(i,j+mD+mF+mI).c_str()) ; 
	printf("\n") ; 
    }
}


//! Prints table definition and number of entries 
void   XNTuple::Show(ostream& os) const 
{
    os << "XNTuple: NVar= " << mNVars << " NEnt= " << mNEnt 
       << " (BlkSz,NBlk= " << mBlkSz << ", " << mNBlk << ")" 
       << " (mNSwBlk= " << mNSwBlk << ")" << endl ; 
    os << "(Sw File= " ; if(swf_name) os << swf_name ; os << ")" << endl ; 
    
    char* buff = new char[80] ; 
    sprintf(buff, "Variables   :  Type         Min         Max  \n") ; 
    os << buff ; 
    
    r_8 min, max ;
    int i; 
    for(i = 0 ; i < mD ; i++) {
	GetMinMax(i, min, max) ; 
	sprintf(buff, "  %-10s:   D    %12.6g   %12.6g \n", 
		NomIndex(i).c_str(), min, max) ; 
	os << buff ; 
    }
    for(i = 0 ; i < mF ; i++) {
	GetMinMax(i+mD, min, max) ; 
	sprintf(buff, "  %-10s:   F    %12.6g   %12.6g \n", 
		NomIndex(i+mD).c_str(), min, max) ; 
	os << buff ; 
    }
    for(i = 0 ; i < mI ; i++) {
	GetMinMax(i+mD+mF, min, max) ; 
	sprintf(buff, "  %-10s:   I    %12.6g   %12.6g \n", 
		NomIndex(i+mD+mF).c_str(), min, max) ; 
	os << buff ; 
    }
    for(i = 0 ; i < mS ; i++) {
	sprintf(buff, "  %-10s:   S      -----        ----- \n", 
		NomIndex(i+mD+mF+mI).c_str()) ; 
	os << buff ; 
    }
    delete[] buff ; 
}

//! NOT YET IMPLEMENTED ! - Fills table from an ascii file
int    XNTuple::FillFromASCIIFile(string const& fn, r_8 ddval,  r_4 dfval,
                                        int dival, const char * dsval)
//	Remplit le ntuple a partir d'un fichier ASCII.
//	Renvoie le nombre de lignes ajoutees.
{
// a faire
return(0);
}


//! Defines swap file path for XNTuple objects (Default=$TMPDIR or /tmp/)
void  XNTuple::SetSwapPath(char* p) 
{
    if (p == NULL) {
      p = getenv("TMPDIR");
      if (p == NULL) p = "/tmp/";
    }
    if(!glob_swp) glob_swp = new char[MAXLEN+2] ; 
    strncpy(glob_swp,p,MAXLEN);
    if(glob_swp[strlen(glob_swp)] != '/') strcat(glob_swp,"/");
}

// 
// 
// Interface NTuple 
// 
// 
sa_size_t XNTuple::NbLines() const
{
    return(NEntry());
}

sa_size_t XNTuple::NbColumns() const
{
    return(NVar());
}

r_8* XNTuple::GetLineD(sa_size_t n) const 
{
    // memcpy() impossible
    // il faut faire des GetVal 
    int i;
    for(i = 0 ; i < mD ; i++) 
	mVarD[i] = GetDVal(n, i) ; 
    for(i = 0 ; i < mF ; i++) 
	mVarD[i+mD] = (r_8)GetFVal(n, i+mD) ; 
    for(i = 0 ; i < mI ; i++) 
	mVarD[i+mD+mF] = (r_8)GetIVal(n, i+mD+mF) ; 
    for(i = 0 ; i < mS ; i++) 
	mVarD[i+mD+mF+mI] = atof(GetSVal(n, i+mD+mF+mI).c_str()) ; 
    return mVarD ; 
}


r_8  XNTuple::GetCell(sa_size_t n, sa_size_t k) const 
{
    if( k<0 || k>=(mD+mF+mI+mS) ) return BADVAL ; 
    if(k<mD) return GetDVal(n,k) ; 
    if(k<(mF+mD)) return GetFVal(n,k) ; 
    if(k<(mF+mD+mI)) return GetIVal(n,k) ; 
    if(k<(mD+mF+mI+mS)) return atof(GetSVal(n,k).c_str()) ; 
    return BADVAL ; 
}


r_8  XNTuple::GetCell(sa_size_t n, string const& nom) const 
{
    int k = IndexNom(nom.c_str()) ; 
    if(k<0) return BADVAL ; 
    if(k<mD) return GetDVal(n,k) ; 
    if(k<(mF+mD)) return GetFVal(n,k) ; 
    if(k<(mF+mD+mI)) return GetIVal(n,k) ; 
    if(k<(mD+mF+mI+mS)) return atof(GetSVal(n,k).c_str()) ; 
    return BADVAL ; 
}

string   XNTuple::GetCelltoString(sa_size_t n, sa_size_t k) const
{
    char buff[32];
    if(k<0) return (""); 
    else if(k<mD) 
      { sprintf(buff, "%g", GetDVal(n,k)); return(buff) ; } 
    else if(k<(mF+mD)) 
      { sprintf(buff, "%g", (double)GetFVal(n,k)); return(buff) ; } 
    else if(k<(mF+mD+mI)) 
      { sprintf(buff, "%d", GetIVal(n,k)); return(buff) ; } 
    else if(k<(mD+mF+mI+mS)) 
      return(GetSVal(n,k).c_str()) ; 
    return("");
}

void XNTuple::GetMinMax(sa_size_t k, double& min, double& max)  const
{
    min = 9E19 ; max = -9E19 ; 
    // variables string non traitees 
    if( k<0 || k>=(mD+mF+mI) ) return ; 
    
    min = mMin[k] ; 
    max = mMax[k] ; 
    return ; 
}


void XNTuple::GetMinMax(string const & nom, double& min, double& max)   const
{
    GetMinMax(IndexNom(nom.c_str()), min, max) ; 
}


sa_size_t XNTuple::ColumnIndex(string const& nom) const 
{
    return IndexNom(nom.c_str()) ; 
}


string XNTuple::ColumnName(sa_size_t k) const 
{
    return NomIndex(k) ; 
}


string XNTuple::VarList_C(const char* nomx) const 
{
    string rets = "" ; 
    rets += "/* r_8 type variables */ \n";
    // variables r_8 
    int i;
    for(i = 0 ; i < mD ; i++) {
	if( i>0 && (i%5)==0 ) rets += ";" ; 
	if( (i%5)==0 ) rets+="\ndouble " ; 
	else rets += ", " ; 
	rets += NomIndex(i) ; 
    }
    if(mD) rets += ";" ; 
    
    // variables r_4 
    rets += "/* r_4 type variables */ \n";
    for(i = 0 ; i < mF ; i++) {
	if( i>0 && (i%5)==0 ) rets += ";" ; 
	if( (i%5)==0 ) rets+="\ndouble " ; 
	else rets += ", " ; 
	rets += NomIndex(i+mD) ; 
    }
    if(mF) rets += ";" ; 
    
    // variables int 
    rets += "/* int type variables */ \n";
    for(i = 0 ; i < mI ; i++) {
	if( i>0 && (i%5)==0 ) rets += ";" ; 
	if( (i%5)==0 ) rets+="\ndouble " ; 
	else rets += ", " ; 
	rets += NomIndex(i+mD+mF) ; 
    }
    if(mI) rets += ";" ; 
    
    // variables string
    rets += "/* string type variables */ \n"; 
    for(i = 0 ; i < mS ; i++) {
	if( i>0 && (i%5)==0 ) rets += ";" ; 
	if( (i%5)==0 ) rets+="\ndouble " ; 
	else rets += ", " ; 
	rets += NomIndex(i+mD+mF+mI) ; 
    }
    if(mS) rets += "; \n" ; 

    if(nomx) {
	char buff[256] ; 
	for(i = 0 ; i < mNVars ; i++) {
	    sprintf(buff, "%s=%s[%d]; ", NomIndex(i).c_str(), nomx, i) ; 
	    rets+=buff ; 
	    if( (i%3 == 0) && (i>0) ) rets += "\n" ; 
	}
    }
    return rets ; 
}


string XNTuple::LineHeaderToString() const 
{
    char buff[32];
    string rets=" Num    ";
    for(int i=0; i<mNVars; i++) {
	sprintf(buff, "%8s ", NomIndex(i).c_str() );
	rets += buff;
    }
    rets += '\n';
    return(rets);

}


string XNTuple::LineToString(sa_size_t n) const 
{
    char buff[32];
    r_8* val;
    val = GetLineD(n);
    sprintf(buff,"%6d: ",n); 
    string rets=buff;
    int i;
    for(i=0; i<(mD+mF+mI); i++) {
	sprintf(buff, "%8.3g ", val[i]);
	rets += buff;
    }
    
    for(i = mD+mF+mI ; i < mNVars ; i++) {
	sprintf(buff, "%8s ", GetSVal(n,i).c_str() ) ; 
	rets += buff ; 
    }
    
    rets += '\n';
    return(rets);
}

/*!
   \class SOPHYA::ObjFileIO<XNTuple>
   \ingroup HiStats
   Persistence (serialisation) handler for class XNTuple
*/

DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
void   ObjFileIO<XNTuple>::WriteSelf(POutPersist& ppout) const 
{
  if (dobj == NULL)   return;
//  On ecrit 3 uint_4 .... 
//  0: Numero de version,  1 : non nul -> has info,  2 : reserve
  uint_4 itab[3];
  itab[0] = 1;  // Numero de version a 1
  itab[1] = itab[2] = 0;
  if (dobj->mInfo)  itab[1] = 1;
  ppout.Put(itab,3);
  if (dobj->mInfo)  if (dobj->mInfo)  ppout << (*(dobj->mInfo));

  // variables internes 
  ppout.PutI4(dobj->mNEnt) ; 
  ppout.PutI4(dobj->mNBlk) ; 
  ppout.PutI4(dobj->mBlkSz) ; 
  ppout.PutI4(dobj->mBlk) ; 
  ppout.PutI4(dobj->mOff) ; 
  ppout.PutI4(dobj->mMaxBlk) ; 
  ppout.PutI4(dobj->mStrSz) ; 
  ppout.PutI4(dobj->mD) ; 
  ppout.PutI4(dobj->mF) ; 
  ppout.PutI4(dobj->mI) ; 
  ppout.PutI4(dobj->mS) ; 
  ppout.PutI4(dobj->mNVars) ; 
    
  // Noms 
  ppout.PutBytes(dobj->mNames, dobj->mNVars*(LENNAME+1)) ; 

  // MinMax 
  ppout.PutR8s(dobj->mMin, (dobj->mD+dobj->mF+dobj->mI)) ; 
  ppout.PutR8s(dobj->mMax, (dobj->mD+dobj->mF+dobj->mI)) ; 
    
  // Ecriture blocs N'ecrire que si datas existent 
  for(int i = 0 ; i < dobj->mNBlk ; i++) {
    // si bloc swappe, on le relit en douce ... 
    if( dobj->ptr[i]->sw )  dobj->read_blk(dobj->ptr[i]) ; 
    ppout.PutR8s(dobj->ptr[i]->ddata, dobj->mD*dobj->mBlkSz) ; 
    ppout.PutR4s(dobj->ptr[i]->fdata, dobj->mF*dobj->mBlkSz) ; 
    ppout.PutI4s(dobj->ptr[i]->idata, dobj->mI*dobj->mBlkSz) ; 
    ppout.PutBytes(dobj->ptr[i]->sdata, dobj->mS*dobj->mBlkSz*(dobj->mStrSz+1)) ; 
    if(dobj->ptr[i]->sw) dobj->ptr[i]->free() ; 
    }
}


DECL_TEMP_SPEC  /* equivalent a template <> , pour SGI-CC en particulier */
void   ObjFileIO<XNTuple>::ReadSelf(PInPersist& ppin) 
{
  if (dobj == NULL) dobj = new XNTuple;
  else    dobj->clean() ; 
//  On lit 3 uint_4 .... 
//  0: Numero de version,  1 : non nul -> has info,  2 : reserve
  uint_4 itab[3];
  ppin.Get(itab,3);
  if (itab[1] != 0) { // Lecture eventuelle du DVList Info
    if (dobj->mInfo == NULL)  dobj->mInfo = new DVList;
    ppin >> (*(dobj->mInfo));
  }

  ppin.GetI4(dobj->mNEnt) ; 
  ppin.GetI4(dobj->mNBlk) ; 
  ppin.GetI4(dobj->mBlkSz) ; 
  ppin.GetI4(dobj->mBlk) ; 
  ppin.GetI4(dobj->mOff) ; 
  ppin.GetI4(dobj->mMaxBlk) ; 
  ppin.GetI4(dobj->mStrSz) ; 
  ppin.GetI4(dobj->mD) ; 
  ppin.GetI4(dobj->mF) ; 
  ppin.GetI4(dobj->mI) ; 
  ppin.GetI4(dobj->mS) ; 
  ppin.GetI4(dobj->mNVars) ; 
    
  // Noms 
  dobj->mNames = new char[dobj->mNVars*(LENNAME+1)] ;
  dobj->mVarD = new r_8[dobj->mNVars]; 
  ppin.GetBytes(dobj->mNames, dobj->mNVars*(LENNAME+1)) ; 

  // MinMax 
  dobj->mMin = new r_8[dobj->mD+dobj->mF+dobj->mI] ; 
  ppin.GetR8s(dobj->mMin, dobj->mD+dobj->mF+dobj->mI) ; 
  dobj->mMax = new r_8[dobj->mD+dobj->mF+dobj->mI] ; 
  ppin.GetR8s(dobj->mMax, dobj->mD+dobj->mF+dobj->mI) ; 
    
  // lecture Blocs 
  int nblk = dobj->mNBlk ; 
  dobj->mBlk = -1 ; dobj->mNBlk = 0 ; 
  for(int i = 0 ; i < nblk ; i++) {
    dobj->add_blk() ; dobj->mBlk++ ; dobj->mNBlk++ ; 
    ppin.GetR8s(dobj->ptr[i]->ddata, dobj->mD*dobj->mBlkSz) ; 
    ppin.GetR4s(dobj->ptr[i]->fdata, dobj->mF*dobj->mBlkSz) ; 
    ppin.GetI4s(dobj->ptr[i]->idata, dobj->mI*dobj->mBlkSz) ; 
    ppin.GetBytes(dobj->ptr[i]->sdata, dobj->mS*dobj->mBlkSz*(dobj->mStrSz+1)) ; 
  } 
}





#ifdef __CXX_PRAGMA_TEMPLATES__
#pragma define_template ObjFileIO<XNTuple>
#endif

#if defined(ANSI_TEMPLATES) || defined(GNU_TEMPLATES)
template class SOPHYA::ObjFileIO<XNTuple>;
#endif


#ifdef __MWERKS__
// Bricolo Dominique Yvon pour faire marcher sur Mac
#include <stdio.h>
#include <stdlib.h>
	static long mktempMemory=0;
	char * mktemp(char * Filename)
	{	sprintf(Filename,"TempFile%8i",mktempMemory);
		mktempMemory++;
		return Filename;
	}
#endif
