//
// Fix.h : variable length fixed point data type 
//

#ifndef _Fix_h
#ifdef __GNUG__
#pragma once
#pragma interface
#endif
#define _Fix_h 1

#include <stream.h>
#include <std.h>
#include <stddef.h>
#include <Integer.h>

typedef unsigned short uint16;
typedef short int16;
typedef unsigned long  uint32;
typedef long int32;

#define _Fix_min_length 	1
#define _Fix_max_length		65535

#define _Fix_min_value		-1.0
#define _Fix_max_value		1.0

extern uint16 Fix_default_length;
extern int    Fix_default_print_width;

struct _Frep                    // internal Fix representation
{
  uint16 len;          		// length in bits
  uint16 siz;			// allocated storage
  int16 ref;          		// reference count
  uint16 s[1];			// start of ushort array represention
};

typedef struct _Frep* _Fix;

extern _Frep _Frep_0;
extern _Frep _Frep_m1;
extern _Frep _Frep_quotient_bump;

class Fix
{
  _Fix            rep;

		  Fix(_Fix);

  void		  unique();

public:
		  Fix();
                  Fix(Fix&);
		  Fix(double&);
                  Fix(int);
                  Fix(int, Fix&);
                  Fix(int, double&);
                  Fix(int, _Frep*);

                  ~Fix();

  Fix             operator =  (Fix&);
  Fix             operator =  (double&);

  friend int      operator == (Fix&, Fix& );
  friend int      operator != (Fix&, Fix&);

  friend int      operator <  (Fix&, Fix&);
  friend int      operator <= (Fix&, Fix&);
  friend int      operator >  (Fix&, Fix&);
  friend int      operator >= (Fix&, Fix&);

  Fix&            operator +  ();
  Fix             operator -  ();

  friend Fix      operator +  (Fix&, Fix&);
  friend Fix      operator -  (Fix&, Fix&);
  friend Fix      operator *  (Fix&, Fix&);
  friend Fix      operator /  (Fix&, Fix&);

  friend Fix      operator *  (Fix&, int);
  friend Fix      operator *  (int, Fix&);
  friend Fix      operator %  (Fix&, int);
  friend Fix      operator << (Fix&, int);
  friend Fix      operator >> (Fix&, int);

#ifdef __GNUG__
  friend Fix     operator <? (Fix&, Fix&); // min
  friend Fix     operator >? (Fix&, Fix&); // max
#endif

  Fix            operator += (Fix&);
  Fix            operator -= (Fix&);
  Fix            operator *= (Fix&);
  Fix            operator /= (Fix&);

  Fix            operator *= (int);
  Fix            operator %= (int);
  Fix            operator <<=(int);
  Fix            operator >>=(int);

  friend char*    Ftoa(Fix&, int width = Fix_default_print_width);
  friend Fix      atoF(const char*, int len = Fix_default_length);
  
  friend istream& operator >> (istream&, Fix&);
  friend ostream& operator << (ostream&, Fix&);

  // built-in functions
  friend Fix      abs(Fix);		// absolute value
  friend int      sgn(Fix&);		// -1, 0, +1
  friend Integer  mantissa(Fix&);	// integer representation
  friend double   value(Fix&);		// double value
  friend int      length(Fix&);		// field length
  friend void	  show(Fix&);		// show contents

  // error handlers
  void            error(const char* msg);		// error handler
  void            range_error(const char* msg);	// range error handler

  // internal class functions
  friend void	  mask(_Fix);
  friend int      compare(_Fix, _Fix = &_Frep_0);

  friend _Fix	  new_Fix(uint16);
  friend _Fix	  new_Fix(uint16, _Fix);
  friend _Fix	  new_Fix(uint16, double);

  friend _Fix	  copy(_Fix, _Fix);
  friend _Fix	  negate(_Fix, _Fix = NULL);
  friend _Fix	  add(_Fix, _Fix, _Fix = NULL);
  friend _Fix	  subtract(_Fix, _Fix, _Fix = NULL);
  friend _Fix	  multiply(_Fix, _Fix, _Fix = NULL);
  friend _Fix	  multiply(_Fix, int, _Fix = NULL);
  friend _Fix	  divide(_Fix, _Fix, _Fix = NULL, _Fix = NULL);
  friend _Fix	  shift(_Fix, int, _Fix = NULL);

