var ZERO = new Rational(0, 1);
var ONE = new Rational(1, 1);

function Rational (top, bot) {
  assert(bot >= 1);
  this.top = top;
  this.bot = bot;
}


Rational.prototype.half = function() {
  return _normalize(this.top, this.bot * 2);
};

Rational.prototype.twice = function() {
  return _normalize(this.top * 2, this.bot);
};

Rational.prototype.add = function(other) {
  var newTop = this.top * other.bot + this.bot * other.top;
  var newBottom = this.bot * other.bot;
  return _normalize(newTop, newBottom);
};

Rational.prototype.subtract = function(other) {
  var newTop = this.top * other.bot - this.bot * other.top;
  var newBottom = this.bot * other.bot;
  return _normalize(newTop, newBottom);
};

Rational.prototype.float = function() {
  return this.top / this.bot;
}

// normalizes only power of 2, e.g. 4/8 = 1/2, but 6/12 != 1/2
// this is because the denominator is always power of two
function _normalize (a,b) {
  var neg = 1;
  if (a < 0) {
    neg = -1;
    a = -a; 
  }
  assert ( b > 0);
  /*
  var min = (a < b) ? a : b;
  var bi = 2;
  while (bi < min) {
    bi += bi;
  }
  
  for (var i = bi; i >= 2; i/=2) {
    if (a % i == 0 && b % i == 0) {
      a/=i;
      b/=i;
    }
  }
  */
  while ((a & 0x1) == 0 && (b & 0x1) == 0) {
    a >>>= 1;
    b >>>= 1;
  }
  if (a == 0) {
    return ZERO;
  } else {
    return new Rational(a * neg, b);
  }
}

Rational.prototype.negate = function() {
  return new Rational(-this.top, this.bot);	
};


Rational.prototype.bigger = function(other) {
  return this.top * other.bot > other.top * this.bot;
}

Rational.prototype.smaller = function(other) {
  return this.top * other.bot < other.top * this.bot;
}

Rational.prototype.biggerFloat = function(f) {
  // top / bot > f
  return this.top > f * this.bot;
}

Rational.prototype.smallerFloat = function(f) {
  // top / bot < f
  return this.top < f * this.bot;
}

Rational.prototype.toString = function() {
  if (!this.str) {
    this.str = this.top + "/" + this.bot;
  }
  return this.str;
}

Rational.prototype.equals = function(other) {
  var a = _normalize(this.top, this.bot);
  var b = _normalize(other.top, other.bot);
  return (a.top === b.top && a.bot === b.bot);
};

toTest.push(Rational);


Rational.prototype.test = function() {

  assert(new Rational(-4, 8).equals(new Rational(-1, 2)));

  assert(new Rational(3, 16).half().equals(new Rational(3, 32)));
  assert(new Rational(4, 5).half().equals(new Rational(2, 5)));
  assert(new Rational(3, 16).twice().equals(new Rational(3, 8)));
  assert(new Rational(4, 5).twice().equals(new Rational(8, 5)));
  var r = new Rational(2, 7);
  assert(r.half().twice().equals(r));
  assert(r.twice().half().equals(r));
  
  assert(new Rational(4, 5).add(new Rational(2, 3)).equals(new Rational(22, 15)));
  assert(new Rational(4, 5).subtract(new Rational(2, 3)).equals(new Rational(2, 15)));
  assert(new Rational(2, 3).subtract(new Rational(2, 6)).subtract(new Rational(3, 9)).equals(ZERO));
  assert(new Rational(3, 2).negate().equals(new Rational(-3, 2)));
  assert(new Rational(-3, 4).negate().equals(new Rational(3, 4)));
  assert(r.negate().add(r).equals(ZERO));
  
  assert(new Rational(4, 5).bigger(new Rational(3, 4)));
  assert(new Rational(-1, 2).smaller(ZERO));
  
  assert(_normalize(8, 4).top == 2);
};
