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     */
017    package org.apache.commons.math.linear;
018    
019    import java.io.Serializable;
020    
021    import org.apache.commons.math.MathRuntimeException;
022    import org.apache.commons.math.util.OpenIntToDoubleHashMap;
023    import org.apache.commons.math.util.OpenIntToDoubleHashMap.Iterator;
024    
025    /**
026     * This class implements the {@link RealVector} interface with a {@link OpenIntToDoubleHashMap} backing store.
027     * @version $Revision: 925812 $ $Date: 2010-03-21 11:49:31 -0400 (Sun, 21 Mar 2010) $
028     * @since 2.0
029    */
030    public class OpenMapRealVector extends AbstractRealVector implements SparseRealVector, Serializable {
031    
032        /** Default Tolerance for having a value considered zero. */
033        public static final double DEFAULT_ZERO_TOLERANCE = 1.0e-12;
034    
035        /** Serializable version identifier. */
036        private static final long serialVersionUID = 8772222695580707260L;
037    
038        /** Entries of the vector. */
039        private final OpenIntToDoubleHashMap entries;
040    
041        /** Dimension of the vector. */
042        private final int virtualSize;
043    
044        /** Tolerance for having a value considered zero. */
045        private double epsilon;
046    
047        /**
048         * Build a 0-length vector.
049         * <p>Zero-length vectors may be used to initialized construction of vectors
050         * by data gathering. We start with zero-length and use either the {@link
051         * #OpenMapRealVector(OpenMapRealVector, int)} constructor
052         * or one of the <code>append</code> method ({@link #append(double)}, {@link
053         * #append(double[])}, {@link #append(RealVector)}) to gather data
054         * into this vector.</p>
055         */
056        public OpenMapRealVector() {
057            this(0, DEFAULT_ZERO_TOLERANCE);
058        }
059    
060        /**
061         * Construct a (dimension)-length vector of zeros.
062         * @param dimension size of the vector
063         */
064        public OpenMapRealVector(int dimension) {
065            this(dimension, DEFAULT_ZERO_TOLERANCE);
066        }
067    
068        /**
069         * Construct a (dimension)-length vector of zeros, specifying zero tolerance.
070         * @param dimension Size of the vector
071         * @param epsilon The tolerance for having a value considered zero
072         */
073        public OpenMapRealVector(int dimension, double epsilon) {
074            virtualSize = dimension;
075            entries = new OpenIntToDoubleHashMap(0.0);
076            this.epsilon = epsilon;
077        }
078    
079        /**
080         * Build a resized vector, for use with append.
081         * @param v The original vector
082         * @param resize The amount to resize it
083         */
084        protected OpenMapRealVector(OpenMapRealVector v, int resize) {
085            virtualSize = v.getDimension() + resize;
086            entries = new OpenIntToDoubleHashMap(v.entries);
087            epsilon = v.epsilon;
088        }
089    
090        /**
091         * Build a vector with known the sparseness (for advanced use only).
092         * @param dimension The size of the vector
093         * @param expectedSize The expected number of non-zero entries
094         */
095        public OpenMapRealVector(int dimension, int expectedSize) {
096            this(dimension, expectedSize, DEFAULT_ZERO_TOLERANCE);
097        }
098    
099        /**
100         * Build a vector with known the sparseness and zero tolerance setting (for advanced use only).
101         * @param dimension The size of the vector
102         * @param expectedSize The expected number of non-zero entries
103         * @param epsilon The tolerance for having a value considered zero
104         */
105        public OpenMapRealVector(int dimension, int expectedSize, double epsilon) {
106            virtualSize = dimension;
107            entries = new OpenIntToDoubleHashMap(expectedSize, 0.0);
108            this.epsilon = epsilon;
109        }
110    
111        /**
112         * Create from a double array.
113         * Only non-zero entries will be stored
114         * @param values The set of values to create from
115         */
116        public OpenMapRealVector(double[] values) {
117            this(values, DEFAULT_ZERO_TOLERANCE);
118        }
119    
120        /**
121         * Create from a double array, specifying zero tolerance.
122         * Only non-zero entries will be stored
123         * @param values The set of values to create from
124         * @param epsilon The tolerance for having a value considered zero
125         */
126        public OpenMapRealVector(double[] values, double epsilon) {
127            virtualSize = values.length;
128            entries = new OpenIntToDoubleHashMap(0.0);
129            this.epsilon = epsilon;
130            for (int key = 0; key < values.length; key++) {
131                double value = values[key];
132                if (!isDefaultValue(value)) {
133                    entries.put(key, value);
134                }
135            }
136        }
137    
138        /**
139         * Create from a Double array.
140         * Only non-zero entries will be stored
141         * @param values The set of values to create from
142         */
143        public OpenMapRealVector(Double[] values) {
144            this(values, DEFAULT_ZERO_TOLERANCE);
145        }
146    
147        /**
148         * Create from a Double array.
149         * Only non-zero entries will be stored
150         * @param values The set of values to create from
151         * @param epsilon The tolerance for having a value considered zero
152         */
153        public OpenMapRealVector(Double[] values, double epsilon) {
154            virtualSize = values.length;
155            entries = new OpenIntToDoubleHashMap(0.0);
156            this.epsilon = epsilon;
157            for (int key = 0; key < values.length; key++) {
158                double value = values[key].doubleValue();
159                if (!isDefaultValue(value)) {
160                    entries.put(key, value);
161                }
162            }
163        }
164    
165        /**
166         * Copy constructor.
167         * @param v The instance to copy from
168         */
169        public OpenMapRealVector(OpenMapRealVector v) {
170            virtualSize = v.getDimension();
171            entries = new OpenIntToDoubleHashMap(v.getEntries());
172            epsilon = v.epsilon;
173        }
174    
175        /**
176         * Generic copy constructor.
177         * @param v The instance to copy from
178         */
179        public OpenMapRealVector(RealVector v) {
180            virtualSize = v.getDimension();
181            entries = new OpenIntToDoubleHashMap(0.0);
182            epsilon = DEFAULT_ZERO_TOLERANCE;
183            for (int key = 0; key < virtualSize; key++) {
184                double value = v.getEntry(key);
185                if (!isDefaultValue(value)) {
186                    entries.put(key, value);
187                }
188            }
189        }
190    
191        /**
192         * Get the entries of this instance.
193         * @return entries of this instance
194         */
195        private OpenIntToDoubleHashMap getEntries() {
196            return entries;
197        }
198    
199        /**
200         * Determine if this value is within epsilon of zero.
201         * @param value The value to test
202         * @return <code>true</code> if this value is within epsilon to zero, <code>false</code> otherwise
203         * @since 2.1
204         */
205        protected boolean isDefaultValue(double value) {
206            return Math.abs(value) < epsilon;
207        }
208    
209        /** {@inheritDoc} */
210        @Override
211        public RealVector add(RealVector v) throws IllegalArgumentException {
212            checkVectorDimensions(v.getDimension());
213            if (v instanceof OpenMapRealVector) {
214                return add((OpenMapRealVector) v);
215            } else {
216                return super.add(v);
217            }
218        }
219    
220        /**
221         * Optimized method to add two OpenMapRealVectors.  Copies the larger vector, iterates over the smaller.
222         * @param v Vector to add with
223         * @return The sum of <code>this</code> with <code>v</code>
224         * @throws IllegalArgumentException If the dimensions don't match
225         */
226        public OpenMapRealVector add(OpenMapRealVector v) throws IllegalArgumentException{
227            checkVectorDimensions(v.getDimension());
228            boolean copyThis = entries.size() > v.entries.size();
229            OpenMapRealVector res = copyThis ? this.copy() : v.copy();
230            Iterator iter = copyThis ? v.entries.iterator() : entries.iterator();
231            OpenIntToDoubleHashMap randomAccess = copyThis ? entries : v.entries;
232            while (iter.hasNext()) {
233                iter.advance();
234                int key = iter.key();
235                if (randomAccess.containsKey(key)) {
236                    res.setEntry(key, randomAccess.get(key) + iter.value());
237                } else {
238                    res.setEntry(key, iter.value());
239                }
240            }
241            return res;
242        }
243    
244        /**
245         * Optimized method to append a OpenMapRealVector.
246         * @param v vector to append
247         * @return The result of appending <code>v</code> to self
248         */
249        public OpenMapRealVector append(OpenMapRealVector v) {
250            OpenMapRealVector res = new OpenMapRealVector(this, v.getDimension());
251            Iterator iter = v.entries.iterator();
252            while (iter.hasNext()) {
253                iter.advance();
254                res.setEntry(iter.key() + virtualSize, iter.value());
255            }
256            return res;
257        }
258    
259        /** {@inheritDoc} */
260        public OpenMapRealVector append(RealVector v) {
261            if (v instanceof OpenMapRealVector) {
262                return append((OpenMapRealVector) v);
263            }
264            return append(v.getData());
265        }
266    
267        /** {@inheritDoc} */
268        public OpenMapRealVector append(double d) {
269            OpenMapRealVector res = new OpenMapRealVector(this, 1);
270            res.setEntry(virtualSize, d);
271            return res;
272        }
273    
274        /** {@inheritDoc} */
275        public OpenMapRealVector append(double[] a) {
276            OpenMapRealVector res = new OpenMapRealVector(this, a.length);
277            for (int i = 0; i < a.length; i++) {
278                res.setEntry(i + virtualSize, a[i]);
279            }
280            return res;
281        }
282    
283        /**
284         * {@inheritDoc}
285         * @since 2.1
286         */
287        @Override
288        public OpenMapRealVector copy() {
289            return new OpenMapRealVector(this);
290        }
291    
292        /**
293         * Optimized method to compute the dot product with an OpenMapRealVector.
294         * Iterates over the smaller of the two.
295         * @param v The vector to compute the dot product with
296         * @return The dot product of <code>this</code> and <code>v</code>
297         * @throws IllegalArgumentException If the dimensions don't match
298         */
299        public double dotProduct(OpenMapRealVector v) throws IllegalArgumentException {
300            checkVectorDimensions(v.getDimension());
301            boolean thisIsSmaller  = entries.size() < v.entries.size();
302            Iterator iter = thisIsSmaller  ? entries.iterator() : v.entries.iterator();
303            OpenIntToDoubleHashMap larger = thisIsSmaller  ? v.entries : entries;
304            double d = 0;
305            while(iter.hasNext()) {
306                iter.advance();
307                d += iter.value() * larger.get(iter.key());
308            }
309            return d;
310        }
311    
312        /** {@inheritDoc} */
313        @Override
314        public double dotProduct(RealVector v) throws IllegalArgumentException {
315            if(v instanceof OpenMapRealVector) {
316                return dotProduct((OpenMapRealVector)v);
317            } else {
318                return super.dotProduct(v);
319            }
320        }
321    
322        /** {@inheritDoc} */
323        public OpenMapRealVector ebeDivide(RealVector v) throws IllegalArgumentException {
324            checkVectorDimensions(v.getDimension());
325            OpenMapRealVector res = new OpenMapRealVector(this);
326            Iterator iter = res.entries.iterator();
327            while (iter.hasNext()) {
328                iter.advance();
329                res.setEntry(iter.key(), iter.value() / v.getEntry(iter.key()));
330            }
331            return res;
332        }
333    
334        /** {@inheritDoc} */
335        @Override
336        public OpenMapRealVector ebeDivide(double[] v) throws IllegalArgumentException {
337            checkVectorDimensions(v.length);
338            OpenMapRealVector res = new OpenMapRealVector(this);
339            Iterator iter = res.entries.iterator();
340            while (iter.hasNext()) {
341                iter.advance();
342                res.setEntry(iter.key(), iter.value() / v[iter.key()]);
343            }
344            return res;
345        }
346    
347        /** {@inheritDoc} */
348        public OpenMapRealVector ebeMultiply(RealVector v) throws IllegalArgumentException {
349            checkVectorDimensions(v.getDimension());
350            OpenMapRealVector res = new OpenMapRealVector(this);
351            Iterator iter = res.entries.iterator();
352            while (iter.hasNext()) {
353                iter.advance();
354                res.setEntry(iter.key(), iter.value() * v.getEntry(iter.key()));
355            }
356            return res;
357        }
358    
359        /** {@inheritDoc} */
360        @Override
361        public OpenMapRealVector ebeMultiply(double[] v) throws IllegalArgumentException {
362            checkVectorDimensions(v.length);
363            OpenMapRealVector res = new OpenMapRealVector(this);
364            Iterator iter = res.entries.iterator();
365            while (iter.hasNext()) {
366                iter.advance();
367                res.setEntry(iter.key(), iter.value() * v[iter.key()]);
368            }
369            return res;
370        }
371    
372        /** {@inheritDoc} */
373        public OpenMapRealVector getSubVector(int index, int n) throws MatrixIndexException {
374            checkIndex(index);
375            checkIndex(index + n - 1);
376            OpenMapRealVector res = new OpenMapRealVector(n);
377            int end = index + n;
378            Iterator iter = entries.iterator();
379            while (iter.hasNext()) {
380                iter.advance();
381                int key = iter.key();
382                if (key >= index && key < end) {
383                    res.setEntry(key - index, iter.value());
384                }
385            }
386            return res;
387        }
388    
389        /** {@inheritDoc} */
390        @Override
391        public double[] getData() {
392            double[] res = new double[virtualSize];
393            Iterator iter = entries.iterator();
394            while (iter.hasNext()) {
395                iter.advance();
396                res[iter.key()] = iter.value();
397            }
398            return res;
399        }
400    
401        /** {@inheritDoc} */
402        public int getDimension() {
403            return virtualSize;
404        }
405    
406        /**
407         * Optimized method to compute distance.
408         * @param v The vector to compute distance to
409         * @return The distance from <code>this</code> and <code>v</code>
410         * @throws IllegalArgumentException If the dimensions don't match
411         */
412        public double getDistance(OpenMapRealVector v) throws IllegalArgumentException {
413            Iterator iter = entries.iterator();
414            double res = 0;
415            while (iter.hasNext()) {
416                iter.advance();
417                int key = iter.key();
418                double delta;
419                delta = iter.value() - v.getEntry(key);
420                res += delta * delta;
421            }
422            iter = v.getEntries().iterator();
423            while (iter.hasNext()) {
424                iter.advance();
425                int key = iter.key();
426                if (!entries.containsKey(key)) {
427                    final double value = iter.value();
428                    res += value * value;
429                }
430            }
431            return Math.sqrt(res);
432        }
433    
434        /** {@inheritDoc} */
435        @Override
436        public double getDistance(RealVector v) throws IllegalArgumentException {
437            checkVectorDimensions(v.getDimension());
438            if (v instanceof OpenMapRealVector) {
439                return getDistance((OpenMapRealVector) v);
440            }
441            return getDistance(v.getData());
442        }
443    
444        /** {@inheritDoc} */
445        @Override
446        public double getDistance(double[] v) throws IllegalArgumentException {
447            checkVectorDimensions(v.length);
448            double res = 0;
449            for (int i = 0; i < v.length; i++) {
450                double delta = entries.get(i) - v[i];
451                res += delta * delta;
452            }
453            return Math.sqrt(res);
454        }
455    
456        /** {@inheritDoc} */
457        public double getEntry(int index) throws MatrixIndexException {
458            checkIndex(index);
459            return entries.get(index);
460        }
461    
462        /**
463         * Distance between two vectors.
464         * <p>This method computes the distance consistent with
465         * L<sub>1</sub> norm, i.e. the sum of the absolute values of
466         * elements differences.</p>
467         * @param v vector to which distance is requested
468         * @return distance between two vectors.
469         */
470        public double getL1Distance(OpenMapRealVector v) {
471            double max = 0;
472            Iterator iter = entries.iterator();
473            while (iter.hasNext()) {
474                iter.advance();
475                double delta = Math.abs(iter.value() - v.getEntry(iter.key()));
476                max += delta;
477            }
478            iter = v.getEntries().iterator();
479            while (iter.hasNext()) {
480                iter.advance();
481                int key = iter.key();
482                if (!entries.containsKey(key)) {
483                    double delta = Math.abs(iter.value());
484                    max +=  Math.abs(delta);
485                }
486            }
487            return max;
488        }
489    
490        /** {@inheritDoc} */
491        @Override
492        public double getL1Distance(RealVector v) throws IllegalArgumentException {
493            checkVectorDimensions(v.getDimension());
494            if (v instanceof OpenMapRealVector) {
495                return getL1Distance((OpenMapRealVector) v);
496            }
497            return getL1Distance(v.getData());
498        }
499    
500        /** {@inheritDoc} */
501        @Override
502        public double getL1Distance(double[] v) throws IllegalArgumentException {
503            checkVectorDimensions(v.length);
504            double max = 0;
505            for (int i = 0; i < v.length; i++) {
506                double delta = Math.abs(getEntry(i) - v[i]);
507                max += delta;
508            }
509            return max;
510        }
511    
512        /**
513         * Optimized method to compute LInfDistance.
514         * @param v The vector to compute from
515         * @return the LInfDistance
516         */
517        private double getLInfDistance(OpenMapRealVector v) {
518            double max = 0;
519            Iterator iter = entries.iterator();
520            while (iter.hasNext()) {
521                iter.advance();
522                double delta = Math.abs(iter.value() - v.getEntry(iter.key()));
523                if (delta > max) {
524                    max = delta;
525                }
526            }
527            iter = v.getEntries().iterator();
528            while (iter.hasNext()) {
529                iter.advance();
530                int key = iter.key();
531                if (!entries.containsKey(key)) {
532                    if (iter.value() > max) {
533                        max = iter.value();
534                    }
535                }
536            }
537            return max;
538        }
539    
540        /** {@inheritDoc} */
541        @Override
542        public double getLInfDistance(RealVector v) throws IllegalArgumentException {
543            checkVectorDimensions(v.getDimension());
544            if (v instanceof OpenMapRealVector) {
545                return getLInfDistance((OpenMapRealVector) v);
546            }
547            return getLInfDistance(v.getData());
548        }
549    
550        /** {@inheritDoc} */
551        @Override
552        public double getLInfDistance(double[] v) throws IllegalArgumentException {
553            checkVectorDimensions(v.length);
554            double max = 0;
555            for (int i = 0; i < v.length; i++) {
556                double delta = Math.abs(getEntry(i) - v[i]);
557                if (delta > max) {
558                    max = delta;
559                }
560            }
561            return max;
562        }
563    
564        /** {@inheritDoc} */
565        public boolean isInfinite() {
566            boolean infiniteFound = false;
567            Iterator iter = entries.iterator();
568            while (iter.hasNext()) {
569                iter.advance();
570                final double value = iter.value();
571                if (Double.isNaN(value)) {
572                    return false;
573                }
574                if (Double.isInfinite(value)) {
575                    infiniteFound = true;
576                }
577            }
578            return infiniteFound;
579        }
580    
581        /** {@inheritDoc} */
582        public boolean isNaN() {
583            Iterator iter = entries.iterator();
584            while (iter.hasNext()) {
585                iter.advance();
586                if (Double.isNaN(iter.value())) {
587                    return true;
588                }
589            }
590            return false;
591        }
592    
593        /** {@inheritDoc} */
594        @Override
595        public OpenMapRealVector mapAdd(double d) {
596            return copy().mapAddToSelf(d);
597        }
598    
599        /** {@inheritDoc} */
600        @Override
601        public OpenMapRealVector mapAddToSelf(double d) {
602            for (int i = 0; i < virtualSize; i++) {
603                setEntry(i, getEntry(i) + d);
604            }
605            return this;
606        }
607    
608         /** {@inheritDoc} */
609        @Override
610        public RealMatrix outerProduct(double[] v) throws IllegalArgumentException {
611            checkVectorDimensions(v.length);
612            RealMatrix res = new OpenMapRealMatrix(virtualSize, virtualSize);
613            Iterator iter = entries.iterator();
614            while (iter.hasNext()) {
615                iter.advance();
616                int row = iter.key();
617                double value = iter.value();
618                for (int col = 0; col < virtualSize; col++) {
619                    res.setEntry(row, col, value * v[col]);
620                }
621            }
622            return res;
623        }
624    
625        /** {@inheritDoc} */
626        public RealVector projection(RealVector v) throws IllegalArgumentException {
627            checkVectorDimensions(v.getDimension());
628            return v.mapMultiply(dotProduct(v) / v.dotProduct(v));
629        }
630    
631        /** {@inheritDoc} */
632        @Override
633        public OpenMapRealVector projection(double[] v) throws IllegalArgumentException {
634            checkVectorDimensions(v.length);
635            return (OpenMapRealVector) projection(new OpenMapRealVector(v));
636        }
637    
638        /** {@inheritDoc} */
639        public void setEntry(int index, double value) throws MatrixIndexException {
640            checkIndex(index);
641            if (!isDefaultValue(value)) {
642                entries.put(index, value);
643            } else if (entries.containsKey(index)) {
644                entries.remove(index);
645            }
646        }
647    
648        /** {@inheritDoc} */
649        @Override
650        public void setSubVector(int index, RealVector v) throws MatrixIndexException {
651            checkIndex(index);
652            checkIndex(index + v.getDimension() - 1);
653            setSubVector(index, v.getData());
654        }
655    
656        /** {@inheritDoc} */
657        @Override
658        public void setSubVector(int index, double[] v) throws MatrixIndexException {
659            checkIndex(index);
660            checkIndex(index + v.length - 1);
661            for (int i = 0; i < v.length; i++) {
662                setEntry(i + index, v[i]);
663            }
664        }
665    
666        /** {@inheritDoc} */
667        @Override
668        public void set(double value) {
669            for (int i = 0; i < virtualSize; i++) {
670                setEntry(i, value);
671            }
672        }
673    
674        /**
675         * Optimized method to subtract OpenMapRealVectors.
676         * @param v The vector to subtract from <code>this</code>
677         * @return The difference of <code>this</code> and <code>v</code>
678         * @throws IllegalArgumentException If the dimensions don't match
679         */
680        public OpenMapRealVector subtract(OpenMapRealVector v) throws IllegalArgumentException{
681            checkVectorDimensions(v.getDimension());
682            OpenMapRealVector res = copy();
683            Iterator iter = v.getEntries().iterator();
684            while (iter.hasNext()) {
685                iter.advance();
686                int key = iter.key();
687                if (entries.containsKey(key)) {
688                    res.setEntry(key, entries.get(key) - iter.value());
689                } else {
690                    res.setEntry(key, -iter.value());
691                }
692            }
693            return res;
694        }
695    
696        /** {@inheritDoc} */
697        @Override
698        public OpenMapRealVector subtract(RealVector v) throws IllegalArgumentException {
699            checkVectorDimensions(v.getDimension());
700            if (v instanceof OpenMapRealVector) {
701                return subtract((OpenMapRealVector) v);
702            }
703            return subtract(v.getData());
704        }
705    
706        /** {@inheritDoc} */
707        @Override
708        public OpenMapRealVector subtract(double[] v) throws IllegalArgumentException {
709            checkVectorDimensions(v.length);
710            OpenMapRealVector res = new OpenMapRealVector(this);
711            for (int i = 0; i < v.length; i++) {
712                if (entries.containsKey(i)) {
713                    res.setEntry(i, entries.get(i) - v[i]);
714                } else {
715                    res.setEntry(i, -v[i]);
716                }
717            }
718            return res;
719        }
720    
721    
722        /** {@inheritDoc} */
723        @Override
724        public OpenMapRealVector unitVector() {
725            OpenMapRealVector res = copy();
726            res.unitize();
727            return res;
728        }
729    
730        /** {@inheritDoc} */
731        @Override
732        public void unitize() {
733            double norm = getNorm();
734            if (isDefaultValue(norm)) {
735                throw  MathRuntimeException.createArithmeticException("cannot normalize a zero norm vector");
736            }
737            Iterator iter = entries.iterator();
738            while (iter.hasNext()) {
739                iter.advance();
740                entries.put(iter.key(), iter.value() / norm);
741            }
742    
743        }
744    
745    
746        /** {@inheritDoc} */
747        @Override
748        public double[] toArray() {
749            return getData();
750        }
751    
752        /** {@inheritDoc}
753         * <p> Implementation Note: This works on exact values, and as a result
754         * it is possible for {@code a.subtract(b)} to be the zero vector, while
755         * {@code a.hashCode() != b.hashCode()}.</p>
756         */
757        @Override
758        public int hashCode() {
759            final int prime = 31;
760            int result = 1;
761            long temp;
762            temp = Double.doubleToLongBits(epsilon);
763            result = prime * result + (int) (temp ^ (temp >>> 32));
764            result = prime * result + virtualSize;
765            Iterator iter = entries.iterator();
766            while (iter.hasNext()) {
767                iter.advance();
768                temp = Double.doubleToLongBits(iter.value());
769                result = prime * result + (int) (temp ^ (temp >>32));
770            }
771            return result;
772        }
773    
774        /**
775         * <p> Implementation Note: This performs an exact comparison, and as a result
776         * it is possible for {@code a.subtract(b}} to be the zero vector, while
777         * {@code  a.equals(b) == false}.</p>
778         * {@inheritDoc}
779         */
780        @Override
781        public boolean equals(Object obj) {
782            if (this == obj) {
783                return true;
784            }
785            if (!(obj instanceof OpenMapRealVector)) {
786                return false;
787            }
788            OpenMapRealVector other = (OpenMapRealVector) obj;
789            if (virtualSize != other.virtualSize) {
790                return false;
791            }
792            if (Double.doubleToLongBits(epsilon) !=
793                Double.doubleToLongBits(other.epsilon)) {
794                return false;
795            }
796            Iterator iter = entries.iterator();
797            while (iter.hasNext()) {
798                iter.advance();
799                double test = other.getEntry(iter.key());
800                if (Double.doubleToLongBits(test) != Double.doubleToLongBits(iter.value())) {
801                    return false;
802                }
803            }
804            iter = other.getEntries().iterator();
805            while (iter.hasNext()) {
806                iter.advance();
807                double test = iter.value();
808                if (Double.doubleToLongBits(test) != Double.doubleToLongBits(getEntry(iter.key()))) {
809                    return false;
810                }
811            }
812            return true;
813        }
814    
815        /**
816         *
817         * @return the percentage of none zero elements as a decimal percent.
818         */
819        public double getSparcity() {
820            return (double)entries.size()/(double)getDimension();
821        }
822    
823        /** {@inheritDoc} */
824        @Override
825        public java.util.Iterator<Entry> sparseIterator() {
826            return new OpenMapSparseIterator();
827        }
828    
829        /**
830         *  Implementation of <code>Entry</code> optimized for OpenMap.
831         * <p>This implementation does not allow arbitrary calls to <code>setIndex</code>
832         * since the order that entries are returned is undefined.
833         */
834        protected class OpenMapEntry extends Entry {
835    
836            /** Iterator pointing to the entry. */
837            private final Iterator iter;
838    
839            /** Build an entry from an iterator point to an element.
840             * @param iter iterator pointing to the entry
841             */
842            protected OpenMapEntry(Iterator iter) {
843                this.iter = iter;
844            }
845    
846            /** {@inheritDoc} */
847            @Override
848            public double getValue() {
849                return iter.value();
850            }
851    
852            /** {@inheritDoc} */
853            @Override
854            public void setValue(double value) {
855                entries.put(iter.key(), value);
856            }
857    
858            /** {@inheritDoc} */
859            @Override
860            public int getIndex() {
861                return iter.key();
862            }
863    
864        }
865    
866        /**
867         *  Iterator class to do iteration over just the non-zero elements.
868         *  <p>This implementation is fail-fast, so cannot be used to modify any zero element.
869         *
870         */
871        protected class OpenMapSparseIterator implements java.util.Iterator<Entry> {
872    
873            /** Underlying iterator. */
874            private final Iterator iter;
875    
876            /** Current entry. */
877            private final Entry current;
878    
879            /** Simple constructor. */
880            protected OpenMapSparseIterator() {
881                iter = entries.iterator();
882                current = new OpenMapEntry(iter);
883            }
884    
885            /** {@inheritDoc} */
886            public boolean hasNext() {
887                return iter.hasNext();
888            }
889    
890            /** {@inheritDoc} */
891            public Entry next() {
892                iter.advance();
893                return current;
894            }
895    
896            /** {@inheritDoc} */
897            public void remove() {
898                throw new UnsupportedOperationException("Not supported");
899           }
900    
901        }
902    }