001/*
002 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
003 * Copyright (C) 2006 - JScience (http://jscience.org/)
004 * All rights reserved.
005 * 
006 * Permission to use, copy, modify, and distribute this software is
007 * freely granted, provided that this notice is preserved.
008 */
009package org.jscience.mathematics.vector;
010
011import java.util.Comparator;
012import javolution.lang.Realtime;
013import javolution.lang.ValueType;
014import javolution.text.Text;
015import javolution.text.TextBuilder;
016import javolution.util.FastTable;
017
018import org.jscience.mathematics.structure.Field;
019import org.jscience.mathematics.structure.VectorSpace;
020
021/**
022 * <p> This class represents an immutable element of a vector space.</p>
023 * 
024 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
025 * @version 3.3, January 2, 2007
026 * @see <a href="http://en.wikipedia.org/wiki/Vector_space">
027 *      Wikipedia: Vector Space</a>
028 */
029public abstract class Vector<F extends Field<F>> 
030        implements VectorSpace<Vector<F>, F>, ValueType, Realtime {
031
032    /**
033     * Default constructor (for sub-classes).
034     */
035    protected Vector() {
036    }
037
038    /**
039     * Returns the number of elements  held by this vector.
040     *
041     * @return this vector dimension.
042     */
043    public abstract int getDimension();
044
045    /**
046     * Returns a single element from this vector.
047     *
048     * @param  i the element index (range [0..n[).
049     * @return the element at <code>i</code>.
050     * @throws IndexOutOfBoundsException <code>(i < 0) || (i >= size())</code>
051     */
052    public abstract F get(int i);
053
054    /**
055     * Returns the negation of this vector.
056     *
057     * @return <code>-this</code>.
058     */
059    public abstract Vector<F> opposite();
060
061    /**
062     * Returns the sum of this vector with the one specified.
063     *
064     * @param   that the vector to be added.
065     * @return  <code>this + that</code>.
066     * @throws  DimensionException is vectors dimensions are different.
067     */
068    public abstract Vector<F> plus(Vector<F> that);
069
070    /**
071     * Returns the difference between this vector and the one specified.
072     *
073     * @param  that the vector to be subtracted.
074     * @return <code>this - that</code>.
075     */
076    public Vector<F> minus(Vector<F> that) {
077        return this.plus(that.opposite());
078    }
079
080    /**
081     * Returns the product of this vector with the specified coefficient.
082     *
083     * @param  k the coefficient multiplier.
084     * @return <code>this · k</code>
085     */
086    public abstract Vector<F> times(F k);
087    
088    /**
089     * Returns the dot product of this vector with the one specified.
090     *
091     * @param  that the vector multiplier.
092     * @return <code>this · that</code>
093     * @throws DimensionException if <code>this.dimension() != that.dimension()</code>
094     * @see <a href="http://en.wikipedia.org/wiki/Dot_product">
095     *      Wikipedia: Dot Product</a>
096     */
097    public abstract F times(Vector<F> that);
098    
099    /**
100     * Returns the cross product of two 3-dimensional vectors.
101     *
102     * @param  that the vector multiplier.
103     * @return <code>this x that</code>
104     * @throws DimensionException if 
105     *         <code>(this.getDimension() != 3) && (that.getDimension() != 3)</code> 
106     */
107    public Vector<F> cross(Vector<F> that) {
108        if ((this.getDimension() != 3) || (that.getDimension() != 3))
109            throw new DimensionException(
110                    "The cross product of two vectors requires "
111                            + "3-dimensional vectors");
112        FastTable<F> elements = FastTable.newInstance();
113        elements.add((this.get(1).times(that.get(2))).plus((this.get(2).times(that
114                .get(1))).opposite()));
115        elements.add((this.get(2).times(that.get(0))).plus((this.get(0).times(that
116                .get(2))).opposite()));
117        elements.add((this.get(0).times(that.get(1))).plus((this.get(1).times(that
118                .get(0))).opposite()));
119        DenseVector<F> V = DenseVector.valueOf(elements);
120        FastTable.recycle(elements);
121        return V;
122    }
123
124    /**
125     * Returns the text representation of this vector.
126     *
127     * @return the text representation of this vector.
128     */
129    public Text toText() {
130        final int dimension = this.getDimension();
131        TextBuilder tmp = TextBuilder.newInstance();
132        tmp.append('{');
133        for (int i = 0; i < dimension; i++) {
134            tmp.append(get(i));
135            if (i != dimension - 1) {
136                tmp.append(", ");
137            }
138        }
139        tmp.append('}');
140        Text txt = tmp.toText();
141        TextBuilder.recycle(tmp); 
142        return txt;
143    }
144
145    /**
146     * Returns the text representation of this vector as a 
147     * <code>java.lang.String</code>.
148     * 
149     * @return <code>toText().toString()</code>
150     */
151    public final String toString() {
152        return toText().toString();
153    }
154
155    /**
156     * Indicates if this vector can be considered equals to the one 
157     * specified using the specified comparator when testing for 
158     * element equality. The specified comparator may allow for some 
159     * tolerance in the difference between the vector elements.
160     *
161     * @param  that the vector to compare for equality.
162     * @param  cmp the comparator to use when testing for element equality.
163     * @return <code>true</code> if this vector and the specified matrix are
164     *         both vector with equal elements according to the specified
165     *         comparator; <code>false</code> otherwise.
166     */
167    public boolean equals(Vector<F> that, Comparator<F> cmp) {
168        if (this == that)
169            return true;
170        final int dimension = this.getDimension();
171        if (that.getDimension() != dimension)
172            return false;
173        for (int i = dimension; --i >= 0;) {
174            if (cmp.compare(this.get(i), that.get(i)) != 0)
175                return false;
176        }
177        return true;
178    }
179
180    /**
181     * Indicates if this vector is equal to the object specified.
182     *
183     * @param  that the object to compare for equality.
184     * @return <code>true</code> if this vector and the specified object are
185     *         both vectors with equal elements; <code>false</code> otherwise.
186     */
187    public boolean equals(Object that) {
188        if (this == that)
189            return true;
190        if (!(that instanceof Vector))
191            return false;
192        final int dimension = this.getDimension();
193        Vector v = (Vector) that;
194        if (v.getDimension() != dimension)
195            return false;
196        for (int i = dimension; --i >= 0;) {
197            if (!this.get(i).equals(v.get(i)))
198                return false;
199        }
200        return true;
201    }
202
203    /**
204     * Returns a hash code value for this vector.
205     * Equals objects have equal hash codes.
206     *
207     * @return this vector hash code value.
208     * @see    #equals
209     */
210    public int hashCode() {
211        final int dimension = this.getDimension();
212        int code = 0;
213        for (int i = dimension; --i >= 0;) {
214            code += get(i).hashCode();
215        }
216        return code;
217    }
218    
219    /**
220     * Returns a copy of this vector 
221     * {@link javolution.context.AllocatorContext allocated} 
222     * by the calling thread (possibly on the stack).
223     *     
224     * @return an identical and independant copy of this matrix.
225     */
226    public abstract Vector<F> copy();
227    
228}