/*=============================================================================
 *     Copyright Rob Clark 2000.  All Rights Reserved.
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * $ProjectHeader: OSCRIPT 0.155 Fri, 20 Dec 2002 18:34:22 -0800 rclark $
 */


const var Math = java.lang.Math;

/**
 * A vector, as in a mathmatical vector.  Like an <code>Array</code>, but 
 * supports vector addition/subtraction and multiplication/division with
 * a scalar.
 * 
 * @param args...      a variable number of args, the elements of the vector
 */
public function Vector(args...) extends Array()
{
  for( var i=0; i<args.length(); i++ )
    this[i] = args[i];

  /*=======================================================================*/
  /**
   * Perform the "+" operation.
   * <pre>
   *     C  =   A  +  B
   *   C[n] = A[n] + B[n]
   * </pre>
   * 
   * @param val          the other value
   * @return a vector
   * @throws NoSuchMethodException
   */
  public function bopPlus(val)
  {
    if( val.length() != length() )
      return val.bopPlusR( this, new pkg.IllegalArgumentException("cannot add vectors of different size") );

    var result = new Vector();

    for( var i=length()-1; i>=0; i-- )
      result[i] = this[i] + val[i];

    return result;
  }
  public function bopPlusR( val, e )
  {
    if( val.length() != length() )
      throw e;

    var result = new Vector();

    for( var i=length()-1; i>=0; i-- )
      result[i] = val[i] + this[i];

    return result;
  }

  /*=======================================================================*/
  /**
   * Perform the "-" operation.
   * <pre>
   *     C  =   A  -  B
   *   C[n] = A[n] - B[n]
   * </pre>
   * 
   * @param val          the other value
   * @return a vector
   * @throws NoSuchMethodException
   */
  public function bopMinus(val)
  {
    if( val.length() != length() )
      return val.bopMinusR( this, new pkg.IllegalArgumentException("cannot subtract vectors of different size") );

    var result = new Vector();

    for( var i=length()-1; i>=0; i-- )
      result[i] = this[i] - val[i];

    return result;
  }
  public function bopMinusR( val, e )
  {
    if( val.length() != length() )
      throw e;

    var result = new Vector();

    for( var i=length()-1; i>=0; i-- )
      result[i] = val[i] - this[i];

    return result;
  }

  /*=======================================================================*/
  /**
   * Perform the "*" operation.  If <code>val</code> is a <code>Vector</code>,
   * this performs the cross-product operation (see <code>crossProduct</code>)
   * otherwise the performs scalar multiplication.
   * <pre>
   *     C  =   A * b
   *   C[n] = A[n] * b
   * </pre>
   * 
   * @param val          the other value
   * @return a vector or scalar
   * @throws NoSuchMethodException
   */
  public function bopMultiply(val)
  {
    if( val instanceof Array )
      return crossProduct(val);

    var result = new Vector();

    for( var i=length()-1; i>=0; i-- )
      result[i] = this[i] * val;

    return result;
  }
  public function bopMultiplyR( val, e )
  {
    if( val instanceof Array )
      return val.crossProduct(this);

    var result = new Vector();

    for( var i=length()-1; i>=0; i-- )
      result[i] = val * this[i];

    return result;
  }

  /*=======================================================================*/
  /**
   * Perform the "/" operation.  This performs scalar division.
   * <pre>
   *     C  =   A / b
   *   C[n] = A[n] / b
   * </pre>
   * 
   * @param val          the other value
   * @return a vector
   * @throws NoSuchMethodException
   */
  public function bopDivide(val)
  {
    var result = new Vector();

    for( var i=length()-1; i>=0; i-- )
      result[i] = this[i] / val;

    return result;
  }
  public function bopDivideR( val, e )
  {
    var result = new Vector();

    for( var i=length()-1; i>=0; i-- )
      result[i] = val / this[i];

    return result;
  }

  /*=======================================================================*/
  /**
   * Perform the dot-product operation.
   * <pre>
   *   c  =  A  .  B
   *   c  = A[1]*B[1] + ... + A[n]*B[n]
   * </pre>
   * 
   * @param val          the "B" value
   * @return a scalar
   */
  public function dotProduct(val)
  {
    if( val.length() != length() )
      throw new pkg.IllegalArgumentException("cannot compute dot product of vectors of different size");

    var result = 0;

    for( var i=length()-1; i>=0; i-- )
      result = this[i] * val[i] + result;    // += could force this[i] * val[i] to be ExactNumber

    return result;
  }

  /*=======================================================================*/
  /**
   * Perform the cross product operation on 3-dimensional vectors.
   * <pre>
   *    C   =  A  x  B
   *   C[0] = A[1]*B[2] - A[2]*B[1]
   *   C[1] = A[0]*B[2] - A[2]*B[0]
   *   C[2] = A[0]*B[1] - A[1]*B[0]
   * </pre>
   * 
   * @param val          the "B" value
   * @return a vector
   */
  public function crossProduct(val)
  {
    if( (length() != 3) || (val.length() != 3) )
      throw new pkg.IllegalArgumentException("cannot compute cross product for non-3d vectors");

    var result = new Vector();

    result[2] = (this[0] * val[1]) - (this[1] * val[0]);
    result[1] = (this[0] * val[2]) - (this[2] * val[0]);
    result[0] = (this[1] * val[2]) - (this[2] * val[1]);

    return result;
  }

  /*=======================================================================*/
  /**
   * Note that the <code>length</code> method of this class returns the
   * array length, ie the dimensions.  This method returns the euclidean
   * length:
   * <pre>
   *    |V| = sqrare_root( V[0]^2 + V[1]^2 + ... + V[n]^2 )
   * </pre>
   * 
   * @return a scalar number
   */
  public function euclideanLength()
  {
    var result = 0;

    for( var i=length()-1; i>=0; i-- )
      result += this[i] * this[i];

    return Math.sqrt(result);
  }

  /*=======================================================================*/
  /**
   * The normalized vector has the euclidean-length of 1
   * 
   * @return a normalized vector
   */
  public function normalize()
  {
    return this / euclideanLength();
  }
}



/*
 *   Local Variables:
 *   tab-width: 2
 *   indent-tabs-mode: nil
 *   mode: java
 *   c-indentation-style: java
 *   c-basic-offset: 2
 *   eval: (c-set-offset 'statement-cont '0)
 *   eval: (c-set-offset 'substatement-open '0)
 *   eval: (c-set-offset 'case-label '+)
 *   eval: (c-set-offset 'inclass '+)
 *   eval: (c-set-offset 'inline-open '0)
 *   End:
 */