#include "basedtable.h" #include #include "pexceptions.h" #include "thsafeop.h" namespace SOPHYA { /*! \class DataTableRow \ingroup HiStats This class is intented to be used with datatable classes (inheriting from BaseDataTable) for representing a row (line) of the table. */ DataTableRow::DataTableRow( vector const& colnames ) { if (colnames.size() < 1) throw ParmError("DataTableRow::DataTableRow(vector& cn) cn.size()==0 "); size_ = colnames.size(); mtv_ = new MuTyV[ size_ ]; for(sa_size_t k=0; k::const_iterator it = nm2idx_.find(colname); if (it == nm2idx_.end()) { string msg = "DataTableRow::get( " ; msg += colname; msg += " ) Not found column name"; throw NotFoundExc(msg); } return mtv_[(*it).second]; } MuTyV& DataTableRow::get(string const& colname) { map::const_iterator it = nm2idx_.find(colname); if (it == nm2idx_.end()) { string msg = "DataTableRow::get( " ; msg += colname; msg += " ) const - Not found column name"; throw NotFoundExc(msg); } return mtv_[(*it).second]; } ostream& DataTableRow::Print(ostream& os) const { for(sa_size_t k=0; k 0) ? segsz : 16; mNSeg = 0; mVarD = NULL; mVarMTV = NULL; mInfo = NULL; mThS = NULL; } BaseDataTable::~BaseDataTable() { if (mVarD) delete[] mVarD; if (mVarMTV) delete[] mVarMTV; if (mInfo) delete mInfo; if (mThS) delete mThS; } /*! \brief Activate or deactivate thread-safe \b AddRow() operations If fg==true, create an ThSafeOp object in order to insure atomic AddRow() and GetRow()/GetColumn() operations. if fg==false, the ThSafeOp object (mThS) of the target DataTable is destroyed. When activated, the following operations are thread-safe : - AddRow(const r_8* data) - AddRow(const MuTyV* data) - AddRow(DataTableRow const& data) - GetRow(sa_size_t n, DataTableRow& row) - GetRow(sa_size_t n, MuTyV* mtvp) - GetColumnD(sa_size_t k) \warning The default AddRow() operation mode for DataTables is NOT thread-safe. Please note also that the thread-safety state is NOt saved to PPF (or FITS) streams. */ void BaseDataTable::SetThreadSafe(bool fg) { if (fg) { if (mThS) return; else mThS = new ThSafeOp(); } else { if (mThS) delete mThS; mThS = NULL; } } /*! \cond \class DT_TSOP_SYNC \ingroup HiStats Classe utilitaire pour faciliter la gestion de lock pour operations thread-safe BaseDataTable */ class DT_TSOP_SYNC { public: explicit DT_TSOP_SYNC(ThSafeOp* ths) { ths_ = ths; if (ths_) ths_->lock(); } ~DT_TSOP_SYNC() { if (ths_) ths_->unlock(); } inline ThSafeOp* NOp() { return ths_; } protected: ThSafeOp* ths_; }; /*! \endcond */ sa_size_t BaseDataTable::CopyStructure(BaseDataTable const & a) { if (NVar() > 0) throw ParmError("BaseDataTable::CopyStructure() Table already has columns"); if (a.NVar() == 0) { cout << "BaseDataTable::CopyStructure(a)/Warning Table a is not initialized" << endl; return 0; } for (size_t kk=0; kk nms; for(sa_size_t k=0; k= NVar())) throw RangeCheckError("BaseDataTable::NomIndex() out of range column index k"); return mNames[k].nom ; } //! Adds a row (or line) to the table with r_8* inout data /*! The data to be added is provided as an array (vector) of double (r_8). The necessary data conversion is performed, depending on each column's data typeconverted to the data type. Return the new number of table rows (lines / entries) \param data : Data for each cell of the row to be appended (data[k] k=0..NbColumns()) */ sa_size_t BaseDataTable::AddRow(const r_8* data) { DT_TSOP_SYNC dttss(mThS); dttss.NOp(); // Thread-safe operation if (NVar() == 0) throw ParmError("BaseDataTable::AddRow(const r_8*) Table has no column !"); if (NEntry() == SegmentSize()*NbSegments()) Extend(); sa_size_t n = NEntry(); sa_size_t bid = n/mSegSz; sa_size_t off = n%mSegSz; for(sa_size_t k=0; kGetSegment(bid)[off] = (int_4)data[mIColIdx[k]]; for(sa_size_t k=0; kGetSegment(bid)[off] = (int_8)data[mLColIdx[k]]; for(sa_size_t k=0; kGetSegment(bid)[off] = (r_4)data[mFColIdx[k]]; for(sa_size_t k=0; kGetSegment(bid)[off] = data[mDColIdx[k]]; for(sa_size_t k=0; kGetSegment(bid)[off] = complex(data[mYColIdx[k]],0.); for(sa_size_t k=0; kGetSegment(bid)[off] = complex(data[mZColIdx[k]],0.); for(sa_size_t k=0; kGetSegment(bid)[off] = (string)MuTyV(data[mSColIdx[k]]); mNEnt++; return mNEnt; } //! Adds a row (or line) to the table with input data as an array of MuTyV /*! The data to be added is provided as an array (vector) of MuTyV. The MuTyV class conversion operators are used to match against each cell data type. Return the new number of table rows (lines / entries) \param data : Data (MuTyV*) for each cell of the row to be appended (data[k] k=0..NbColumns()) */ sa_size_t BaseDataTable::AddRow(const MuTyV* data) { DT_TSOP_SYNC dttss(mThS); dttss.NOp(); // Thread-safe operation if (NVar() == 0) throw ParmError("BaseDataTable::AddRow(const MuTyV*) Table has no column !"); if (NEntry() == SegmentSize()*NbSegments()) Extend(); sa_size_t n = NEntry(); sa_size_t bid = n/mSegSz; sa_size_t off = n%mSegSz; for(sa_size_t k=0; kGetSegment(bid)[off] = (int_4)data[mIColIdx[k]]; for(sa_size_t k=0; kGetSegment(bid)[off] = (int_8)data[mLColIdx[k]]; for(sa_size_t k=0; kGetSegment(bid)[off] = (string)data[mSColIdx[k]]; for(sa_size_t k=0; kGetSegment(bid)[off] = (r_4)data[mFColIdx[k]]; for(sa_size_t k=0; kGetSegment(bid)[off] = (r_8)data[mDColIdx[k]]; for(sa_size_t k=0; kGetSegment(bid)[off] = complex(data[mYColIdx[k]].GetRealPart(), data[mYColIdx[k]].GetImagPart()); for(sa_size_t k=0; kGetSegment(bid)[off] = complex(data[mZColIdx[k]].GetRealPart(), data[mZColIdx[k]].GetImagPart()); mNEnt++; return mNEnt; } //! Adds a row (or line) to the table with input data as DataTableRow object /*! The internal MuTyV array of the object contains the data and the MuTyV class conversion operators are used to match against each cell data type. Only the size of the input data object is checked. Return the new number of table rows (lines / entries) \param data : Data for each cell of the row to be appended (data[k] k=0..NbColumns()) */ sa_size_t BaseDataTable::AddRow(DataTableRow const& data) { if ( data.Size() != NCols() ) throw SzMismatchError(" BaseDataTable::AddRow() - data.Size() != NCols() "); return AddRow(data.MTVPtr()); } /*! Extends the table (in the row direction). This method is called automatically when needed. */ sa_size_t BaseDataTable::Extend() { for(sa_size_t k=0; kExtend(); for(sa_size_t k=0; kExtend(); for(sa_size_t k=0; kExtend(); for(sa_size_t k=0; kExtend(); for(sa_size_t k=0; kExtend(); for(sa_size_t k=0; kExtend(); for(sa_size_t k=0; kExtend(); mNSeg++; return mNSeg; } /*! Fills the input \b row object with the content of row \b n. Return a reference to the input \b row object. Generate an exception if the input \b row object has the wrong size. */ DataTableRow& BaseDataTable::GetRow(sa_size_t n, DataTableRow& row) const { if ( row.Size() != NCols() ) throw SzMismatchError(" BaseDataTable::GetRow(n, row) - row.Size() != NCols() "); GetRow(n, row.MTVPtr()); return row; } /*! For thread-safe operation, specify a valid \b mtvp pointer (!= NULL) */ MuTyV* BaseDataTable::GetRow(sa_size_t n, MuTyV* mtvp) const { DT_TSOP_SYNC dttss(mThS); dttss.NOp(); // Thread-safe operation if ((n < 0) || (n >= NEntry())) throw RangeCheckError("BaseDataTable::GetRow() out of range line index n"); if (mtvp == NULL) { if (mVarMTV == NULL) mVarMTV = new MuTyV[NVar()]; mtvp = mVarMTV; } sa_size_t bid = n/mSegSz; sa_size_t off = n%mSegSz; for(sa_size_t k=0; kGetCstSegment(bid)[off]; for(sa_size_t k=0; kGetCstSegment(bid)[off]; for(sa_size_t k=0; kGetCstSegment(bid)[off]; for(sa_size_t k=0; kGetCstSegment(bid)[off]); else mtvp[mDColIdx[k]] = mDColsP[k]->GetCstSegment(bid)[off]; } for(sa_size_t k=0; kGetCstSegment(bid)[off]; for(sa_size_t k=0; kGetCstSegment(bid)[off]; for(sa_size_t k=0; kGetCstSegment(bid)[off]; return mtvp; } #define BADVAL -1.e39 TVector BaseDataTable::GetColumnD(sa_size_t k) const { DT_TSOP_SYNC dttss(mThS); dttss.NOp(); // Thread-safe operation if ((k < 0) || (k >= NVar())) throw RangeCheckError("BaseDataTable::GetColumnD() out of range column index k"); sa_size_t sk = mNames[k].ser; sa_size_t i = 0; TVector rv(NEntry()); for (sa_size_t is=0; isGetCstSegment(is)[j]; break; case LongField : for(sa_size_t j=0; (jGetCstSegment(is)[j]; break; case FloatField : for(sa_size_t j=0; (jGetCstSegment(is)[j]; break; case DoubleField : case DateTimeField : for(sa_size_t j=0; (jGetCstSegment(is)[j]; break; case ComplexField : for(sa_size_t j=0; (jGetCstSegment(is)[j].real(); break; case DoubleComplexField : for(sa_size_t j=0; (jGetCstSegment(is)[j].real(); break; case StringField : for(sa_size_t j=0; (jGetCstSegment(is)[j].c_str()); break; default: for(sa_size_t j=0; (j 0) ) throw ParmError("BaseDataTable::CopyMerge(a) Table has entries already"); if (a.NVar() == 0) throw ParmError("BaseDataTable::CopyMerge(a) Table a has no column"); if (NVar() == 0) CopyStructure(a); else if (!CompareStructure(a)) throw SzMismatchError("BaseDataTable::CopyMerge(a) (this,a) have different table structure"); if (a.NEntry() == 0) { cout << " BaseDataTable::CopyMerge(a)/Warning : table a has zero (0) entry ! " << endl; return; } for(sa_size_t kk=0; kk~ 10^6) */ void BaseDataTable::Show(ostream& os) const { os << "BaseDataTable: NVar= " << NVar() << " NEnt= " << NEntry() << " ( SegSize= " << SegmentSize() << " NbSegments= " << NbSegments() << " )" << endl; os << "--------------------------------------------------------------------" << endl; os << setw(3) << "i" << ":" << setw(20) << " Name" << " (" << setw(4) << "Type" << ") | " << setw(15) << " Min " << " | " << setw(15) << " Max " << endl; os << "--------------------------------------------------------------------" << endl; r_8 min, max ; for(sa_size_t i = 0 ; i < NVar() ; i++) { GetMinMax(i, min, max) ; string nom; if (mNames[i].nom.length() > 20) nom = mNames[i].nom.substr(20); else nom = mNames[i].nom; os << setw(3) << i << ":" << setw(20) << nom << " (" << setw(4) << ColTypeToString(mNames[i].type) << ") | " << setw(15) << min << " | " << setw(15) << max << endl; } os << "--------------------------------------------------------------------" << endl; return; } //! Fills table from an ascii (text) file /* Return number of non empt lines (added to table) \param is : input ascii (text) stream \param clm : Lines starting with clm are ignored (comments) \param sep : separator between different fields (columns) */ sa_size_t BaseDataTable::FillFromASCIIFile(istream& is, char clm, const char* sep) { string str; if (mVarMTV == NULL) mVarMTV = new MuTyV[NVar()]; sa_size_t iv, nl; nl = 0; while (!is.eof()) { str = ""; getline(is, str); if (is.good() || is.eof()) { size_t l = str.length(); if ((l == 0) || (str[0]==clm)) continue; for(iv=0; iv= l) break; if (str[p] == '\'') { // Decodage d'un string q = str.find('\'',p+1); if (q < l) { mVarMTV[iv] = str.substr(p+1,q-p-1); q++; } else mVarMTV[iv] = str.substr(p+1,l-p-1); iv++; } else { q = str.find_first_of(sep,p); if (q > l) q = l; mVarMTV[iv] = str.substr(p,q-p); iv++; } if (mNames[iv-1].type == DateTimeField) { string tts = (string)mVarMTV[iv-1]; mVarMTV[iv-1] = TimeStamp(tts); } } AddRow(mVarMTV); nl++; } } // Fin boucle lignes fichier cout << "BaseDataTable::FillFromASCIIFile()/Info: " << nl << " lines decoded from stream " << endl; return(nl); } // // ------------------------------------ // ------- Interface NTuple ----------- // ------------------------------------ // sa_size_t BaseDataTable::NbLines() const { return(NEntry()); } sa_size_t BaseDataTable::NbColumns() const { return(NVar()); } r_8* BaseDataTable::GetLineD(sa_size_t n) const { if ((n < 0) || (n >= NEntry())) throw RangeCheckError("BaseDataTable::GetLineD() out of range line index n"); if (mVarD == NULL) mVarD = new r_8[NVar()]; sa_size_t bid = n/mSegSz; sa_size_t off = n%mSegSz; for(sa_size_t k=0; kGetCstSegment(bid)[off]; for(sa_size_t k=0; kGetCstSegment(bid)[off]; for(sa_size_t k=0; kGetCstSegment(bid)[off]; for(sa_size_t k=0; kGetCstSegment(bid)[off]; for(sa_size_t k=0; kGetCstSegment(bid)[off].real(); for(sa_size_t k=0; kGetCstSegment(bid)[off].real(); for(sa_size_t k=0; kGetCstSegment(bid)[off].c_str()); return mVarD; } r_8 BaseDataTable::GetCell(sa_size_t n, sa_size_t k) const { if ((n < 0) || (n >= NEntry())) throw RangeCheckError("BaseDataTable::GetCell() out of range line index n"); if ((k < 0) || (k >= NVar())) throw RangeCheckError("BaseDataTable::GetCell() out of range column index k"); double rv = BADVAL; sa_size_t sk = mNames[k].ser; sa_size_t bid = n/mSegSz; sa_size_t off = n%mSegSz; switch (mNames[k].type) { case IntegerField : rv = mIColsP[sk]->GetCstSegment(bid)[off]; break; case LongField : rv = mLColsP[sk]->GetCstSegment(bid)[off]; break; case FloatField : rv = mFColsP[sk]->GetCstSegment(bid)[off]; break; case DoubleField : case DateTimeField : rv = mDColsP[sk]->GetCstSegment(bid)[off]; break; case ComplexField : rv = mYColsP[sk]->GetCstSegment(bid)[off].real(); break; case DoubleComplexField : rv = mZColsP[sk]->GetCstSegment(bid)[off].real(); break; case StringField : rv = atof(mSColsP[sk]->GetCstSegment(bid)[off].c_str()); break; default: rv = BADVAL; break; } return rv ; } r_8 BaseDataTable::GetCell(sa_size_t n, string const& nom) const { return GetCell(n, IndexNom(nom)); } string BaseDataTable::GetCelltoString(sa_size_t n, sa_size_t k) const { if ((n < 0) || (n >= NEntry())) throw RangeCheckError("BaseDataTable::GetCell() out of range line index n"); if ((k < 0) || (k >= NVar())) throw RangeCheckError("BaseDataTable::GetCell() out of range column index k"); MuTyV rv;; sa_size_t sk = mNames[k].ser; sa_size_t bid = n/mSegSz; sa_size_t off = n%mSegSz; switch (mNames[k].type) { case IntegerField : rv = mIColsP[sk]->GetCstSegment(bid)[off]; break; case LongField : rv = mLColsP[sk]->GetCstSegment(bid)[off]; break; case FloatField : rv = mFColsP[sk]->GetCstSegment(bid)[off]; break; case DoubleField : rv = mDColsP[sk]->GetCstSegment(bid)[off]; break; case ComplexField : rv = mYColsP[sk]->GetCstSegment(bid)[off]; break; case DoubleComplexField : rv = mZColsP[sk]->GetCstSegment(bid)[off]; break; case StringField : rv = mSColsP[sk]->GetCstSegment(bid)[off]; break; case DateTimeField : rv = TimeStamp(mDColsP[sk]->GetCstSegment(bid)[off]); break; default: rv = " "; break; } return (string)rv ; } void BaseDataTable::GetMinMax(sa_size_t k, double& min, double& max) const { min = 9E39 ; max = -9E39 ; if ((k < 0) || (k >= NVar())) throw RangeCheckError("BaseDataTable::GetCell() out of range column index k"); if (mMinMaxNEnt.size() < NVar()) { mMin.clear(); mMax.clear(); mMinMaxNEnt.clear(); for(size_t kk=0; kkNbSegments(); is++) { const int_4* sp = mIColsP[sk]->GetCstSegment(is); for(size_t n=0; nSegmentSize(); n++) { if (cnt >= NEntry()) break; if (sp[n] > max) max = sp[n]; if (sp[n] < min) min = sp[n]; cnt++; } } break; case LongField : for(size_t is=0; isNbSegments(); is++) { const int_8* sp = mLColsP[sk]->GetCstSegment(is); for(size_t n=0; nSegmentSize(); n++) { if (cnt >= NEntry()) break; if (sp[n] > max) max = sp[n]; if (sp[n] < min) min = sp[n]; cnt++; } } break; case FloatField : for(size_t is=0; isNbSegments(); is++) { const r_4* sp = mFColsP[sk]->GetCstSegment(is); for(size_t n=0; nSegmentSize(); n++) { if (cnt >= NEntry()) break; if (sp[n] > max) max = sp[n]; if (sp[n] < min) min = sp[n]; cnt++; } } break; case DoubleField : case DateTimeField : for(size_t is=0; isNbSegments(); is++) { const r_8* sp = mDColsP[sk]->GetCstSegment(is); for(size_t n=0; nSegmentSize(); n++) { if (cnt >= NEntry()) break; if (sp[n] > max) max = sp[n]; if (sp[n] < min) min = sp[n]; cnt++; } } break; case ComplexField : for(size_t is=0; isNbSegments(); is++) { const complex * sp = mYColsP[sk]->GetCstSegment(is); for(size_t n=0; nSegmentSize(); n++) { if (cnt >= NEntry()) break; if (sp[n].real() > max) max = sp[n].real(); if (sp[n].real() < min) min = sp[n].real(); cnt++; } } break; case DoubleComplexField : for(size_t is=0; isNbSegments(); is++) { const complex * sp = mZColsP[sk]->GetCstSegment(is); for(size_t n=0; nSegmentSize(); n++) { if (cnt >= NEntry()) break; if (sp[n].real() > max) max = sp[n].real(); if (sp[n].real() < min) min = sp[n].real(); cnt++; } } break; case StringField : return; break; default: return; break; } mMinMaxNEnt[k] = cnt; mMin[k] = min; mMax[k] = max; return ; } void BaseDataTable::GetMinMax(string const & nom, double& min, double& max) const { GetMinMax(IndexNom(nom), min, max) ; } sa_size_t BaseDataTable::ColumnIndex(string const& nom) const { return IndexNom(nom) ; } string BaseDataTable::ColumnName(sa_size_t k) const { return NomIndex(k) ; } string BaseDataTable::VarList_C(const char* nomx) const { string rets=""; sa_size_t i; for(i=0; i 0) ) rets += ";"; if (i%5 == 0) rets += "\ndouble "; else rets += ","; rets += mNames[i].nom; } rets += "; \n"; if (nomx) { char buff[256]; for(i=0; i 0) ) rets += "\n"; } } return(rets); } string BaseDataTable::LineHeaderToString() const { string rets,s; for(int i=0; i 0) rets += ' '; rets += s; } return(rets); } /*! Return a table row (line) as a string \sa TableRowToString() */ string BaseDataTable::LineToString(sa_size_t n) const { return TableRowToString(n, false); } /*! \param n : table row index ( 0 ... NEntry()-1) \param qstr : if true , enclose strings in quotes '' \param sep : separates fields using \b sep \param fw : minimum field width */ string BaseDataTable::TableRowToString(sa_size_t n, bool qstr, const char* sep, int fw) const { if ((n < 0) || (n >= NEntry())) throw RangeCheckError("BaseDataTable::GetCell() out of range line index n"); string rs; MuTyV rv;; sa_size_t bid = n/mSegSz; sa_size_t off = n%mSegSz; for(sa_size_t k=0; kGetCstSegment(bid)[off]; break; case LongField : rv = mLColsP[sk]->GetCstSegment(bid)[off]; break; case FloatField : rv = mFColsP[sk]->GetCstSegment(bid)[off]; break; case DoubleField : rv = mDColsP[sk]->GetCstSegment(bid)[off]; break; case ComplexField : rv = mYColsP[sk]->GetCstSegment(bid)[off]; break; case DoubleComplexField : rv = mZColsP[sk]->GetCstSegment(bid)[off]; break; case StringField : rv = mSColsP[sk]->GetCstSegment(bid)[off]; break; case DateTimeField : rv = TimeStamp(mDColsP[sk]->GetCstSegment(bid)[off]); break; default: rv = " "; break; } string s; if ( (mNames[k].type == StringField) && (qstr) ) { s = '\''; s += (string)rv; s += '\''; } else s= (string)rv; size_t l = s.length(); for(size_t ii=l; ii 0) rs += sep; rs += s; } return rs; } } // FIN namespace SOPHYA