//
// File: entry.h
// Entry class definition, for storing nonterminal-frequency entries.
//
// Copyright 2001, KUN
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// CVS ID: $Id: entry.h,v 1.21 2006/04/12 16:00:23 olafs Exp $
//
#ifndef ENTRY_H
#define ENTRY_H

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>
#include <math.h>
#include <stdio.h> // for snprintf()
#include <vector>
#include <iostream>
//#include <stream.h> // for dec(), obsolete

#include <lexicon.h>
#include "nonttab.h"

//------------------------------------------------------------------------------
// struct Entry
//
// Description:
// An entry is a nonterminal with a frequency. It is defined at
// a position specified with a line number, used for error reporting.
//------------------------------------------------------------------------------

struct Entry
{
    Entry();
    Entry(pNonterm nonterm, unsigned line_nr, long count);
    void print(ostream& os) const; 
    string to_string();

    pNonterm nonterm;
    union {
        long freq;
        Penalty penalty;
    } freqpen;
    unsigned line_nr;
    string string_rep;
};

inline Entry::Entry()
{
    nonterm = NULL;
    line_nr = 0;
    freqpen.freq = 1;
    string_rep = "";
}

inline
Entry::Entry(pNonterm p_nonterm, unsigned u_line_nr, long count)
{
    nonterm = p_nonterm;
    line_nr = u_line_nr;
    freqpen.freq = count;
    string_rep = "";
}

inline string Entry::to_string()
{
    if (string_rep.length() == 0) {
        string_rep = nonterm->to_string();

        //string_rep = string_rep + string(dec(freqpen.penalty));
	char cbuf[99];
	snprintf(cbuf, sizeof(cbuf), "%d", freqpen.penalty);
        string_rep = string_rep + string(cbuf);
    }

    return string_rep;
}

ostream&
operator <<(ostream& os, const Entry& e);

inline bool
operator <(const Entry& entry1, const Entry& entry2)
{
    if (*(entry1.nonterm) < *(entry2.nonterm)) {
        return true;
    } else if (*(entry2.nonterm) < *(entry1.nonterm)) {
        return false;
    } else if (entry1.freqpen.freq < entry2.freqpen.freq) {
        return true;
    } else {
        return !(entry1.freqpen.freq > entry2.freqpen.freq);
    }
}

inline bool
operator == (const Entry& entry1, const Entry& entry2)
{
    return (*(entry1.nonterm) == *(entry2.nonterm))
           && (entry1.freqpen.freq == entry2.freqpen.freq);
}

//------------------------------------------------------------------------------
// type EntryList
//
// Description:
// Associated with each lexeme is a list of entries.
// The list should be sorted in order to achieve better compression.
//------------------------------------------------------------------------------

typedef vector<Entry> EntryListBase;

class EntryList: public EntryListBase
{
    private:

        EntryList::iterator already_stored(iterator el);

    public:
        EntryList(const Entry& entry) { push_back(entry); }
        void join_entrylists(EntryList& entries);
        //inline void sort() { ::sort(begin(), end()); };
        void print(ostream& os) const;
        void calculate_penalties();

        string to_string();
};

inline bool
operator ==(const EntryList& el1, const EntryList& el2)
{
    if (el1.size() != el2.size()) {
        return false;
    }

    EntryList::const_iterator i1 = /*(Entry*)*/ el1.begin();
    EntryList::const_iterator i2 = /*(Entry*)*/ el2.begin();

    while (i1 != el1.end()) {
        if (!(*i1 == *i2)) {	// != operator not found
            return false;
        }
        ++i1;
        ++i2;
    }

    return true;
}

ostream& operator <<(ostream& os, const EntryList& entries);

inline void EntryList::calculate_penalties()
{
#if 0
    const double minuslog2 = - log10(2.0);
    double frqtot = 0.0;

    for (EntryList::iterator i = begin(); i != end(); ++i) {
	frqtot += (double)(i->freqpen.freq);
    }
    for (EntryList::iterator i = begin(); i != end(); ++i) {
        const double f = (double)(i->freqpen.freq);
        const double p = log10(f / frqtot) / minuslog2;
        i->freqpen.penalty = (Penalty) (p + 0.5);
    }
#endif
    for (EntryList::iterator i = begin(); i != end(); ++i) {
        i->freqpen.penalty = (Penalty) -i->freqpen.freq;
    }
}

#endif // ENTRY_H
