Index: trunk/Mars/mcore/checksum.h
===================================================================
--- trunk/Mars/mcore/checksum.h	(revision 12995)
+++ trunk/Mars/mcore/checksum.h	(revision 12995)
@@ -0,0 +1,166 @@
+#ifndef MARS_checksum
+#define MARS_checksum
+
+namespace std
+{
+
+class Checksum
+{
+public:
+    uint64_t buffer;
+
+    void reset() { buffer = 0; }
+    Checksum() : buffer(0) { }
+    Checksum(const Checksum &sum) : buffer(sum.buffer) { }
+    Checksum(uint64_t v) : buffer(((v>>16)&0xffff) | ((v&0xffff)<<32)) { }
+
+    uint32_t val() const { return (((buffer&0xffff)<<16) | ((buffer>>32)&0xffff)); }
+
+    bool valid() const { return buffer==0xffff0000ffff; }
+
+    void HandleCarryBits()
+    {
+        while (1)
+        {
+            const uint64_t carry = ((buffer>>48)&0xffff) | ((buffer&0xffff0000)<<16);
+            if (!carry)
+                break;
+
+            buffer = (buffer&0xffff0000ffff) + carry;
+        }
+    }
+
+    Checksum &operator+=(const Checksum &sum)
+    {
+        buffer += sum.buffer;
+        HandleCarryBits();
+        return *this;
+    }
+    Checksum operator+(Checksum sum) const
+    {
+        return (sum += *this);
+    }
+
+
+    bool add(const char *buf, size_t len)
+    {
+        // Avoid overflows in carry bits
+        if (len>262140) // 2^18-4
+        {
+            add(buf, 262140);
+            return add(buf+262140, len-262140);
+        }
+
+        if (len%4>0)
+        {
+            ostringstream sout;
+            sout << "Length " << len << " not dividable by 4." << endl;
+
+#ifdef __EXCEPTIONS
+            throw runtime_error(sout.str());
+#else
+            gLog << ___err___ << "ERROR - " << sout.str() << endl;
+            return false;
+#endif
+        }
+
+        const unsigned short *sbuf = reinterpret_cast<const unsigned short *>(buf);
+
+        uint32_t *hilo  = reinterpret_cast<uint32_t*>(&buffer);
+
+        for (size_t ii = 0; ii < len/2; ii++)
+            hilo[(ii+1)%2] += sbuf[ii];
+
+        HandleCarryBits();
+
+        return true;
+    }
+
+    bool add(const vector<char> &v)
+    {
+        return add(v.data(), v.size());
+    }
+
+    string str(bool complm=true) const
+    {
+        string rc(16,0);
+
+        const uint8_t exclude[13] =
+        {
+            0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
+            0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60
+        };
+
+        const uint32_t value = complm ? ~val() : val();   // complement each bit of the value
+/*
+        for (int ii = 0; ii < 4; ii++)
+        {
+            uint8_t byte = (value >> (24 - (8 * ii)));
+
+            const uint8_t quotient  = byte / 4 + offset;
+            const uint8_t remainder = byte % 4;
+
+            uint32_t ch[4] = { quotient+remainder, quotient, quotient, quotient };
+
+            // avoid ASCII  punctuation
+            while (1)
+            {
+                bool check = false;
+                for (int kk = 0; kk < 13; kk++)
+                {
+                    for (int jj = 0; jj < 4; jj += 2)
+                    {
+                        if (ch[jj] != exclude[kk] &&  ch[jj+1] != exclude[kk])
+                            continue;
+
+                        ch[jj]++;
+                        ch[jj+1]--;
+                        check++;
+                    }
+                }
+
+                if (!check)
+                    break;
+            }
+
+            for (int jj = 0; jj < 4; jj++)        // assign the bytes
+                rc[4*jj+ii] = ch[jj];
+        }
+
+        return rc;
+        */
+        const uint8_t *p = reinterpret_cast<const uint8_t*>(&value);
+
+        for (int i=0; i<4; i++)
+        {
+            rc[i+ 0] = '0' + p[i]/4 + p[i]%4;
+            rc[i+ 4] = '0' + p[i]/4;
+            rc[i+ 8] = '0' + p[i]/4;
+            rc[i+12] = '0' + p[i]/4;
+        }
+
+        while(1)
+        {
+            bool ok  = true;
+            for (int i=0; i<16-4; i++)
+            {
+                for (int j=0; j<13; j++)
+                    if (rc[i]==exclude[j] || rc[(i+4)%16]==exclude[j])
+                    {
+                        rc[i]++;
+                        rc[(i+4)%16]--;
+                        ok = false;
+                    }
+            }
+
+            if (ok)
+                break;
+        }
+
+
+        return rc;
+    }
+};
+}
+
+#endif
Index: trunk/Mars/mcore/ofits.h
===================================================================
--- trunk/Mars/mcore/ofits.h	(revision 12995)
+++ trunk/Mars/mcore/ofits.h	(revision 12995)
@@ -0,0 +1,832 @@
+#ifndef MARS_ofits
+#define MARS_ofits
+
+#include <string>
+#include <string.h>
+#include <algorithm>
+#include <sstream>
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <vector>
+#include <inttypes.h>
+#include <algorithm>
+
+#ifdef __EXCEPTIONS
+#include <stdexcept>
+#endif
+
+#include "checksum.h"
+
+namespace std
+{
+// Sloppy:  allow / <--- left
+//          allow all characters (see specs for what is possible)
+
+// units: m kg s rad sr K A mol cd Hz J W V N Pa C Ohm S F Wb T Hlm lx
+
+class ofits : public ofstream
+{
+    struct Key
+    {
+        string key;
+        bool   delim;
+        string value;
+        string comment;
+
+        off_t offset;   // offset in file
+
+        bool changed;   // For closing the file
+
+        Key(const string &k="") : key(k), delim(false), offset(0), changed(true) { }
+
+        string Trim(const string &str)
+        {
+            // Trim Both leading and trailing spaces
+            const size_t start = str.find_first_not_of(' '); // Find the first character position after excluding leading blank spaces
+            const size_t end   = str.find_last_not_of(' ');  // Find the first character position from reverse af
+
+            // if all spaces or empty return an empty string
+            if (string::npos==start || string::npos==end)
+                return string();
+
+            return str.substr(start, end-start+1);
+        }
+
+        bool FormatKey()
+        {
+            key = Trim(key);
+            if (key.size()==0)
+            {
+#ifdef __EXCEPTIONS
+                throw runtime_error("Key name empty.");
+#else
+                gLog << ___err___ << "ERROR - Key name empty." << endl;
+                return false;
+#endif
+            }
+            if (key.size()>8)
+            {
+                ostringstream sout;
+                sout << "Key '" << key << "' exceeds 8 bytes.";
+#ifdef __EXCEPTIONS
+                throw runtime_error(sout.str());
+#else
+                gLog << ___err___ << "ERROR - " << sout.str() << endl;
+                return false;
+#endif
+            }
+
+            //transform(key.begin(), key.end(), key.begin(), toupper);
+
+            for (string::const_iterator c=key.begin(); c<key.end(); c++)
+                if ((*c<'A' || *c>'Z') && (*c<'0' || *c>'9') && *c!='-' && *c!='_')
+                {
+                    ostringstream sout;
+                    sout << "Invalid character '" << *c << "' found in key '" << key << "'";
+#ifdef __EXCEPTIONS
+                    throw runtime_error(sout.str());
+#else
+                    gLog << ___err___ << "ERROR - " << sout.str() << endl;
+                    return false;
+#endif
+                }
+
+            return true;
+        }
+
+        bool FormatComment()
+        {
+            comment = Trim(comment);
+
+            for (string::const_iterator c=key.begin(); c<key.end(); c++)
+                if (*c<32 || *c>126)
+                {
+                    ostringstream sout;
+                    sout << "Invalid character '" << *c << "' [" << int(*c) << "] found in comment '" << comment << "'";
+#ifdef __EXCEPTIONS
+                    throw runtime_error(sout.str());
+#else
+                    gLog << ___err___ << "ERROR - " << sout.str() << endl;
+                    return false;
+#endif
+                }
+
+            return true;
+        }
+
+        bool check()
+        {
+            if (!FormatKey())
+                return false;
+
+            if (!FormatComment())
+                return false;
+
+            const size_t sz = CalcSize();
+            if (sz>80)
+            {
+                ostringstream sout;
+                sout << "Size " << sz << " of entry for key '" << key << "' exceeds 80 characters.";
+#ifdef __EXCEPTIONS
+                throw runtime_error(sout.str());
+#else
+                gLog << ___err___ << "ERROR - " << sout.str() << endl;
+                return false;
+#endif
+            }
+
+            return true;
+        }
+
+        size_t CalcSize() const
+        {
+            if (!delim)
+                return 10+comment.size();
+
+            return 10 + (value.size()<20?20:value.size()) + 3 + comment.size();
+        }
+
+        string Compile()
+        {
+            ostringstream out;
+            out << std::left << setw(8) << key;
+
+            if (!delim)
+            {
+                out << "  " << comment;
+                return out.str();
+            }
+
+            out << "= ";
+            out << (value[0]=='\''?std::left:std::right);
+            out << setw(20) << value << std::left;
+
+            if (comment.size()>0)
+                out << " / " << comment;
+
+            return out.str();
+        }
+
+        Checksum checksum;
+
+        void Out(ofstream &out)
+        {
+            if (!changed)
+                return;
+
+            string str = Compile();
+            str.insert(str.end(), 80-str.size(), ' ');
+
+            if (offset==0)
+                offset = out.tellp();
+
+            //cout << "Write[" << offset << "]: " << key << "/" << value << endl;
+
+            out.seekp(offset);
+            out << str;
+
+            checksum.reset();
+            checksum.add(str.c_str(), 80);
+
+            changed = false;
+        }
+
+        void Out(ostream &out)
+        {
+            string str = Compile();
+
+            str.insert(str.end(), 80-str.size(), ' ');
+
+            out << str;
+            changed = false;
+        }
+    };
+
+    vector<Key> fKeys;
+
+    vector<Key>::iterator findkey(const string &key)
+    {
+        for (auto it=fKeys.begin(); it!=fKeys.end(); it++)
+            if (key==it->key)
+                return it;
+
+        return fKeys.end();
+    }
+
+    bool Set(const string &key="", bool delim=false, const string &value="", const string &comment="")
+    {
+        // If no delimit add the row no matter if it alread exists
+        if (delim)
+        {
+            // if the row already exists: update it
+            auto it = findkey(key);
+            if (it!=fKeys.end())
+            {
+                it->value   = value;
+                it->changed = true;
+                return true;
+            }
+        }
+
+        if (fTable.num_rows>0)
+        {
+            ostringstream sout;
+            sout << "No new header key can be defined, rows were already written to the file... ignoring new key '" << key << "'";
+#ifdef __EXCEPTIONS
+                throw runtime_error(sout.str());
+#else
+                gLog << ___err___ << "ERROR - " << sout.str() << endl;
+                return false;
+#endif
+        }
+
+        Key entry;
+
+        entry.key     = key;
+        entry.delim   = delim;
+        entry.value   = value;
+        entry.comment = comment;
+        entry.offset  = 0;
+        entry.changed = true;
+
+        if (!entry.check())
+            return false;
+
+        fKeys.push_back(entry);
+        return true;
+    }
+
+    struct Table
+    {
+        off_t offset;
+
+        size_t bytes_per_row;
+        size_t num_rows;
+        size_t num_cols;
+
+        struct Column
+        {
+            string name;
+            size_t offset;
+            size_t num;
+            size_t size;
+            char   type;
+        };
+
+        vector<Column> cols;
+
+        Table() : offset(0), bytes_per_row(0), num_rows(0), num_cols(0)
+        {
+        }
+    };
+
+
+    Table fTable;
+
+    vector<char> fOutputBuffer;
+
+    vector<Table::Column>::iterator findcol(const string &name)
+    {
+        for (auto it=fTable.cols.begin(); it!=fTable.cols.end(); it++)
+            if (name==it->name)
+                return it;
+
+        return fTable.cols.end();
+    }
+
+    Checksum fDataSum;
+    Checksum fHeaderSum;
+
+public:
+    ofits()
+    {
+    }
+    ofits(const char *fname) : ofstream()
+    {
+        this->open(fname);
+    }
+    ~ofits() { close(); }
+
+    void open(const char * filename)
+    {
+        fDataSum  = 0;
+        fHeaderSum = 0;
+
+        fTable = Table();
+        fKeys.clear();
+
+        SetStr("XTENSION", "BINTABLE", "binary table extension");
+        SetStr("EXTNAME", "", "name of extension table");
+        SetInt("BITPIX",                     8, "8-bit bytes");
+        SetInt("NAXIS",                      2, "2-dimensional binary table");
+        SetInt("NAXIS1",                     0, "width of table in bytes");
+        SetInt("NAXIS2",                     0, "number of rows in table");
+        SetInt("PCOUNT",                     0, "size of special data area");
+        SetInt("GCOUNT",                     1, "one data group (required keyword)");
+        SetInt("TFIELDS",                    0, "number of fields in each row");
+        SetStr("CHECKSUM", "{0000000000000000}", "Checksum for the whole file");
+        SetInt("DATASUM",                    0, "Checksum for the data block");
+
+        ofstream::open(filename);
+    }
+
+
+    bool SetBool(const string &key, bool b, const string &comment="")
+    {
+        return Set(key, true, b?"T":"F", comment);
+    }
+
+    bool AddEmpty(const string &key, const string &comment="")
+    {
+        return Set(key, true, "", comment);
+    }
+
+    bool SetStr(const string &key, string s, const string &comment="")
+    {
+        for (string::iterator c=s.begin(); c<s.end(); c++)
+            if (*c=='\'')
+                s.insert(c++, '\'');
+
+        return Set(key, true, "'"+s+"'", comment);
+    }
+
+    bool SetInt(const string &key, int64_t i, const string &comment="")
+    {
+        ostringstream out;
+        out << i;
+
+        return Set(key, true, out.str(), comment);
+    }
+
+    bool SetFloat(const string &key, double f, int p, const string &comment="")
+    {
+        ostringstream out;
+
+        if (p<0)
+            out << setprecision(-p) << fixed;
+        if (p>0)
+            out << setprecision(p);
+        if (p==0)
+            out << setprecision(f>1e-100 && f<1e100 ? 15 : 14);
+
+        out << f;
+
+        string str = out.str();
+
+        replace(str.begin(), str.end(), 'e', 'E');
+
+        if (str.find_first_of('E')==string::npos && str.find_first_of('.')==string::npos)
+            str += ".";
+
+        return Set(key, true, str, comment);
+    }
+
+    bool SetFloat(const string &key, double f, const string &comment="")
+    {
+        return SetFloat(key, f, 0, comment);
+    }
+
+    bool SetHex(const string &key, uint64_t i, const string &comment="")
+    {
+        ostringstream out;
+        out << hex << "0x" << i;
+        return SetStr(key, out.str(), comment);
+    }
+
+    bool AddComment(const string &comment)
+    {
+        return Set("COMMENT", false, "", comment);
+    }
+
+    bool AddHistory(const string &comment)
+    {
+        return Set("HISTORY", false, "", comment);
+    }
+
+    void End()
+    {
+        Set("END");
+        while (fKeys.size()%36!=0)
+            fKeys.push_back(Key());
+    }
+
+    bool AddColumn(uint32_t cnt, char typechar, const string &name, const string &unit, const string &comment="")
+    {
+        if (tellp()<0)
+        {
+            ostringstream sout;
+            sout << "File not open... ignoring column '" << name << "'";
+#ifdef __EXCEPTIONS
+            throw runtime_error(sout.str());
+#else
+            gLog << ___err___ << "ERROR - " << sout.str() << endl;
+            return false;
+#endif
+        }
+
+        if (tellp()>0)
+        {
+            ostringstream sout;
+            sout << "Header already writtenm, no new column can be defined... ignoring column '" << name << "'";
+#ifdef __EXCEPTIONS
+            throw runtime_error(sout.str());
+#else
+            gLog << ___err___ << "ERROR - " << sout.str() << endl;
+            return false;
+#endif
+        }
+
+        if (findcol(name)!=fTable.cols.end())
+        {
+            ostringstream sout;
+            sout << "A column with the name '" << name << "' already exists.";
+#ifdef __EXCEPTIONS
+            throw runtime_error(sout.str());
+#else
+            gLog << ___err___ << "ERROR - " << sout.str() << endl;
+            return false;
+#endif
+        }
+
+        typechar = toupper(typechar);
+
+        static const string allow("LABIJKED");
+        if (std::find(allow.begin(), allow.end(), typechar)==allow.end())
+        {
+            ostringstream sout;
+            sout << "Column type '" << typechar << "' not supported.";
+#ifdef __EXCEPTIONS
+            throw runtime_error(sout.str());
+#else
+            gLog << ___err___ << "ERROR - " << sout.str() << endl;
+            return false;
+#endif
+        }
+
+        ostringstream type;
+        type << cnt << typechar;
+
+        fTable.num_cols++;
+
+        ostringstream typekey, formkey, unitkey, unitcom, typecom;
+        typekey << "TTYPE" << fTable.num_cols;
+        formkey << "TFORM" << fTable.num_cols;
+        unitkey << "TUNIT" << fTable.num_cols;
+        unitcom << "unit of " << name;
+
+        typecom << "format of " << name << " [";
+
+        switch (typechar)
+        {
+        case 'L': typecom << "1-byte BOOL]";  break;
+        case 'A': typecom << "1-byte CHAR]";  break;
+        case 'B': typecom << "1-byte BOOL]";  break;
+        case 'I': typecom << "2-byte INT]";   break;
+        case 'J': typecom << "4-byte INT]";   break;
+        case 'K': typecom << "8-byte INT]";   break;
+        case 'E': typecom << "4-byte FLOAT]"; break;
+        case 'D': typecom << "8-byte FLOAT]"; break;
+        }
+
+        SetStr(formkey.str(), type.str(), typecom.str());
+        SetStr(typekey.str(), name,       comment);
+
+        if (!unit.empty())
+            SetStr(unitkey.str(), unit, unitcom.str());
+
+        size_t size = 0;
+
+        switch (typechar)
+        {
+        case 'L': size = 1; break;
+        case 'A': size = 1; break;
+        case 'B': size = 1; break;
+        case 'I': size = 2; break;
+        case 'J': size = 4; break;
+        case 'K': size = 8; break;
+        case 'E': size = 4; break;
+        case 'D': size = 8; break;
+        }
+
+        Table::Column col;
+        col.name   = name;
+        col.type   = typechar;
+        col.num    = cnt;
+        col.size   = size;
+        col.offset = fTable.bytes_per_row;
+
+        fTable.cols.push_back(col);
+
+        fTable.bytes_per_row += size*cnt;
+
+        // Align to four bytes
+        fOutputBuffer.resize(fTable.bytes_per_row + 4-fTable.bytes_per_row%4);
+
+        return true;
+    }
+
+    bool AddColumnShort(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(cnt, 'I', name, unit, comment); }
+    bool AddColumnInt(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(cnt, 'J', name, unit, comment); }
+    bool AddColumnLong(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(cnt, 'K', name, unit, comment); }
+    bool AddColumnFloat(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(cnt, 'E', name, unit, comment); }
+    bool AddColumnDouble(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(cnt, 'D', name, unit, comment); }
+    bool AddColumnChar(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(cnt, 'A', name, unit, comment); }
+    bool AddColumnByte(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(cnt, 'B', name, unit, comment); }
+    bool AddColumnBool(uint32_t cnt, const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(cnt, 'L', name, unit, comment); }
+
+    bool AddColumnShort(const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(1, 'I', name, unit, comment); }
+    bool AddColumnInt(const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(1, 'J', name, unit, comment); }
+    bool AddColumnLong(const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(1, 'K', name, unit, comment); }
+    bool AddColumnFloat(const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(1, 'E', name, unit, comment); }
+    bool AddColumnDouble(const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(1, 'D', name, unit, comment); }
+    bool AddColumnChar(const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(1, 'A', name, unit, comment); }
+    bool AddColumnByte(const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(1, 'B', name, unit, comment); }
+    bool AddColumnBool(const string &name, const string &unit="", const string &comment="")
+    { return AddColumn(1, 'L', name, unit, comment); }
+
+    /*
+    bool AddKey(string key, double d, const string &comment)
+    {
+        ostringstream out;
+        out << d;
+
+        string s = out.str();
+
+        replace(s.begin(), s.end(), "e", "E");
+
+        return AddKey(key, s, comment);
+        }*/
+
+
+    Checksum WriteHeader(ofstream &fout)
+    {
+        Checksum sum;
+        for (auto it=fKeys.begin(); it!=fKeys.end(); it++)
+        {
+            it->Out(fout);
+            sum += it->checksum;
+        }
+        fout.flush();
+
+        return sum;
+    }
+
+    Checksum WriteHeader()
+    {
+        return WriteHeader(*this);
+    }
+
+    void FlushHeader()
+    {
+        const off_t pos = tellp();
+        WriteHeader();
+        seekp(pos);
+    }
+
+    Checksum WriteFitsHeader()
+    {
+        ofits h;
+
+        h.SetBool("SIMPLE", true, "file does conform to FITS standard");
+        h.SetInt ("BITPIX",    8, "number of bits per data pixel");
+        h.SetInt ("NAXIS",     0, "number of data axes");
+        h.SetBool("EXTEND", true, "FITS dataset may contain extensions");
+
+        h.AddComment("FITS (Flexible Image Transport System) format is defined in 'Astronomy");
+        h.AddComment("and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H");
+        h.End();
+
+        return h.WriteHeader(*this);
+    }
+
+    bool WriteTableHeader(const char *name="DATA")
+    {
+        if (tellp()>0)
+        {
+#ifdef __EXCEPTIONS
+            throw runtime_error("File not empty anymore.");
+#else
+            gLog << ___err___ << "ERROR - File not empty anymore." << endl;
+            return false;
+#endif
+        }
+
+        fHeaderSum = WriteFitsHeader();
+
+        SetStr("EXTNAME", name);
+        SetInt("NAXIS1",  fTable.bytes_per_row);
+        SetInt("TFIELDS", fTable.cols.size());
+
+        End();
+
+        WriteHeader();
+
+        return good();
+    }
+
+    template<size_t N>
+        void revcpy(char *dest, const char *src, int num)
+    {
+        const char *pend = src + num*N;
+        for (const char *ptr = src; ptr<pend; ptr+=N, dest+=N)
+            reverse_copy(ptr, ptr+N, dest);
+    }
+
+    uint32_t GetBytesPerRow() const { return fTable.bytes_per_row; }
+
+    bool WriteRow(const void *ptr, size_t cnt, bool byte_swap=true)
+    {
+        // FIXME: Make sure that header was already written
+        //        or write header now!
+        if (cnt!=fTable.bytes_per_row)
+        {
+            ostringstream sout;
+            sout << "WriteRow - Size " << cnt << " does not match expected size " << fTable.bytes_per_row;
+#ifdef __EXCEPTIONS
+            throw runtime_error(sout.str());
+#else
+            gLog << ___err___ << "ERROR - " << sout.str() << endl;
+            return false;
+#endif
+        }
+
+        // For the checksum we need everything to be correctly aligned
+        const uint8_t offset = fTable.offset%4;
+
+        char *buffer = fOutputBuffer.data() + offset;
+
+        auto ib = fOutputBuffer.begin();
+        auto ie = fOutputBuffer.end();
+        *ib++ = 0;
+        *ib++ = 0;
+        *ib++ = 0;
+        *ib   = 0;
+
+        *--ie = 0;
+        *--ie = 0;
+        *--ie = 0;
+        *--ie = 0;
+
+        if (!byte_swap)
+            memcpy(buffer, ptr, cnt);
+        else
+        {
+            for (auto it=fTable.cols.begin(); it!=fTable.cols.end(); it++)
+            {
+                const char *src  = reinterpret_cast<const char*>(ptr) + it->offset;
+                char       *dest = buffer + it->offset;
+
+                // Let the compiler do some optimization by
+                // knowing the we only have 1, 2, 4 and 8
+                switch (it->size)
+                {
+                case 1: memcpy   (dest, src, it->num*it->size); break;
+                case 2: revcpy<2>(dest, src, it->num);          break;
+                case 4: revcpy<4>(dest, src, it->num);          break;
+                case 8: revcpy<8>(dest, src, it->num);          break;
+                }
+            }
+        }
+
+        write(buffer, cnt);
+        fDataSum.add(fOutputBuffer);
+
+        fTable.num_rows++;
+        fTable.offset += cnt;
+        return good();
+    }
+
+    template<typename N>
+    bool WriteRow(const vector<N> &vec)
+    {
+        return WriteRow(vec.data(), vec.size()*sizeof(N));
+    }
+
+    // Flushes the number of rows to the header on disk
+    void FlushNumRows()
+    {
+        SetInt("NAXIS2", fTable.num_rows);
+        FlushHeader();
+    }
+
+    bool close()
+    {
+        if (tellp()<0)
+            return false;
+
+        if (tellp()%(80*36)>0)
+        {
+            //cout << "fill" << endl;
+            const vector<char> filler(80*36-tellp()%(80*36));
+            write(filler.data(), filler.size());
+        }
+
+        // We don't hav eto jump back to the end of the file
+        SetInt("NAXIS2",  fTable.num_rows);
+        SetInt("DATASUM", fDataSum.val());
+
+        const Checksum sum = WriteHeader();
+        //sum += headersum;
+
+        SetStr("CHECKSUM", "{"+(sum+fDataSum).str()+"}");
+
+        const Checksum chk = WriteHeader();
+
+        ofstream::close();
+
+        if ((chk+fDataSum).valid())
+            return true;
+
+        ostringstream sout;
+        sout << "Checksum (" << std::hex << chk.val() << ") invalid.";
+#ifdef __EXCEPTIONS
+        throw runtime_error(sout.str());
+#else
+        gLog << ___err___ << "ERROR - " << sout.str() << endl;
+        return false;
+#endif
+    }
+};
+
+}
+
+#if 0
+#include "fits.h"
+
+int main()
+{
+    using namespace std;
+
+    ofits h2("delme.fits");
+
+    h2.SetInt("KEY1", 1, "comment 1");
+    h2.AddColumnInt(2, "testcol1", "counts", "My comment");
+    h2.AddColumnInt("testcol2", "counts", "My comment");
+    //h2.AddColumnInt("testcol2", "counts", "My comment");
+    h2.SetInt("KEY2", 2, "comment 2");
+
+    /*
+     AddFloat("X0",           0.000123456, "number of fields in each row");
+     AddFloat("X1",           0, "number of fields in each row");
+     AddFloat("X2",      12345, "number of fields in each row");
+     AddFloat("X3",      123456.67890, "number of fields in each row");
+     AddFloat("X4", 1234567890123456789.12345678901234567890, "number of fields in each row");
+     AddFloat("X5", 1234567890.1234567890e20, "number of fields in each row");
+     AddFloat("X6", 1234567890.1234567890e-20, "number of fields in each row");
+     AddFloat("XB", 1234567890.1234567890e-111, "number of fields in each row");
+     AddFloat("X7", 1e-5, "number of fields in each row");
+     AddFloat("X8", 1e-6, "number of fields in each row");
+     //AddStr("12345678", "123456789012345678", "12345678901234567890123456789012345678901234567");
+     */
+    // -
+
+    h2.WriteTableHeader("TABLE_NAME");
+
+    for (int i=0; i<10; i++)
+    {
+        int j[3] = { i+10, i*10, i*100 };
+        h2.WriteRow(j, 3*sizeof(i));
+    }
+
+    //h2.AddColumnInt("testcol2", "counts", "My comment");
+    //h2.SetInt("KEY3", 2, "comment 2");
+    h2.SetInt("KEY2", 2, "comment 2xxx");
+    h2.SetInt("KEY1", 11);
+
+    h2.close();
+
+    cout << "---" << endl;
+
+    fits f("delme.fits");
+    if (!f)
+        throw runtime_error("xxx");
+
+    cout << "Header is valid: " << f.IsHeaderOk() << endl;
+
+    while (f.GetNextRow());
+
+    cout << "File   is valid: " << f.IsFileOk() << endl;
+
+    cout << "---" << endl;
+
+    return 0;
+}
+#endif
+
+#endif
