#include <iostream.h>

class Quantity; class Dimensions;

class Unit {
protected:
  char *_name;
  Dimensions *_dims;
  double _factor;
  // Units are chained on _dims->units.
  struct Unit *_next;
public:
  Unit() { }
  inline Unit(Dimensions& dims);
  virtual void print(ostream&, Quantity&) const = 0;
  // absolute() is one if we should consider the unit to be an
  // absolute measure, as opposed to relative.   You cannot
  // add two absoluete quantities (such as two dates).
  int absolute() { return difference() != NULL; }
  // The unit to use for the result when subtracting another absolute unit.
  virtual Unit* difference();
  double factor() const { return _factor; }
  Dimensions& dimensions() { return *_dims; }
  char* abbreviation() const;
};

class BaseUnit : public Unit {
    int _index;
    static int nextbase;
  public:
    BaseUnit(char*);
    BaseUnit(char*, Dimensions&);
    int index() { return _index; } // A unique number
    virtual void print(ostream&, Quantity&) const;
};

// A Dimensions object is a product/ratio of some number of BaseUnits.

class Dimensions {
  friend Unit;
  int size;  // Length of bases and exponents arrays.
  BaseUnit **bases; // sorted by BaseUnit::index().
  short *exponents;
  /* List of units having that Dimension ??? */
  struct Unit *units;
  static unsigned long hash(int count, BaseUnit **bases, short *exponents);
  static unsigned long Dimensions::hash();
  static Dimensions** lookup(int count, BaseUnit **bases, short *exponents);
public:
  Dimensions(int s, BaseUnit**b, short*e) { size=s; bases=b; exponents=e; }
  Unit* select(Unit& a, Unit& b, int op);
  static Dimensions* find(int count, BaseUnit **bases, short *exponents);
  int equal(int count, BaseUnit **bases, short *exponents);
  BaseUnit& base(int i) { return *bases[i]; }
  int exponent(int i) { return exponents[i]; }
  int count() { return size; }
};

inline int operator ==(Dimensions&x, Dimensions& y) { return &x == &y; }
inline int operator !=(Dimensions&x, Dimensions& y) { return &x != &y; }

class CompoundUnit : public Unit {
  public:
    virtual void print(ostream&, Quantity&) const;
    CompoundUnit(char*, double, Dimensions&);
};

Dimensions& LookupDimensions(int count, BaseUnit** bases, short **expoenents);

class Quantity
{
  double _factor;
  Unit *_unit;
 public:
  Quantity(Quantity& q) { _factor = q._factor; _unit = q._unit; }
  Quantity(double f, Unit& u) { _factor = f; _unit = &u; }
  Unit& unit() const { return *_unit; }
  double factor() const { return _factor; }
  Dimensions& dimensions() const { return _unit->dimensions(); }
  void print(ostream&) const;
  int absolute() const { return _unit->absolute(); }
};

extern Quantity operator+(const Quantity& a, const Quantity& b);
extern Quantity operator-(const Quantity& a);
extern Quantity operator-(const Quantity& a, const Quantity& b);
extern Quantity operator*(const Quantity& a, const Quantity& b);
extern Quantity operator/(const Quantity& a, const Quantity& b);
extern Quantity operator*(double a, const Quantity& b);
extern Quantity operator*(const Quantity& a, double b);

class DateUnitT : public Unit {
public:
  DateUnitT();
  virtual Unit* difference();
  virtual void print(ostream&, Quantity&) const;
};

inline Unit::Unit(Dimensions& dims)
{ 
  _dims = &dims;
   _next = dims.units;
   dims.units = this;
}

extern Dimensions NullDim;
extern Dimensions TimeDim;

extern CompoundUnit NullUnit;
extern DateUnitT DateUnit;
extern BaseUnit SecondsUnit;
extern BaseUnit MeterUnit;
extern BaseUnit GramUnit;
extern BaseUnit KelvinUnit;
extern BaseUnit Seconds;
extern CompoundUnit MinutesUnit;
extern CompoundUnit HoursUnit;
extern CompoundUnit YardUnit;
extern CompoundUnit FootUnit;
extern CompoundUnit InchUnit;
