001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math3.linear;
018
019import java.io.Serializable;
020
021import org.apache.commons.math3.Field;
022import org.apache.commons.math3.FieldElement;
023import org.apache.commons.math3.exception.DimensionMismatchException;
024import org.apache.commons.math3.exception.MathArithmeticException;
025import org.apache.commons.math3.exception.NotPositiveException;
026import org.apache.commons.math3.exception.NullArgumentException;
027import org.apache.commons.math3.exception.NumberIsTooSmallException;
028import org.apache.commons.math3.exception.OutOfRangeException;
029import org.apache.commons.math3.exception.util.LocalizedFormats;
030import org.apache.commons.math3.util.MathArrays;
031import org.apache.commons.math3.util.MathUtils;
032import org.apache.commons.math3.util.OpenIntToFieldHashMap;
033
034/**
035 * This class implements the {@link FieldVector} interface with a {@link OpenIntToFieldHashMap} backing store.
036 * <p>
037 *  Caveat: This implementation assumes that, for any {@code x},
038 *  the equality {@code x * 0d == 0d} holds. But it is is not true for
039 *  {@code NaN}. Moreover, zero entries will lose their sign.
040 *  Some operations (that involve {@code NaN} and/or infinities) may
041 *  thus give incorrect results.
042 * </p>
043 * @param <T> the type of the field elements
044 * @since 2.0
045 */
046public class SparseFieldVector<T extends FieldElement<T>> implements FieldVector<T>, Serializable {
047    /**  Serialization identifier. */
048    private static final long serialVersionUID = 7841233292190413362L;
049    /** Field to which the elements belong. */
050    private final Field<T> field;
051    /** Entries of the vector. */
052    private final OpenIntToFieldHashMap<T> entries;
053    /** Dimension of the vector. */
054    private final int virtualSize;
055
056    /**
057     * Build a 0-length vector.
058     * Zero-length vectors may be used to initialize construction of vectors
059     * by data gathering. We start with zero-length and use either the {@link
060     * #SparseFieldVector(SparseFieldVector, int)} constructor
061     * or one of the {@code append} method ({@link #append(FieldVector)} or
062     * {@link #append(SparseFieldVector)}) to gather data into this vector.
063     *
064     * @param field Field to which the elements belong.
065     */
066    public SparseFieldVector(Field<T> field) {
067        this(field, 0);
068    }
069
070
071    /**
072     * Construct a vector of zeroes.
073     *
074     * @param field Field to which the elements belong.
075     * @param dimension Size of the vector.
076     */
077    public SparseFieldVector(Field<T> field, int dimension) {
078        this.field = field;
079        virtualSize = dimension;
080        entries = new OpenIntToFieldHashMap<T>(field);
081    }
082
083    /**
084     * Build a resized vector, for use with append.
085     *
086     * @param v Original vector
087     * @param resize Amount to add.
088     */
089    protected SparseFieldVector(SparseFieldVector<T> v, int resize) {
090        field = v.field;
091        virtualSize = v.getDimension() + resize;
092        entries = new OpenIntToFieldHashMap<T>(v.entries);
093    }
094
095
096    /**
097     * Build a vector with known the sparseness (for advanced use only).
098     *
099     * @param field Field to which the elements belong.
100     * @param dimension Size of the vector.
101     * @param expectedSize Expected number of non-zero entries.
102     */
103    public SparseFieldVector(Field<T> field, int dimension, int expectedSize) {
104        this.field = field;
105        virtualSize = dimension;
106        entries = new OpenIntToFieldHashMap<T>(field,expectedSize);
107    }
108
109    /**
110     * Create from a Field array.
111     * Only non-zero entries will be stored.
112     *
113     * @param field Field to which the elements belong.
114     * @param values Set of values to create from.
115     * @exception NullArgumentException if values is null
116     */
117    public SparseFieldVector(Field<T> field, T[] values) throws NullArgumentException {
118        MathUtils.checkNotNull(values);
119        this.field = field;
120        virtualSize = values.length;
121        entries = new OpenIntToFieldHashMap<T>(field);
122        for (int key = 0; key < values.length; key++) {
123            T value = values[key];
124            entries.put(key, value);
125        }
126    }
127
128    /**
129     * Copy constructor.
130     *
131     * @param v Instance to copy.
132     */
133    public SparseFieldVector(SparseFieldVector<T> v) {
134        field = v.field;
135        virtualSize = v.getDimension();
136        entries = new OpenIntToFieldHashMap<T>(v.getEntries());
137    }
138
139    /**
140     * Get the entries of this instance.
141     *
142     * @return the entries of this instance
143     */
144    private OpenIntToFieldHashMap<T> getEntries() {
145        return entries;
146    }
147
148    /**
149     * Optimized method to add sparse vectors.
150     *
151     * @param v Vector to add.
152     * @return {@code this + v}.
153     * @throws DimensionMismatchException if {@code v} is not the same size as
154     * {@code this}.
155     */
156    public FieldVector<T> add(SparseFieldVector<T> v)
157        throws DimensionMismatchException {
158        checkVectorDimensions(v.getDimension());
159        SparseFieldVector<T> res = (SparseFieldVector<T>)copy();
160        OpenIntToFieldHashMap<T>.Iterator iter = v.getEntries().iterator();
161        while (iter.hasNext()) {
162            iter.advance();
163            int key = iter.key();
164            T value = iter.value();
165            if (entries.containsKey(key)) {
166                res.setEntry(key, entries.get(key).add(value));
167            } else {
168                res.setEntry(key, value);
169            }
170        }
171        return res;
172
173    }
174
175    /**
176     * Construct a vector by appending a vector to this vector.
177     *
178     * @param v Vector to append to this one.
179     * @return a new vector.
180     */
181    public FieldVector<T> append(SparseFieldVector<T> v) {
182        SparseFieldVector<T> res = new SparseFieldVector<T>(this, v.getDimension());
183        OpenIntToFieldHashMap<T>.Iterator iter = v.entries.iterator();
184        while (iter.hasNext()) {
185            iter.advance();
186            res.setEntry(iter.key() + virtualSize, iter.value());
187        }
188        return res;
189    }
190
191    /** {@inheritDoc} */
192    public FieldVector<T> append(FieldVector<T> v) {
193        if (v instanceof SparseFieldVector<?>) {
194            return append((SparseFieldVector<T>) v);
195        } else {
196            final int n = v.getDimension();
197            FieldVector<T> res = new SparseFieldVector<T>(this, n);
198            for (int i = 0; i < n; i++) {
199                res.setEntry(i + virtualSize, v.getEntry(i));
200            }
201            return res;
202        }
203    }
204
205    /** {@inheritDoc}
206     * @exception NullArgumentException if d is null
207     */
208    public FieldVector<T> append(T d) throws NullArgumentException {
209        MathUtils.checkNotNull(d);
210        FieldVector<T> res = new SparseFieldVector<T>(this, 1);
211        res.setEntry(virtualSize, d);
212        return res;
213     }
214
215    /** {@inheritDoc} */
216    public FieldVector<T> copy() {
217        return new SparseFieldVector<T>(this);
218    }
219
220    /** {@inheritDoc} */
221    public T dotProduct(FieldVector<T> v) throws DimensionMismatchException {
222        checkVectorDimensions(v.getDimension());
223        T res = field.getZero();
224        OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
225        while (iter.hasNext()) {
226            iter.advance();
227            res = res.add(v.getEntry(iter.key()).multiply(iter.value()));
228        }
229        return res;
230    }
231
232    /** {@inheritDoc} */
233    public FieldVector<T> ebeDivide(FieldVector<T> v)
234        throws DimensionMismatchException, MathArithmeticException {
235        checkVectorDimensions(v.getDimension());
236        SparseFieldVector<T> res = new SparseFieldVector<T>(this);
237        OpenIntToFieldHashMap<T>.Iterator iter = res.entries.iterator();
238        while (iter.hasNext()) {
239            iter.advance();
240            res.setEntry(iter.key(), iter.value().divide(v.getEntry(iter.key())));
241        }
242        return res;
243    }
244
245    /** {@inheritDoc} */
246    public FieldVector<T> ebeMultiply(FieldVector<T> v)
247        throws DimensionMismatchException {
248        checkVectorDimensions(v.getDimension());
249        SparseFieldVector<T> res = new SparseFieldVector<T>(this);
250        OpenIntToFieldHashMap<T>.Iterator iter = res.entries.iterator();
251        while (iter.hasNext()) {
252            iter.advance();
253            res.setEntry(iter.key(), iter.value().multiply(v.getEntry(iter.key())));
254        }
255        return res;
256    }
257
258    /**
259     * {@inheritDoc}
260     *
261     * @deprecated as of 3.1, to be removed in 4.0. Please use the {@link #toArray()} method instead.
262     */
263    @Deprecated
264    public T[] getData() {
265        return toArray();
266    }
267
268    /** {@inheritDoc} */
269    public int getDimension() {
270        return virtualSize;
271    }
272
273    /** {@inheritDoc} */
274    public T getEntry(int index) throws OutOfRangeException {
275        checkIndex(index);
276        return entries.get(index);
277   }
278
279    /** {@inheritDoc} */
280    public Field<T> getField() {
281        return field;
282    }
283
284    /** {@inheritDoc} */
285    public FieldVector<T> getSubVector(int index, int n)
286        throws OutOfRangeException, NotPositiveException {
287        if (n < 0) {
288            throw new NotPositiveException(LocalizedFormats.NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE, n);
289        }
290        checkIndex(index);
291        checkIndex(index + n - 1);
292        SparseFieldVector<T> res = new SparseFieldVector<T>(field,n);
293        int end = index + n;
294        OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
295        while (iter.hasNext()) {
296            iter.advance();
297            int key = iter.key();
298            if (key >= index && key < end) {
299                res.setEntry(key - index, iter.value());
300            }
301        }
302        return res;
303    }
304
305    /** {@inheritDoc} */
306    public FieldVector<T> mapAdd(T d) throws NullArgumentException {
307        return copy().mapAddToSelf(d);
308    }
309
310    /** {@inheritDoc} */
311    public FieldVector<T> mapAddToSelf(T d) throws NullArgumentException {
312        for (int i = 0; i < virtualSize; i++) {
313            setEntry(i, getEntry(i).add(d));
314        }
315        return this;
316    }
317
318    /** {@inheritDoc} */
319    public FieldVector<T> mapDivide(T d)
320        throws NullArgumentException, MathArithmeticException {
321        return copy().mapDivideToSelf(d);
322    }
323
324    /** {@inheritDoc} */
325    public FieldVector<T> mapDivideToSelf(T d)
326        throws NullArgumentException, MathArithmeticException {
327        OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
328        while (iter.hasNext()) {
329            iter.advance();
330            entries.put(iter.key(), iter.value().divide(d));
331        }
332        return this;
333    }
334
335    /** {@inheritDoc} */
336    public FieldVector<T> mapInv() throws MathArithmeticException {
337        return copy().mapInvToSelf();
338    }
339
340    /** {@inheritDoc} */
341    public FieldVector<T> mapInvToSelf() throws MathArithmeticException {
342        for (int i = 0; i < virtualSize; i++) {
343            setEntry(i, field.getOne().divide(getEntry(i)));
344        }
345        return this;
346    }
347
348    /** {@inheritDoc} */
349    public FieldVector<T> mapMultiply(T d) throws NullArgumentException {
350        return copy().mapMultiplyToSelf(d);
351    }
352
353    /** {@inheritDoc} */
354    public FieldVector<T> mapMultiplyToSelf(T d) throws NullArgumentException {
355        OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
356        while (iter.hasNext()) {
357            iter.advance();
358            entries.put(iter.key(), iter.value().multiply(d));
359        }
360        return this;
361    }
362
363    /** {@inheritDoc} */
364    public FieldVector<T> mapSubtract(T d) throws NullArgumentException {
365        return copy().mapSubtractToSelf(d);
366    }
367
368    /** {@inheritDoc} */
369    public FieldVector<T> mapSubtractToSelf(T d) throws NullArgumentException {
370        return mapAddToSelf(field.getZero().subtract(d));
371    }
372
373    /**
374     * Optimized method to compute outer product when both vectors are sparse.
375     * @param v vector with which outer product should be computed
376     * @return the matrix outer product between instance and v
377     */
378    public FieldMatrix<T> outerProduct(SparseFieldVector<T> v) {
379        final int n = v.getDimension();
380        SparseFieldMatrix<T> res = new SparseFieldMatrix<T>(field, virtualSize, n);
381        OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
382        while (iter.hasNext()) {
383            iter.advance();
384            OpenIntToFieldHashMap<T>.Iterator iter2 = v.entries.iterator();
385            while (iter2.hasNext()) {
386                iter2.advance();
387                res.setEntry(iter.key(), iter2.key(), iter.value().multiply(iter2.value()));
388            }
389        }
390        return res;
391    }
392
393    /** {@inheritDoc} */
394    public FieldMatrix<T> outerProduct(FieldVector<T> v) {
395        if (v instanceof SparseFieldVector<?>) {
396            return outerProduct((SparseFieldVector<T>)v);
397        } else {
398            final int n = v.getDimension();
399            FieldMatrix<T> res = new SparseFieldMatrix<T>(field, virtualSize, n);
400            OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
401            while (iter.hasNext()) {
402                iter.advance();
403                int row = iter.key();
404                FieldElement<T>value = iter.value();
405                for (int col = 0; col < n; col++) {
406                    res.setEntry(row, col, value.multiply(v.getEntry(col)));
407                }
408            }
409            return res;
410        }
411    }
412
413    /** {@inheritDoc} */
414    public FieldVector<T> projection(FieldVector<T> v)
415        throws DimensionMismatchException, MathArithmeticException {
416        checkVectorDimensions(v.getDimension());
417        return v.mapMultiply(dotProduct(v).divide(v.dotProduct(v)));
418    }
419
420    /** {@inheritDoc}
421     * @exception NullArgumentException if value is null
422     */
423    public void set(T value) {
424        MathUtils.checkNotNull(value);
425        for (int i = 0; i < virtualSize; i++) {
426            setEntry(i, value);
427        }
428    }
429
430    /** {@inheritDoc}
431     * @exception NullArgumentException if value is null
432     */
433    public void setEntry(int index, T value) throws NullArgumentException, OutOfRangeException {
434        MathUtils.checkNotNull(value);
435        checkIndex(index);
436        entries.put(index, value);
437    }
438
439    /** {@inheritDoc} */
440    public void setSubVector(int index, FieldVector<T> v)
441        throws OutOfRangeException {
442        checkIndex(index);
443        checkIndex(index + v.getDimension() - 1);
444        final int n = v.getDimension();
445        for (int i = 0; i < n; i++) {
446            setEntry(i + index, v.getEntry(i));
447        }
448    }
449
450    /**
451     * Optimized method to compute {@code this} minus {@code v}.
452     * @param v vector to be subtracted
453     * @return {@code this - v}
454     * @throws DimensionMismatchException if {@code v} is not the same size as
455     * {@code this}.
456     */
457    public SparseFieldVector<T> subtract(SparseFieldVector<T> v)
458        throws DimensionMismatchException {
459        checkVectorDimensions(v.getDimension());
460        SparseFieldVector<T> res = (SparseFieldVector<T>)copy();
461        OpenIntToFieldHashMap<T>.Iterator iter = v.getEntries().iterator();
462        while (iter.hasNext()) {
463            iter.advance();
464            int key = iter.key();
465            if (entries.containsKey(key)) {
466                res.setEntry(key, entries.get(key).subtract(iter.value()));
467            } else {
468                res.setEntry(key, field.getZero().subtract(iter.value()));
469            }
470        }
471        return res;
472    }
473
474    /** {@inheritDoc} */
475    public FieldVector<T> subtract(FieldVector<T> v)
476        throws DimensionMismatchException {
477        if (v instanceof SparseFieldVector<?>) {
478            return subtract((SparseFieldVector<T>)v);
479        } else {
480            final int n = v.getDimension();
481            checkVectorDimensions(n);
482            SparseFieldVector<T> res = new SparseFieldVector<T>(this);
483            for (int i = 0; i < n; i++) {
484                if (entries.containsKey(i)) {
485                    res.setEntry(i, entries.get(i).subtract(v.getEntry(i)));
486                } else {
487                    res.setEntry(i, field.getZero().subtract(v.getEntry(i)));
488                }
489            }
490            return res;
491        }
492    }
493
494    /** {@inheritDoc} */
495    public T[] toArray() {
496        T[] res = MathArrays.buildArray(field, virtualSize);
497        OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
498        while (iter.hasNext()) {
499            iter.advance();
500            res[iter.key()] = iter.value();
501        }
502        return res;
503    }
504
505    /**
506     * Check whether an index is valid.
507     *
508     * @param index Index to check.
509     * @throws OutOfRangeException if the index is not valid.
510     */
511    private void checkIndex(final int index) throws OutOfRangeException {
512        if (index < 0 || index >= getDimension()) {
513            throw new OutOfRangeException(index, 0, getDimension() - 1);
514        }
515    }
516
517    /**
518     * Checks that the indices of a subvector are valid.
519     *
520     * @param start the index of the first entry of the subvector
521     * @param end the index of the last entry of the subvector (inclusive)
522     * @throws OutOfRangeException if {@code start} of {@code end} are not valid
523     * @throws NumberIsTooSmallException if {@code end < start}
524     * @since 3.3
525     */
526    private void checkIndices(final int start, final int end)
527        throws NumberIsTooSmallException, OutOfRangeException {
528        final int dim = getDimension();
529        if ((start < 0) || (start >= dim)) {
530            throw new OutOfRangeException(LocalizedFormats.INDEX, start, 0,
531                                          dim - 1);
532        }
533        if ((end < 0) || (end >= dim)) {
534            throw new OutOfRangeException(LocalizedFormats.INDEX, end, 0,
535                                          dim - 1);
536        }
537        if (end < start) {
538            throw new NumberIsTooSmallException(LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW,
539                                                end, start, false);
540        }
541    }
542
543    /**
544     * Check if instance dimension is equal to some expected value.
545     *
546     * @param n Expected dimension.
547     * @throws DimensionMismatchException if the dimensions do not match.
548     */
549    protected void checkVectorDimensions(int n)
550        throws DimensionMismatchException {
551        if (getDimension() != n) {
552            throw new DimensionMismatchException(getDimension(), n);
553        }
554    }
555
556    /** {@inheritDoc} */
557    public FieldVector<T> add(FieldVector<T> v) throws DimensionMismatchException {
558        if (v instanceof SparseFieldVector<?>) {
559            return add((SparseFieldVector<T>) v);
560        } else {
561            final int n = v.getDimension();
562            checkVectorDimensions(n);
563            SparseFieldVector<T> res = new SparseFieldVector<T>(field,
564                                                                getDimension());
565            for (int i = 0; i < n; i++) {
566                res.setEntry(i, v.getEntry(i).add(getEntry(i)));
567            }
568            return res;
569        }
570    }
571
572    /**
573     * Visits (but does not alter) all entries of this vector in default order
574     * (increasing index).
575     *
576     * @param visitor the visitor to be used to process the entries of this
577     * vector
578     * @return the value returned by {@link FieldVectorPreservingVisitor#end()}
579     * at the end of the walk
580     * @since 3.3
581     */
582    public T walkInDefaultOrder(final FieldVectorPreservingVisitor<T> visitor) {
583        final int dim = getDimension();
584        visitor.start(dim, 0, dim - 1);
585        for (int i = 0; i < dim; i++) {
586            visitor.visit(i, getEntry(i));
587        }
588        return visitor.end();
589    }
590
591    /**
592     * Visits (but does not alter) some entries of this vector in default order
593     * (increasing index).
594     *
595     * @param visitor visitor to be used to process the entries of this vector
596     * @param start the index of the first entry to be visited
597     * @param end the index of the last entry to be visited (inclusive)
598     * @return the value returned by {@link FieldVectorPreservingVisitor#end()}
599     * at the end of the walk
600     * @throws NumberIsTooSmallException if {@code end < start}.
601     * @throws OutOfRangeException if the indices are not valid.
602     * @since 3.3
603     */
604    public T walkInDefaultOrder(final FieldVectorPreservingVisitor<T> visitor,
605                                final int start, final int end)
606        throws NumberIsTooSmallException, OutOfRangeException {
607        checkIndices(start, end);
608        visitor.start(getDimension(), start, end);
609        for (int i = start; i <= end; i++) {
610            visitor.visit(i, getEntry(i));
611        }
612        return visitor.end();
613    }
614
615    /**
616     * Visits (but does not alter) all entries of this vector in optimized
617     * order. The order in which the entries are visited is selected so as to
618     * lead to the most efficient implementation; it might depend on the
619     * concrete implementation of this abstract class.
620     *
621     * @param visitor the visitor to be used to process the entries of this
622     * vector
623     * @return the value returned by {@link FieldVectorPreservingVisitor#end()}
624     * at the end of the walk
625     * @since 3.3
626     */
627    public T walkInOptimizedOrder(final FieldVectorPreservingVisitor<T> visitor) {
628        return walkInDefaultOrder(visitor);
629    }
630
631    /**
632     * Visits (but does not alter) some entries of this vector in optimized
633     * order. The order in which the entries are visited is selected so as to
634     * lead to the most efficient implementation; it might depend on the
635     * concrete implementation of this abstract class.
636     *
637     * @param visitor visitor to be used to process the entries of this vector
638     * @param start the index of the first entry to be visited
639     * @param end the index of the last entry to be visited (inclusive)
640     * @return the value returned by {@link FieldVectorPreservingVisitor#end()}
641     * at the end of the walk
642     * @throws NumberIsTooSmallException if {@code end < start}.
643     * @throws OutOfRangeException if the indices are not valid.
644     * @since 3.3
645     */
646    public T walkInOptimizedOrder(final FieldVectorPreservingVisitor<T> visitor,
647                                  final int start, final int end)
648        throws NumberIsTooSmallException, OutOfRangeException {
649        return walkInDefaultOrder(visitor, start, end);
650    }
651
652    /**
653     * Visits (and possibly alters) all entries of this vector in default order
654     * (increasing index).
655     *
656     * @param visitor the visitor to be used to process and modify the entries
657     * of this vector
658     * @return the value returned by {@link FieldVectorChangingVisitor#end()}
659     * at the end of the walk
660     * @since 3.3
661     */
662    public T walkInDefaultOrder(final FieldVectorChangingVisitor<T> visitor) {
663        final int dim = getDimension();
664        visitor.start(dim, 0, dim - 1);
665        for (int i = 0; i < dim; i++) {
666            setEntry(i, visitor.visit(i, getEntry(i)));
667        }
668        return visitor.end();
669    }
670
671    /**
672     * Visits (and possibly alters) some entries of this vector in default order
673     * (increasing index).
674     *
675     * @param visitor visitor to be used to process the entries of this vector
676     * @param start the index of the first entry to be visited
677     * @param end the index of the last entry to be visited (inclusive)
678     * @return the value returned by {@link FieldVectorChangingVisitor#end()}
679     * at the end of the walk
680     * @throws NumberIsTooSmallException if {@code end < start}.
681     * @throws OutOfRangeException if the indices are not valid.
682     * @since 3.3
683     */
684    public T walkInDefaultOrder(final FieldVectorChangingVisitor<T> visitor,
685                                final int start, final int end)
686        throws NumberIsTooSmallException, OutOfRangeException {
687        checkIndices(start, end);
688        visitor.start(getDimension(), start, end);
689        for (int i = start; i <= end; i++) {
690            setEntry(i, visitor.visit(i, getEntry(i)));
691        }
692        return visitor.end();
693    }
694
695    /**
696     * Visits (and possibly alters) all entries of this vector in optimized
697     * order. The order in which the entries are visited is selected so as to
698     * lead to the most efficient implementation; it might depend on the
699     * concrete implementation of this abstract class.
700     *
701     * @param visitor the visitor to be used to process the entries of this
702     * vector
703     * @return the value returned by {@link FieldVectorChangingVisitor#end()}
704     * at the end of the walk
705     * @since 3.3
706     */
707    public T walkInOptimizedOrder(final FieldVectorChangingVisitor<T> visitor) {
708        return walkInDefaultOrder(visitor);
709    }
710
711    /**
712     * Visits (and possibly change) some entries of this vector in optimized
713     * order. The order in which the entries are visited is selected so as to
714     * lead to the most efficient implementation; it might depend on the
715     * concrete implementation of this abstract class.
716     *
717     * @param visitor visitor to be used to process the entries of this vector
718     * @param start the index of the first entry to be visited
719     * @param end the index of the last entry to be visited (inclusive)
720     * @return the value returned by {@link FieldVectorChangingVisitor#end()}
721     * at the end of the walk
722     * @throws NumberIsTooSmallException if {@code end < start}.
723     * @throws OutOfRangeException if the indices are not valid.
724     * @since 3.3
725     */
726    public T walkInOptimizedOrder(final FieldVectorChangingVisitor<T> visitor,
727                                  final int start, final int end)
728        throws NumberIsTooSmallException, OutOfRangeException {
729        return walkInDefaultOrder(visitor, start, end);
730    }
731
732    /** {@inheritDoc} */
733    @Override
734    public int hashCode() {
735        final int prime = 31;
736        int result = 1;
737        result = prime * result + ((field == null) ? 0 : field.hashCode());
738        result = prime * result + virtualSize;
739        OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
740        while (iter.hasNext()) {
741            iter.advance();
742            int temp = iter.value().hashCode();
743            result = prime * result + temp;
744        }
745        return result;
746    }
747
748
749    /** {@inheritDoc} */
750    @Override
751    public boolean equals(Object obj) {
752
753        if (this == obj) {
754            return true;
755        }
756
757        if (!(obj instanceof SparseFieldVector<?>)) {
758            return false;
759        }
760
761        @SuppressWarnings("unchecked") // OK, because "else if" check below ensures that
762                                       // other must be the same type as this
763        SparseFieldVector<T> other = (SparseFieldVector<T>) obj;
764        if (field == null) {
765            if (other.field != null) {
766                return false;
767            }
768        } else if (!field.equals(other.field)) {
769            return false;
770        }
771        if (virtualSize != other.virtualSize) {
772            return false;
773        }
774
775        OpenIntToFieldHashMap<T>.Iterator iter = entries.iterator();
776        while (iter.hasNext()) {
777            iter.advance();
778            T test = other.getEntry(iter.key());
779            if (!test.equals(iter.value())) {
780                return false;
781            }
782        }
783        iter = other.getEntries().iterator();
784        while (iter.hasNext()) {
785            iter.advance();
786            T test = iter.value();
787            if (!test.equals(getEntry(iter.key()))) {
788                return false;
789            }
790        }
791        return true;
792    }
793}