/**
 * $Id: Vector3.java,v 1.8.4.1 2000/06/15 14:11:43 yamauchi Exp $
 *     (M\pi)^2 3 dimensional Vector class 
 *	Copyright 1999 By YAMAUCHI Hitoshi »³Æâ ÀÆ 
 *	@author YAMAUCHI Hitoshi »³Æâ ÀÆ
 *
 * original source Vector3.h, Vector3.cc
 */

package jp.ac.tohoku.is.archi.mpi2.util;

import  java.lang.Math;
import  java.lang.Double;
import  java.io.Serializable;
import  jp.ac.tohoku.is.archi.mpi2.util.IntVec3;

public final class Vector3 
implements Encodable
{
    /**
     * indicate 1st element of Vector3
     */
    public final static int X = 0; 
    /**
     * indicate 2nd element of Vector3
     */
    public final static int Y = 1;
    /**
     * indicate 3rd element of Vector3
     */
    public final static int Z = 2;

    // Number of vector elements of this class
    private final static int NElem = 3;

    // elements of this class
    private double   mE[];

    /**
     * Allocates a new Vector3 object
     */
    public final static Vector3 ZERO  = new Vector3(0.0, 0.0, 0.0);
    public final static Vector3 PLUS_INFTY = new Vector3(Double.MAX_VALUE, 
							 Double.MAX_VALUE, 
							 Double.MAX_VALUE);
    /**
     * Allocates a new Vector3 object
     */
    public Vector3()
    {
	this.mE = new double[NElem];
	this.set(0.0, 0.0, 0.0);
    }

    /**
     * Allocate a new Vector3 object and initialize by x, y, z
     * 
     * @param x 
     * @param y
     * @param z
     */
    public Vector3(double x, double y, double z)
    {
	this.mE = new double[NElem];
	this.set(x, y, z);
    }

    /**
     * Allocate a new Vector3 object and initialize by vec
     * vec.length must be 3
     *
     * @param ivec
     */
    public Vector3(double [] vec)
    {
	this.mE = new double[NElem];
	this.setByArray(vec);
    }

    /**
     * Allocate a new Vector3 object and initialize by other Vector3
     */
    public Vector3(Vector3 rhs)
    {
	this.mE = new double[NElem];
	this.mE[Vector3.X] = rhs.mE[Vector3.X];
	this.mE[Vector3.Y] = rhs.mE[Vector3.Y];
	this.mE[Vector3.Z] = rhs.mE[Vector3.Z];    	
    }


    /**
     * IntVec3 -> Vector3
     */
    public Vector3(IntVec3 rhs)
    {
	this.mE = new double[NElem];
	this.mE[Vector3.X] = (double)(rhs.get(IntVec3.X));
	this.mE[Vector3.Y] = (double)(rhs.get(IntVec3.Y));
	this.mE[Vector3.Z] = (double)(rhs.get(IntVec3.Z));    	
    }

    /**
     * set contents of this vector
     */
    public void set(double x, double y, double z)
    {
	this.mE[X] = x;
	this.mE[Y] = y;
	this.mE[Z] = z;    
    }

    public void set(Vector3 rhs)
    {
	this.mE[X] = rhs.mE[X];
	this.mE[Y] = rhs.mE[Y];
	this.mE[Z] = rhs.mE[Z];    
    }

    /**
     * get nth element of this vector
     */
    public double get(int nth)
    {
	return(this.mE[nth]);
    }

    /**
     * set vector elements with double array
     */
    public void setByArray(double [] v)
    {
	this.mE[Vector3.X] = v[Vector3.X];
	this.mE[Vector3.Y] = v[Vector3.Y];
	this.mE[Vector3.Z] = v[Vector3.Z];
    }

    /**
     * get vector elements as double array
     */
    public double [] getNewAsArray()
    {
	double [] ret = new double[Vector3.NElem];
	ret[Vector3.X] = this.mE[Vector3.X];
	ret[Vector3.Y] = this.mE[Vector3.Y];
	ret[Vector3.Z] = this.mE[Vector3.Z];
	return(ret);
    }

    /** 
     * plus operator
     */
    public Vector3 plusNew(Vector3 rhs)
    {
	Vector3 ret = new Vector3();
	ret.mE[Vector3.X] = this.mE[Vector3.X] + rhs.mE[Vector3.X];
	ret.mE[Vector3.Y] = this.mE[Vector3.Y] + rhs.mE[Vector3.Y];
	ret.mE[Vector3.Z] = this.mE[Vector3.Z] + rhs.mE[Vector3.Z];
	return(ret);	
    }
    public void plusHere(Vector3 rhs)
    {
	this.mE[Vector3.X] += rhs.mE[Vector3.X];
	this.mE[Vector3.Y] += rhs.mE[Vector3.Y];
	this.mE[Vector3.Z] += rhs.mE[Vector3.Z];
    }

    /** 
     * minus operator
     */
    public Vector3 minusNew(Vector3 rhs)
    {
	Vector3 ret = new Vector3();
	ret.mE[Vector3.X] = this.mE[Vector3.X] - rhs.mE[Vector3.X];
	ret.mE[Vector3.Y] = this.mE[Vector3.Y] - rhs.mE[Vector3.Y];
	ret.mE[Vector3.Z] = this.mE[Vector3.Z] - rhs.mE[Vector3.Z];
	return(ret);	
    }
    public void minusHere(Vector3 rhs)
    {
	this.mE[Vector3.X] -= rhs.mE[Vector3.X];
	this.mE[Vector3.Y] -= rhs.mE[Vector3.Y];
	this.mE[Vector3.Z] -= rhs.mE[Vector3.Z];
    }

    /** 
     * multiply vector each element
     */
    public Vector3 mulVecNew(Vector3 rhs)
    {
	return(new Vector3((this.mE[Vector3.X] * rhs.mE[Vector3.X]),
			   (this.mE[Vector3.Y] * rhs.mE[Vector3.Y]),
			   (this.mE[Vector3.Z] * rhs.mE[Vector3.Z])));
    }

    /** 
     * multiply vector each element in here
     */
    public void mulVecHere(Vector3 rhs)
    {
	this.mE[Vector3.X] *= rhs.mE[Vector3.X];
	this.mE[Vector3.Y] *= rhs.mE[Vector3.Y];
	this.mE[Vector3.Z] *= rhs.mE[Vector3.Z];
    }

    /** 
     * multiply with scaler value
     */
    public Vector3  mulScalerNew(double rhs)
    {
	return(new Vector3((this.mE[Vector3.X] * rhs),
			   (this.mE[Vector3.Y] * rhs),
			   (this.mE[Vector3.Z] * rhs)));
    }

    /** 
     * multiply with scaler value to here
     */
    public void mulScalerHere(double rhs)
    {
	this.mE[Vector3.X] *= rhs;
	this.mE[Vector3.Y] *= rhs;
	this.mE[Vector3.Z] *= rhs;
    }

    /** 
     * div with scaler value
     */
    public Vector3  divScalerNew(double rhs)
    {
	return(new Vector3((this.mE[Vector3.X] / rhs),
			   (this.mE[Vector3.Y] / rhs),
			   (this.mE[Vector3.Z] / rhs)));
    }
    public void divScalerHere(double rhs)
    {
	this.mE[Vector3.X] /= rhs;
	this.mE[Vector3.Y] /= rhs;
	this.mE[Vector3.Z] /= rhs;
    }

    /**
     * inner product
     */
    public double innPrd(Vector3 rhs)
    {
	return(this.mE[Vector3.X] * rhs.mE[Vector3.X] + 
	       this.mE[Vector3.Y] * rhs.mE[Vector3.Y] + 
	       this.mE[Vector3.Z] * rhs.mE[Vector3.Z]);
    }

    /**
     * outer product
     * 
     * @param rhs    
     */
    public Vector3 outPrdNew(Vector3 rhs)
    {
	return(new Vector3(this.mE[Vector3.Y] * rhs.mE[Vector3.Z] - 
			   this.mE[Vector3.Z] * rhs.mE[Vector3.Y],
			   
			   this.mE[Vector3.Z] * rhs.mE[Vector3.X] - 
			   this.mE[Vector3.X] * rhs.mE[Vector3.Z],
			   
			   this.mE[Vector3.X] * rhs.mE[Vector3.Y] - 
			   this.mE[Vector3.Y] * rhs.mE[Vector3.X])  );
    }

    
    /**
     * get norm of this vector
     * 
     * @return norm
     */
    public double getNorm() 
    {
	double d = Math.sqrt(this.mE[Vector3.X] * this.mE[Vector3.X] + 
			     this.mE[Vector3.Y] * this.mE[Vector3.Y] + 
			     this.mE[Vector3.Z] * this.mE[Vector3.Z]);
	return(d);
    }
	
    /**
     * normalize this vector
     */
    public void normalize()
    {
	double d = this.getNorm();
	if(d == 0.0)
	{
	    throw new ArithmeticException("Vector3: norm == 0, " +
					  "can not normalize");
	}
	this.mE[Vector3.X] /= d;
	this.mE[Vector3.Y] /= d;
	this.mE[Vector3.Z] /= d;
    }

    /**
     * compare with rhs value
     */
    public boolean equals(Object obj)
    {
	if(obj instanceof Vector3)
	{
	    Vector3 rhs = (Vector3)obj;
	    return((this.mE[Vector3.X] == rhs.mE[Vector3.X]) &&
		   (this.mE[Vector3.Y] == rhs.mE[Vector3.Y]) &&
		   (this.mE[Vector3.Z] == rhs.mE[Vector3.Z]));
	}else
	{
	    return(false);
	}
    }
    
    /**
     * convert to String representation of this class
     */
    public String toString()
    {
	return(Double.toString(this.mE[Vector3.X]) + "\t" + 
	       Double.toString(this.mE[Vector3.Y]) + "\t" + 
	       Double.toString(this.mE[Vector3.Z]));
    }

    // For Encodable interface (and ObjectDecoder).
    public int sizeInDouble() 
    {
	return(3);
    }
    public void encodeInDouble(double[] array, int offset) 
    {
	array[offset] 	  = mE[Vector3.X];
	array[offset + 1] = mE[Vector3.Y];
	array[offset + 2] = mE[Vector3.Z];
    }
    static class Vector3ObjectDecoder implements ObjectDecoder 
    {
    	public int sizeInDouble() 
	{
	    return 3;
	}
    	public Object decodeFromDouble(double[] array, int offset) 
	{
	    return(new Vector3(array[offset], 
			       array[offset + 1], 
			       array[offset + 2]));
	}
    }
    private final static ObjectDecoder odInstance = new Vector3ObjectDecoder();
    public static ObjectDecoder getObjectDecoder() 
    {
	return odInstance;
    }

    // test
    public static void main(String [] arg)
    {
	Vector3 a = new Vector3();
	System.out.println("Vector3 = " + a);
    }
}