  // non-operator versions for user
  friend void	  negate(Fix& x, Fix& r);
  friend void	  add(Fix& x, Fix& y, Fix& r);
  friend void	  subtract(Fix& x, Fix& y, Fix& r);
  friend void	  multiply(Fix& x, Fix& y, Fix& r);
  friend void	  divide(Fix& x, Fix& y, Fix& q, Fix& r);
  friend void	  shift(Fix& x, int y, Fix& r);
};

// error handlers

extern void	
  default_Fix_error_handler(const char*),
  default_Fix_range_error_handler(const char*);

extern one_arg_error_handler_t 
  Fix_error_handler,
  Fix_range_error_handler;

extern one_arg_error_handler_t 
  set_Fix_error_handler(one_arg_error_handler_t f),
  set_Fix_range_error_handler(one_arg_error_handler_t f);

typedef void (*Fix_peh)(_Fix&);
extern Fix_peh Fix_overflow_handler;

extern void 
  Fix_overflow_saturate(_Fix&),
  Fix_overflow_wrap(_Fix&),
  Fix_overflow_warning_saturate(_Fix&),
  Fix_overflow_warning(_Fix&),
  Fix_overflow_error(_Fix&);

extern Fix_peh set_overflow_handler(Fix_peh);

extern int Fix_set_default_length(int);

// function definitions

#if defined(__OPTIMIZE__) || defined(USE_LIBGXX_INLINES)

inline void Fix::unique()
{
  if ( rep->ref > 1 )
  {
    rep->ref--;
    rep = new_Fix(rep->len,rep);
  }
}

inline void mask (_Fix x)
{
  int n = x->len & 0x0f;
  if ( n )
    x->s[x->siz - 1] &= 0xffff0000 >> n; 
}

inline _Fix copy(_Fix from, _Fix to)
{
  uint16 *ts = to->s, *fs = from->s;
  int ilim = to->siz < from->siz ? to->siz : from->siz;
  for ( int i=0; i < ilim; i++ )
    *ts++ = *fs++;
  for ( ; i < to->siz; i++ )
    *ts++ = 0;
  mask(to);
  return to;
}

inline Fix::Fix(_Fix f)
{
  rep = f;
}

inline Fix::Fix()
{
  rep = new_Fix(Fix_default_length);
}

inline Fix::Fix(int len)
{
  if ( len < _Fix_min_length || len > _Fix_max_length )
    error("illegal length in declaration");
  rep = new_Fix((uint16 )len);
}

inline Fix::Fix(double& d)
{
  rep = new_Fix(Fix_default_length,d);
}

inline Fix::Fix(Fix&  y)
{
  rep = y.rep; rep->ref++;
}

inline Fix::Fix(int len, Fix&  y)
{
  if ( len < _Fix_min_length || len > _Fix_max_length )
    error("illegal length in declaration");
  rep = new_Fix((uint16 )len,y.rep);
}

inline Fix::Fix(int len, _Frep* fr)
{
  if ( len < 	1  || len > 	65535  )
    error("illegal length in declaration");
  rep = new_Fix((uint16 )len,fr);
}

inline Fix::Fix(int len, double& d)
{
  if ( len < _Fix_min_length || len > _Fix_max_length )
    error("illegal length in declaration");
  rep = new_Fix((uint16 )len,d);
}

inline Fix::~Fix()
{
  if ( --rep->ref <= 0 ) delete rep;
}

inline Fix  Fix::operator = (Fix&  y)
{
  if ( rep->len == y.rep->len ) {
    ++y.rep->ref;
    if ( --rep->ref <= 0 ) delete rep;
    rep = y.rep; 
  }
  else {
    unique();
    copy(y.rep,rep);
  }
  return *this;
}

inline Fix  Fix::operator = (double& d)
{
  int oldlen = rep->len;
  if ( --rep->ref <= 0 ) delete rep;
  rep = new_Fix(oldlen,d);
  return *this;
}

inline int operator == (Fix&  x, Fix&  y)
{
  return compare(x.rep, y.rep) == 0; 
}

inline int operator != (Fix&  x, Fix&  y)
{
  return compare(x.rep, y.rep) != 0; 
}

