//
// Copyright (c) 2005 Mooffie <mooffie@typo.co.il>
//
// This script may not be installed on any server
// other than that contracted by Mooffie.

function prt(s) {
    E("output").value += s + "\n";
}

function dbg(s) {
    if (0)
	prt(s);
}

function _int(x) {
    return (x < 0) ? Math.ceil(x) : Math.floor(x);
}

function is_whole(x) {
    if (ISRAT(x))
	return x.n == 0 || x.d == 1 || x.n == x.d;
    else
	return x % 1 == 0;
}

function get_whole(x) {
    return ISRAT(x) ? _int(x.float_val()) : _int(x);
}

function FVAL(x) {
    return ISRAT(x) ? x.float_val() : x;
}

// get_neg() negates a number. A number may be a simple JavaScript
// number, or a Rational object.
function get_neg(x) {
    if (ISRAT(x)) {
	x = x.copy();
	x.neg();
	return x;
    }
    else if (ISIRR(x)) {
	// this line of code should never be reached...
	window.alert("BUG: How the hell did you get here?");
	return x;
    }
    else {
	return -x;
    }
}

// gcd() - Computes GCD using Euclid Algorithm

function gcd(x, y) {
    var z = 1;
    x = Math.abs(get_whole(x));
    y = Math.abs(get_whole(y));
    while (z) {
	z = x % y;
	x = y;
	y = z;
    }
    return x;
}

function gcd_mult(L) {
    var d = L[0];
    for (var i = 1; i < L.length; i++) {
	if (get_whole(L[i]) != 0)
	    d = gcd(d, L[i]);
    }
    return d;
}

//////////////////////////////////////////////////////////////////////////

// The Rational class represents a rational number of the
// form n/d (numerator/denominator).

function ISRAT(x) {
    return (typeof(x) == "object") && x.reduce;
}

function Rational(n, d)
{
    this.copy = Rational_copy;
    this.neg = Rational_neg;
    this.reduce = Rational_reduce;
    this.float_val = Rational_float_val;
    this.toString = Rational_toString;

    this.n = n;
    this.d = d;
    this.reduce();
}

// float_val() returns the value of this Rational object suitable for
// JavaScript's math operators.
function Rational_float_val() {
    return this.n / this.d;
}

// reduce() reduces a rational. E.g. 4/10 to 2/5.
function Rational_reduce() {
    if (this.n != 0) {
	var m = gcd(this.n, this. d);
	this.n /= m;
	this.d /= m;
    }
}

// Negates a rational number
function Rational_neg() {
    // for aesthetic reasons, first try to get rid of
    // an existing minus sign.
    if (this.n < 0)
	this.n = -this.n;
    else if (this.d < 0)
	this.d = -this.d;
    else
	this.n = -this.n;
}

function Rational_copy() {
    return new Rational(this.n, this.d);
}

function Rational_toString() {
    if (Math.abs(this.n) > Math.abs(this.d)) {
	// improper fraction
	var whole = get_whole(this.n / this.d);
	var fract = get_whole(this.n % this.d);
	if (fract == 0)
	    return "{" + whole + "}";
	else
	    return "{" + whole + " " + Math.abs(fract) + "/" + Math.abs(this.d) + "}";
    }
    else if (this.n == 0) {
	return "{0}";
    }
    else if (this.n == this.d) {
	return "{1}";
    }
    else {
	return "{" + this.n + "/" + this.d + "}";
    }
}

//////////////////////////////////////////////////////////////////////////

// The Irrational class represents an conjugal structure of the form:
//
//        a +/- b*sqrt(c)i
//        ----------------
//               d
//
// If the 'imag' property is true, this is a complex number and 'i' is
// printed in output.
//
// The irrational object is instantiated upon applying the quadratic
// formula.

function ISIRR(x) {
    return (typeof(x) == "object") && x.minimize_root;
}

function Irrational(n, d)
{
    this.minimize_root = Irrational_minimize_root;
    this.normalize = Irrational_normalize;
    this.toString = Irrational_toString;

    this.a = 0;
    this.b = 0;
    this.c = 0;
    this.d = 0;
    this.imag = false;
    this.plus_minus_outside = false;
}

// minimize_root() removes as much as possible from under the radical.
// E.g. sqrt(80) ==> 4*sqrt(5);
function Irrational_minimize_root() {
    if (is_whole(this.c)) {
	var n = 2;
	var nc;
	while (nc = n*n, nc <= this.c) {
	    if (is_whole(this.c / nc)) {
		this.b *= n;
		this.c /= nc;
	    } else {
		n++;
	    }
	}
    }
}

function Irrational_normalize() {
    if (this.c < 0) {
	this.c = -this.c;
	this.imag = true;
    }
    this.minimize_root();
    if (is_whole(this.d) && is_whole(this.a) && is_whole(this.b)) {
	// Reduce a, b and c.
	// e.g (3 + 6*a / 12) ==> (1 + 2*a / 4)
	var m = gcd_mult([this.d, this.a, this.b]);
	this.a /= m;
	this.b /= m;
	this.d /= m;
    }
    // if a is zero, the plus_minus sign is not printed above the
    // divisor line, so the outside code is responsible for printing it.
    this.plus_minus_outside = (this.a == 0);
}

function Irrational_toString() {
    // I'm using only ASCII here. The application may
    // change it to something more beautiful.
    var ch_radical    = "q";
    var ch_plus_minus = "+/-";
    var ch_imag       = "j";

    var s = "";
    if (this.c != 1)
	s = ch_radical + this.c;
    if (this.b != 1 || (this.c == 1 && !this.imag))
	s = this.b + s;
    if (this.imag)
	s += ch_imag;
    if (this.a != 0)
	s = this.a + " " + ch_plus_minus + " " + s;

    if (this.d != 1) {
	s = "(" + s + ")";
	s = s + "/" + this.d;
    }
    else {
	if (this.a != 0)
	    s = "(" + s + ")";
    }
    return s;
}


