/*******************************************************************/
/*      File: prob.h                                               */
/*    Author: Helmut Schmid                                        */
/*   Purpose: Logarithmic representation of probabilities to avoid */
/*            underflow problems                                   */
/*   Created: Tue Oct 29 10:01:36 2002                             */
/*  Modified: Wed Sep  5 14:08:30 2007 (schmid)                    */
/* Copyright: Institut fuer maschinelle Sprachverarbeitung         */
/*            Universitaet Stuttgart                               */
/*******************************************************************/

#ifndef PROB_H
#define PROB_H

#include <float.h>
#include <math.h>
#include <assert.h>

static const double LOG_ZERO = -DBL_MAX;
static const double MaxLogDiff = 45;
static const double MinLog = -745;


class Prob {

  double prob;

public:
  inline Prob( const double p ) {
    assert(p >= 0.0);
    if (p == 0.0)
      prob = LOG_ZERO;
    else if (p == 1.0)
      prob = 0.0;
    else
      prob = log(p);
  }
  inline Prob( const Prob &p ) {
    prob = p.prob;
  }
  inline Prob() {
    prob = LOG_ZERO;
  }
  inline Prob operator+(const Prob x) const;
  inline Prob operator-(const Prob x) const;
  inline Prob operator*(const Prob x) const;
  inline Prob operator*(const double x) const;
  inline Prob operator/(const Prob x) const;
  inline Prob operator+=(const Prob x);
  inline Prob operator-=(const Prob x);
  inline Prob operator*=(const Prob x);
  inline Prob operator*=(const double x);
  inline int  operator==(const Prob x) const;
  inline int  operator>(const Prob x) const;
  inline int  operator>=(const Prob x) const;
  inline int  operator<(const Prob x) const;
  inline int  operator<=(const Prob x) const;
  inline int  operator>(double p) const;
  inline int  operator>=(double p) const;
  inline int  operator<(double p) const;
  inline int  operator<=(double p) const;
  double log_val() const { return prob; };
  operator double() const { 
    //if (prob < MinLog) return 0.0;
    return exp(prob);
  }
};


inline Prob Prob::operator*(const Prob x) const
{
  Prob result; // constructor setzt result auf 0

  if (prob != LOG_ZERO && x.prob != LOG_ZERO)
    result.prob = prob + x.prob;
  return result;
}

inline Prob Prob::operator*(const double x) const
{
  Prob result; // constructor setzt result auf 0

  if (prob != LOG_ZERO && x != 0.0)
    result.prob = prob + log(x);
  return result;
}

inline Prob Prob::operator/(const Prob x) const
{
  Prob result;

  if (x.prob == LOG_ZERO) {
    assert(0);
    throw("division by zero");
  }
  else if (prob == LOG_ZERO)
    result.prob = LOG_ZERO;
  else
    result.prob = prob - x.prob;
  return result;
}

inline Prob Prob::operator+(const Prob x) const
{
  double base;
  Prob result;

  if (prob == LOG_ZERO)
    result.prob = x.prob;
  else if (x.prob == LOG_ZERO)
    result.prob = prob;
  else if (prob < x.prob - MaxLogDiff)
    result = x;
  else if (x.prob < prob - MaxLogDiff)
    result = *this;
  else {
    base = (prob < x.prob) ? prob : x.prob;
    result.prob = base + log(exp(prob-base) + exp(x.prob-base));
  }
  return result;
}

inline Prob Prob::operator-(const Prob x) const
{
  double base;
  Prob result;

  if (x.prob == LOG_ZERO)
    result.prob = prob;
  else if (prob < x.prob) {
    assert(0);
    throw("negative result of Prob subtraction");
  }
  else if (prob - MaxLogDiff > x.prob)
    result = *this;
  else {
    base = (prob < x.prob) ? prob : x.prob;
    result.prob = base + log(exp(prob-base) - exp(x.prob-base));
  }
  return result;
}

inline Prob Prob::operator+=(const Prob x)
{
  return (*this = *this + x);
}

inline Prob Prob::operator-=(const Prob x)
{
  return (*this = *this - x);
}

inline Prob Prob::operator*=(const Prob x)
{
  if (prob == LOG_ZERO)
    ;
  else if (x.prob == LOG_ZERO)
    prob = LOG_ZERO;
  else
    prob += x.prob;
  return *this;
}

inline Prob Prob::operator*=(const double x)
{
  if (prob == LOG_ZERO)
    ;
  else if (x == 0.0)
    prob = LOG_ZERO;
  else
    prob += log(x);
  return *this;
}

inline int Prob::operator==(Prob x) const
{
  return (prob == x.prob);
}

inline int Prob::operator>(Prob x) const
{
  return (prob > x.prob);
}

inline int Prob::operator>=(Prob x) const
{
  return (prob >= x.prob);
}

inline int Prob::operator<(Prob x) const
{
  return (prob < x.prob);
}

inline int Prob::operator<=(Prob x) const
{
  return (prob <= x.prob);
}


inline int Prob::operator>(double p) const
{
  if (p == 0.0)
    return prob > LOG_ZERO;
  return (prob > log(p));
}

inline int Prob::operator>=(double p) const
{
  if (p == 0.0)
    return true;
  return (prob >= log(p));
}

inline int Prob::operator<(double p) const
{
  if (p == 0.0)
    return false;
  return (prob < log(p));
}

inline int Prob::operator<=(double p) const
{
  if (p == 0.0)
    return prob == LOG_ZERO;
  return (prob <= log(p));
}


inline Prob operator+(const double f, const Prob x)
{
  return (Prob)f + x;
}

inline Prob operator*(const double f, const Prob x)
{
  return (Prob)f * x;
}

inline Prob operator/(const double f, const Prob x)
{
  return (Prob)f / x;
}

inline Prob operator==(const double f, const Prob x)
{
  return ((Prob)f == x);
}

inline Prob operator>(const double f, const Prob x)
{
  return ((Prob)f > x);
}

inline Prob operator<(const double f, const Prob x)
{
  return ((Prob)f < x);
}


inline double log(const Prob x)
{
  return x.log_val();
}

#endif //PROB_H
