Index: trunk/FACT++/src/fitsdump.cc
===================================================================
--- trunk/FACT++/src/fitsdump.cc	(revision 17123)
+++ trunk/FACT++/src/fitsdump.cc	(revision 17147)
@@ -15,4 +15,5 @@
 #include <boost/regex.hpp>
 
+#include "tools.h"
 #include "Time.h"
 #include "externals/factfits.h"
@@ -45,5 +46,5 @@
     minMaxStruct() : min(FLT_MAX), max(-FLT_MAX), average(0), squared(0), numValues(0) { }
 
-    void add(double val)
+    void add(long double val)
     {
         average += val;
@@ -78,10 +79,13 @@
 
     double GetDouble(const MyColumn &, size_t) const;
+    int64_t GetInteger(const MyColumn &, size_t) const;
+    string Format(const string &fmt, const double &val) const;
+    string Format(const string &fmt, const MyColumn &, size_t) const;
 
     ///Display the selected columns values VS time
-    void Dump(ofstream &, const vector<MyColumn> &, const string &, size_t, size_t, const string &);
+    void Dump(ofstream &, const vector<string> &, const vector<MyColumn> &, const string &, size_t, size_t, const string &);
     void DumpRoot(ofstream &, const vector<string> &, const string &, size_t, size_t, const string &);
     void DumpMinMax(ofstream &, const vector<MyColumn> &, size_t, size_t, bool);
-    void DumpStats(ofstream &, const vector<MyColumn> &, size_t, size_t);
+    void DumpStats(ofstream &, const vector<MyColumn> &, const string &, size_t, size_t);
 
 public:
@@ -284,9 +288,108 @@
 }
 
+int64_t FitsDumper::GetInteger(const MyColumn &it, size_t i) const
+{
+    switch (it.col.type)
+    {
+    case 'A':
+        return reinterpret_cast<const char*>(it.ptr)[i];
+
+    case 'L':
+        return reinterpret_cast<const bool*>(it.ptr)[i];
+
+    case 'B':
+        return (unsigned int)reinterpret_cast<const uint8_t*>(it.ptr)[i];
+
+    case 'I':
+        return reinterpret_cast<const int16_t*>(it.ptr)[i];
+
+    case 'J':
+        return reinterpret_cast<const int32_t*>(it.ptr)[i];
+
+    case 'K':
+        return reinterpret_cast<const int64_t*>(it.ptr)[i];
+
+    case 'E':
+        return reinterpret_cast<const float*>(it.ptr)[i];
+
+    case 'D':
+        return reinterpret_cast<const double*>(it.ptr)[i];
+    }
+
+    return 0;
+}
+
+string FitsDumper::Format(const string &format, const MyColumn &col, size_t i) const
+{
+    switch (*format.rbegin())
+    {
+    case 'd':
+    case 'i':
+    case 'o':
+    case 'u':
+    case 'x':
+    case 'X':
+        return Tools::Form(format.c_str(), GetDouble(col, i));
+
+    case 'e':
+    case 'E':
+    case 'f':
+    case 'F':
+    case 'g':
+    case 'G':
+    case 'a':
+    case 'A':
+        return Tools::Form(format.c_str(), GetInteger(col, i));
+
+    case 'h':
+        {
+            string rc = Tools::Scientific(GetDouble(col, i));
+            *remove_if(rc.begin(), rc.end(), ::isspace)=0;
+            return rc;
+        }
+    }
+
+    return "";
+}
+
+string FitsDumper::Format(const string &format, const double &val) const
+{
+    switch (*format.rbegin())
+    {
+    case 'd':
+    case 'i':
+    case 'o':
+    case 'u':
+    case 'x':
+    case 'X':
+        return Tools::Form(format.c_str(), int64_t(val));
+
+    case 'e':
+    case 'E':
+    case 'f':
+    case 'F':
+    case 'g':
+    case 'G':
+    case 'a':
+    case 'A':
+        return Tools::Form(format.c_str(), val);
+
+    case 'h':
+        {
+            string rc = Tools::Scientific(val);
+            *remove_if(rc.begin(), rc.end(), ::isspace)=0;
+            return rc;
+        }
+    }
+
+    return "";
+}
+
+
 // --------------------------------------------------------------------------
 //
 //! Perform the actual dump, based on the current parameters
 //
