/*
 * Complex.js:
 * This file defines a Complex class to represent complex numbers.
 * Recall that a complex number is the sum of a real number and an
 * imaginary number, and that the imaginary number i is the
 * square root of -1.
 */

/*
 * The first step in defining a class is defining the constructor
 * function of the class. This constructor should initialize any
 * instance properties of the object. These are the essential
 * "state variables" that make each instance of the class different.
 */
function Complex(real, imaginary) {
    this.re = real;       // The real part of the number
    this.im = imaginary;  // The imaginary part of the number
}

/*
 * The second step in defining a class is defining its instance
 * methods (and possibly other properties) in the prototype object
 * of the constructor. Any properties defined in this object will
 * be inherited by all instances of the class. Note that instance
 * methods operate implicitly on the this keyword. For many methods,
 * no other arguments are needed.
 */

// Return the magnitude of a complex number. This is defined
// as its distance from the origin (0,0) of the complex plane.
Complex.prototype.magnitude = function() {
    return Math.sqrt(this.re*this.re + this.im*this.im);
};

// Return a complex number that is the negative of this one.
Complex.prototype.negative = function() {
    return new Complex(-this.re, -this.im);
};

// Convert a Complex object to a string in a useful way.
// This is invoked when a Complex object is used as a string.
Complex.prototype.toString = function() {
    return "{" + this.re + "," + this.im + "}";
};

// Return the real portion of a complex number. This function
// is invoked when a Complex object is treated as a primitive value.
Complex.prototype.valueOf = function() { return this.re; }

/*
 * The third step in defining a class is to define class methods,
 * constants, and any needed class properties as properties of the
 * constructor function itself (instead of as properties of the
 * prototype object of the constructor). Note that class methods
 * do not use the this keyword: they operate only on their arguments.
 */

// Add two complex numbers and return the result.
Complex.add = function (a, b) {
    return new Complex(a.re + b.re, a.im + b.im);
};

// Subtract one complex number from another.
Complex.subtract = function (a, b) {
    return new Complex(a.re - b.re, a.im - b.im);
};

// Multiply two complex numbers and return the product.
Complex.multiply = function(a, b) {
    return new Complex(a.re * b.re - a.im * b.im,
                       a.re * b.im + a.im * b.re);
};


Complex.divide = function(a, b) {
    c = new Complex();
  var r, tmp;

  if( Math.abs(b.re) >= Math.abs(b.im) ) {
    r = b.im / b.re;
    tmp = b.re + r * b.im;
    c.re = (a.re + r * a.im) / tmp;
    c.im = (a.im - r * a.re) / tmp;
  } else {
    r = b.re / b.im;
    tmp = b.im + r * b.re;
    c.re = (a.re * r + a.im) / tmp;
    c.im = (a.im * r - a.re) / tmp;
  }
  return c;
};


Complex.tan = function(a) {

    c = new Complex();
    var R = a.re;
    var I = a.im;

    if ( Math.abs(I) < 1 ) {
	var D = Math.pow( Math.cos(R), 2.0 ) + Math.pow( Math.sinh(I), 2.0 );
	c.re = 0.5*Math.sin( 2*R)/D;
	c.im = 0.5*Math.sinh( 2*I )/D;
    }
    else {
      var u = Math.exp (-I);
      var C = 2 * u / ( 1 - Math.pow( u, 2.0 ) );
      var D = 1 + Math.pow( Math.cos(R), 2.0 ) * Math.pow( C, 2.0 );

      var S = Math.pow( C, 2.0 );
      var T = 1.0/Math.tanh(I);

      c.re = 0.5*Math.sin( 2*R )*S/D;
      c.im = T/D;
    }
    return c;
};

// Here are some useful predefined complex numbers.
// They are defined as class properties, where they can be used as
// "constants." (Note, though, that they are not actually read-only.)
Complex.zero = new Complex(0,0);
Complex.one = new Complex(1,0);
Complex.i = new Complex(0,1);

// extra 
// Add the sinh function to the Math object
function sinh(aValue) {
    var myTerm1 = Math.pow(Math.E, aValue);
    var myTerm2 = Math.pow(Math.E, -aValue);
   
    return (myTerm1-myTerm2)/2;
}
// Register the new function
Math.constructor.prototype.sinh = sinh;

// Add the cosh function to the Math object
function cosh(aValue) {
    var myTerm1 = Math.pow(Math.E, aValue);
    var myTerm2 = Math.pow(Math.E, -aValue);
   
    return (myTerm1+myTerm2)/2;
}
// Register the new function
Math.constructor.prototype.cosh = cosh;

function tanh(aValue) {
    return Math.sinh(aValue)/Math.cosh(aValue);
}
Math.constructor.prototype.tanh = tanh;