inline int operator <  (Fix&  x, Fix&  y)
{
  return compare(x.rep, y.rep) <  0; 
}

inline int operator <= (Fix&  x, Fix&  y)
{
  return compare(x.rep, y.rep) <= 0; 
}

inline int operator >  (Fix&  x, Fix&  y)
{
  return compare(x.rep, y.rep) >  0; 
}

inline int operator >= (Fix&  x, Fix&  y)
{
  return compare(x.rep, y.rep) >= 0; 
}

inline Fix& Fix::operator +  ()
{
  return *this;
}

inline Fix Fix::operator -  ()
{
  _Fix r = negate(rep); return r;
}

inline Fix      operator +  (Fix&  x, Fix& y)
{
  _Fix r = add(x.rep, y.rep); return r;
}

inline Fix      operator -  (Fix&  x, Fix& y)
{
  _Fix r = subtract(x.rep, y.rep); return r;
}

inline Fix      operator *  (Fix&  x, Fix& y)
{
  _Fix r = multiply(x.rep, y.rep); return r;
}

inline Fix      operator *  (Fix&  x, int y)
{
  _Fix r = multiply(x.rep, y); return r;
}

inline Fix      operator *  (int  y, Fix& x)
{
  _Fix r = multiply(x.rep, y); return r;
}

inline Fix operator / (Fix& x, Fix& y)
{
  _Fix r = divide(x.rep, y.rep); return r;
}

inline Fix  Fix::operator += (Fix& y)
{
  unique(); add(rep, y.rep, rep); return *this;
}

inline Fix  Fix::operator -= (Fix& y)
{
  unique(); subtract(rep, y.rep, rep); return *this;
}

inline Fix  Fix::operator *= (Fix& y)
{
  unique(); multiply(rep, y.rep, rep); return *this;
}

inline Fix  Fix::operator *= (int y)
{
  unique(); multiply(rep, y, rep); return *this;
}

inline Fix Fix::operator /= (Fix& y)
{
  unique(); divide(rep, y.rep, rep); return *this;
}

inline Fix operator % (Fix& x, int y)
{
  Fix r((int )x.rep->len + y, x); return r;
}

inline Fix      operator << (Fix&  x, int y)
{
  _Fix rep = shift(x.rep, y); return rep;
}

inline Fix      operator >> (Fix&  x, int y)
{  
  _Fix rep = shift(x.rep, -y); return rep;
}

inline Fix Fix::operator <<= (int y)
{
  unique(); shift(rep, y, rep); return *this;
}

inline Fix  Fix::operator >>= (int y)
{
  unique(); shift(rep, -y, rep); return *this;
}

#ifdef __GNUG__
inline Fix operator <? (Fix& x, Fix& y)
{
  if ( compare(x.rep, y.rep) <= 0 ) return x; else return y;
}

inline Fix operator >? (Fix& x, Fix& y)
{
  if ( compare(x.rep, y.rep) >= 0 ) return x; else return y;
}
#endif

inline Fix abs(Fix  x)
{
  _Fix r = (compare(x.rep) >= 0 ? new_Fix(x.rep->len,x.rep) : negate(x.rep));
  return r;
}

inline int sgn(Fix& x)
{
  int a = compare(x.rep);
  return a == 0 ? 0 : (a > 0 ? 1 : -1);
}

inline int length(Fix& x)
{
  return x.rep->len;
}

inline ostream& operator << (ostream& s, Fix& y)
{
  return s << Ftoa(y);
}

inline void	negate (Fix& x, Fix& r)
{
  negate(x.rep, r.rep);
}

inline void	add (Fix& x, Fix& y, Fix& r)
{
  add(x.rep, y.rep, r.rep);
}

inline void	subtract (Fix& x, Fix& y, Fix& r)
{
  subtract(x.rep, y.rep, r.rep);
}

inline void	multiply (Fix& x, Fix& y, Fix& r)
{
  multiply(x.rep, y.rep, r.rep);
}

inline void	divide (Fix& x, Fix& y, Fix& q, Fix& r)
{
  divide(x.rep, y.rep, q.rep, r.rep);
}

inline void	shift (Fix& x, int y, Fix& r)
{
  shift(x.rep, y, r.rep);
}

#endif
#endif