-void FitsDumper::Dump(ofstream &fout, const vector<MyColumn> &cols, const string &filter, size_t first, size_t limit, const string &filename)
+void FitsDumper::Dump(ofstream &fout, const vector<string> &format, const vector<MyColumn> &cols, const string &filter, size_t first, size_t limit, const string &filename)
 {
     const fits::Table::Keys &fKeyMap = GetKeys();
@@ -357,39 +460,46 @@
         ostringstream sout;
         sout.precision(fout.precision());
-        for (auto it=cols.begin(); it!=cols.end(); it++)
+        sout.flags(fout.flags());
+
+        uint32_t col = 0;
+        for (auto it=cols.begin(); it!=cols.end(); it++, col++)
         {
             string msg;
             for (uint32_t i=it->first; i<=it->last; i++, p++)
             {
-                switch (it->col.type)
+                if (col<format.size())
+                    sout << Format("%"+format[col], *it, i) << " ";
+                else
                 {
-                case 'A':
-                    msg += reinterpret_cast<const char*>(it->ptr)[i];
-                    break;
-                case 'B':
-                    sout << (unsigned int)reinterpret_cast<const unsigned char*>(it->ptr)[i] << " ";
-                    break;
-                case 'L':
-                    sout << reinterpret_cast<const bool*>(it->ptr)[i] << " ";
-                    break;
-                case 'I':
-                    sout << reinterpret_cast<const int16_t*>(it->ptr)[i] << " ";
-                    break;
-                case 'J':
-                    sout << reinterpret_cast<const int32_t*>(it->ptr)[i] << " ";
-                    break;
-                case 'K':
-                    sout << reinterpret_cast<const int64_t*>(it->ptr)[i] << " ";
-                    break;
-                case 'E':
-                    sout << reinterpret_cast<const float*>(it->ptr)[i] << " ";
-                    break;
-                case 'D':
-                    sout << reinterpret_cast<const double*>(it->ptr)[i] << " ";
-                    break;
-                default:
-                    ;
+                    switch (it->col.type)
+                    {
+                    case 'A':
+                        msg += reinterpret_cast<const char*>(it->ptr)[i];
+                        break;
+                    case 'B':
+                        sout << (unsigned int)reinterpret_cast<const unsigned char*>(it->ptr)[i] << " ";
+                        break;
+                    case 'L':
+                        sout << reinterpret_cast<const bool*>(it->ptr)[i] << " ";
+                        break;
+                    case 'I':
+                        sout << reinterpret_cast<const int16_t*>(it->ptr)[i] << " ";
+                        break;
+                    case 'J':
+                        sout << reinterpret_cast<const int32_t*>(it->ptr)[i] << " ";
+                        break;
+                    case 'K':
+                        sout << reinterpret_cast<const int64_t*>(it->ptr)[i] << " ";
+                        break;
+                    case 'E':
+                        sout << reinterpret_cast<const float*>(it->ptr)[i] << " ";
+                        break;
+                    case 'D':
+                        sout << reinterpret_cast<const double*>(it->ptr)[i] << " ";
+                        break;
+                    default:
+                        ;
+                    }
                 }
-
 #ifdef HAVE_ROOT
                 if (!filter.empty())
@@ -680,11 +790,8 @@
     out << "Max: " << double(val[numElems-1]) << '\n';
 
-    if (numElems>2)
-    {
-        if (numElems%2 == 0)
-            out << "Med: " << (double(val[numElems/2]) + double(val[numElems/2+1]))/2 << '\n';
-        else
-            out << "Med: " << double(val[numElems/2+1]) << '\n';
-    }
+    if (numElems%2 == 0)
+        out << "Med: " << (double(val[numElems/2-1]) + double(val[numElems/2]))/2 << '\n';
+    else
+        out << "Med: " << double(val[numElems/2]) << '\n';
 
     long double avg = 0;
@@ -692,6 +799,7 @@
     for (uint32_t i=0;i<numElems;i++)
     {
-        avg += double(val[i]);
-        rms += double(val[i])*double(val[i]);
+        const long double v = val[i];
+        avg += v;
+        rms += v*v;
     }
 
@@ -704,6 +812,12 @@
 }
 
-void FitsDumper::DumpStats(ofstream &fout, const vector<MyColumn> &cols, size_t first, size_t limit)
-{
+void FitsDumper::DumpStats(ofstream &fout, const vector<MyColumn> &cols, const string &filter, size_t first, size_t limit)
+{
+#ifdef HAVE_ROOT
+    TFormula select;
+    if (!filter.empty() && select.Compile(filter.c_str()))
+        throw runtime_error("Syntax Error: TFormula::Compile failed for '"+filter+"'");
+#endif
+
     // Loop over all columns in our list of requested columns
     vector<vector<char>> statData;
@@ -712,8 +826,14 @@
 
     for (auto it=cols.begin(); it!=cols.end(); it++)
-        statData.push_back(vector<char>(it->col.size*num*(it->last-it->first+1)));
+        statData.emplace_back(vector<char>(it->col.size*num*(it->last-it->first+1)));
+
+#ifdef HAVE_ROOT
+    vector<Double_t> data(num+1);
+#endif
 
     // Loop over all columns in our list of requested columns
     const size_t last = limit ? first + limit : size_t(-1);
+
+    uint64_t counter = 0;
 
     while (GetRow(first++))
@@ -723,4 +843,19 @@
             break;
 
+#ifdef HAVE_ROOT
+        if (!filter.empty())
+        {
+            size_t p = 0;
+
+            data[p++] = first-1;
+
+            for (auto it=cols.begin(); it!=cols.end(); it++)
+                for (uint32_t i=it->first; i<=it->last; i++, p++)
+                    data[p] = GetDouble(*it, i);
+
+            if (select.EvalPar(0, data.data())<0.5)
+                continue;
+        }
+#endif
 
         auto statsIt = statData.begin();
@@ -729,6 +864,8 @@
             const char *src = reinterpret_cast<const char*>(it->ptr);
             const size_t sz = (it->last-it->first+1)*it->col.size;
-            memcpy(statsIt->data()+row*sz, src+it->first*it->col.size, sz);
-        }
+            memcpy(statsIt->data()+counter*sz, src+it->first*it->col.size, sz);
+        }
+
+        counter++;
     }
 
@@ -740,4 +877,7 @@
             fout << ':' << it->last;
         fout << "]\n";
+
+        const size_t sz = (it->last-it->first+1)*it->col.size;
+        statsIt->resize(counter*sz);
 
         switch (it->col.type)
@@ -792,10 +932,22 @@
     if (conf.Get<bool>("minmax") && conf.Get<bool>("stat"))
     {
-        cerr << "Invalid combination of options: cannot do stats and minmax. Aborting" << endl;
+        cerr << "Invalid combination of options: cannot do stats and minmax." << endl;
         return -1;
     }
     if (conf.Get<bool>("stat") && conf.Get<bool>("nozero"))
     {
-        cerr << "Invalid combination of options: nozero only works with minmax. Aborting" << endl;
+        cerr << "Invalid combination of options: nozero only works with minmax." << endl;
+        return -1;
+    }
+
+    if (conf.Get<bool>("scientific") && conf.Get<bool>("fixed"))
+    {
+        cerr << "Switched --scientific and --fixed are mutually exclusive." << endl;
+        return -1;
+    }
+
+    if (conf.Has("%") && conf.Has("%%"))
+    {
+        cerr << "Switched --% and --%% are mutually exclusive." << endl;
         return -1;
     }
@@ -812,7 +964,10 @@
     }
     fout.precision(conf.Get<int>("precision"));
+    if (conf.Get<bool>("fixed"))
+        fout << fixed;
+    if (conf.Get<bool>("scientific"))
+        fout << scientific;
 
     const string filter = conf.Has("filter") ? conf.Get<string>("filter") : "";
-
     const size_t first  = conf.Get<size_t>("first");
     const size_t limit  = conf.Get<size_t>("limit");
@@ -825,4 +980,17 @@
     }
 #endif
+
+    const vector<string> format = conf.Vec<string>("%");
+    for (auto it=format.begin(); it<format.end(); it++)
+    {
+        static const boost::regex expr("-?[0-9]*[.]?[0-9]*[diouxXeEfFgGaAh]");
+
+        boost::smatch what;
+        if (!boost::regex_match(*it, what, expr, boost::match_extra))
+        {
+            cerr << "Format '" << *it << "' not supported." << endl;
+            return -1;
+        }
+    }
 
     const vector<MyColumn> cols = InitColumns(conf.Vec<string>("col"));
@@ -830,5 +998,4 @@
         return false;
 
-
     if (conf.Get<bool>("minmax"))
     {
@@ -839,9 +1006,9 @@
     if (conf.Get<bool>("stat"))
     {
-        DumpStats(fout, cols, first, limit);
+        DumpStats(fout, cols, filter, first, limit);
         return 0;
     }
 
-    Dump(fout, cols, filter, first, limit, filename);
+    Dump(fout, format, cols, filter, first, limit, filename);
 
     return 0;
@@ -897,4 +1064,12 @@
         "\n"
         "  fitsdump -r \"#\" --filter=\"#>10 && #<100\"\n"
+        "\n"
+        "To format a single column you can do\n"
+        "\n"
+        "  fitsdump col1 -%.1f col2 -%d\n"
+        "\n"
+        "A special format is provided converting to 'human readable format'\n"
+        "\n"
+        "  fitsdump col1 -%h\n"
         "\n";
     cout << endl;
@@ -920,7 +1095,10 @@
         ("minmax,m",    po_switch(),            "Calculates min and max of data")
         ("nozero,z",    po_switch(),            "skip 0 values for stats")
+        ("fixed",       po_switch(),            "Switch output stream to floating point values in fixed-point notation")
+        ("scientific",  po_switch(),            "Switch output stream to floating point values in scientific notation")
+        ("%,%",         vars<string>(),         "Format for the output (currently not available in root-mode)")
         ("force",       po_switch(),            "Force reading the fits file even if END key is missing")
-        ("first",       var<size_t>(size_t(0)), "First number of row to read")
-        ("limit",       var<size_t>(size_t(0)), "Limit for the maximum number of rows to read (0=unlimited)")
+        ("first",       var<size_t>(0),         "First number of row to read")
+        ("limit",       var<size_t>(0),         "Limit for the maximum number of rows to read (0=unlimited)")
         ("tablename,t", var<string>(""),        "Name of the table to open. If not specified, first binary table is opened")
 #ifdef HAVE_ROOT
