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.geometry.euclidean.oned;
018
019import java.util.Comparator;
020import java.util.function.UnaryOperator;
021
022import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
023import org.apache.commons.geometry.euclidean.EuclideanVector;
024import org.apache.commons.geometry.euclidean.EuclideanVectorSum;
025import org.apache.commons.geometry.euclidean.internal.Vectors;
026import org.apache.commons.numbers.core.Precision;
027
028/** This class represents vectors and points in one-dimensional Euclidean space.
029 * Instances of this class are guaranteed to be immutable.
030 */
031public class Vector1D extends EuclideanVector<Vector1D> {
032
033    /** Zero vector (coordinates: 0). */
034    public static final Vector1D ZERO = new Vector1D(0.0);
035
036    /** A vector with all coordinates set to NaN. */
037    public static final Vector1D NaN = new Vector1D(Double.NaN);
038
039    /** A vector with all coordinates set to positive infinity. */
040    public static final Vector1D POSITIVE_INFINITY =
041        new Vector1D(Double.POSITIVE_INFINITY);
042
043    /** A vector with all coordinates set to negative infinity. */
044    public static final Vector1D NEGATIVE_INFINITY =
045        new Vector1D(Double.NEGATIVE_INFINITY);
046
047    /** Comparator that sorts vectors in component-wise ascending order.
048     * Vectors are only considered equal if their coordinates match exactly.
049     * Null arguments are evaluated as being greater than non-null arguments.
050     */
051    public static final Comparator<Vector1D> COORDINATE_ASCENDING_ORDER = (a, b) -> {
052        int cmp = 0;
053
054        if (a != null && b != null) {
055            cmp = Double.compare(a.getX(), b.getX());
056        } else if (a != null) {
057            cmp = -1;
058        } else if (b != null) {
059            cmp = 1;
060        }
061
062        return cmp;
063    };
064
065    /** Abscissa (coordinate value). */
066    private final double x;
067
068    /** Simple constructor.
069     * @param x abscissa (coordinate value)
070     */
071    private Vector1D(final double x) {
072        this.x = x;
073    }
074
075    /**
076     * Returns the abscissa (coordinate value) of the instance.
077     * @return the abscissa value
078     */
079    public double getX() {
080        return x;
081    }
082
083    /** {@inheritDoc} */
084    @Override
085    public int getDimension() {
086        return 1;
087    }
088
089    /** {@inheritDoc} */
090    @Override
091    public boolean isNaN() {
092        return Double.isNaN(x);
093    }
094
095    /** {@inheritDoc} */
096    @Override
097    public boolean isInfinite() {
098        return !isNaN() && Double.isInfinite(x);
099    }
100
101    /** {@inheritDoc} */
102    @Override
103    public boolean isFinite() {
104        return Double.isFinite(x);
105    }
106
107    /** {@inheritDoc} */
108    @Override
109    public Vector1D vectorTo(final Vector1D v) {
110        return v.subtract(this);
111    }
112
113    /** {@inheritDoc} */
114    @Override
115    public Unit directionTo(final Vector1D v) {
116        return vectorTo(v).normalize();
117    }
118
119    /** {@inheritDoc} */
120    @Override
121    public Vector1D lerp(final Vector1D p, final double t) {
122        return Sum.create()
123                .addScaled(1.0 - t, this)
124                .addScaled(t, p).get();
125    }
126
127    /** {@inheritDoc} */
128    @Override
129    public Vector1D getZero() {
130        return ZERO;
131    }
132
133    /** {@inheritDoc} */
134    @Override
135    public double norm() {
136        return Vectors.norm(x);
137    }
138
139    /** {@inheritDoc} */
140    @Override
141    public double normSq() {
142        return Vectors.normSq(x);
143    }
144
145    /** {@inheritDoc} */
146    @Override
147    public Vector1D withNorm(final double magnitude) {
148        getCheckedNorm(); // validate our norm value
149        return (x > 0.0) ? new Vector1D(magnitude) : new Vector1D(-magnitude);
150    }
151
152    /** {@inheritDoc} */
153    @Override
154    public Vector1D add(final Vector1D v) {
155        return new Vector1D(x + v.x);
156    }
157
158    /** {@inheritDoc} */
159    @Override
160    public Vector1D add(final double factor, final Vector1D v) {
161        return new Vector1D(x + (factor * v.x));
162    }
163
164    /** {@inheritDoc} */
165    @Override
166    public Vector1D subtract(final Vector1D v) {
167        return new Vector1D(x - v.x);
168    }
169
170    /** {@inheritDoc} */
171    @Override
172    public Vector1D subtract(final double factor, final Vector1D v) {
173        return new Vector1D(x - (factor * v.x));
174    }
175
176    /** {@inheritDoc} */
177    @Override
178    public Vector1D negate() {
179        return new Vector1D(-x);
180    }
181
182    /** {@inheritDoc} */
183    @Override
184    public Unit normalize() {
185        return Unit.from(x);
186    }
187
188    /** {@inheritDoc} */
189    @Override
190    public Unit normalizeOrNull() {
191        return Unit.tryCreateNormalized(x, false);
192    }
193
194    /** {@inheritDoc} */
195    @Override
196    public Vector1D multiply(final double a) {
197        return new Vector1D(a * x);
198    }
199
200    /** {@inheritDoc} */
201    @Override
202    public double distance(final Vector1D v) {
203        return Vectors.norm(x - v.x);
204    }
205
206    /** {@inheritDoc} */
207    @Override
208    public double distanceSq(final Vector1D v) {
209        return Vectors.normSq(x - v.x);
210    }
211
212    /** {@inheritDoc} */
213    @Override
214    public double dot(final Vector1D v) {
215        return x * v.x;
216    }
217
218    /** {@inheritDoc}
219     * <p>For the one-dimensional case, this method returns 0 if the vector x values have
220     * the same sign and {@code pi} if they are opposite.</p>
221     */
222    @Override
223    public double angle(final Vector1D v) {
224        // validate the norm values
225        getCheckedNorm();
226        v.getCheckedNorm();
227
228        final double sig1 = Math.signum(x);
229        final double sig2 = Math.signum(v.x);
230
231        // the angle is 0 if the x value signs are the same and pi if not
232        return (sig1 == sig2) ? 0.0 : Math.PI;
233    }
234
235    /** Convenience method to apply a function to this vector. This
236     * can be used to transform the vector inline with other methods.
237     * @param fn the function to apply
238     * @return the transformed vector
239     */
240    public Vector1D transform(final UnaryOperator<Vector1D> fn) {
241        return fn.apply(this);
242    }
243
244    /** {@inheritDoc} */
245    @Override
246    public boolean eq(final Vector1D vec, final Precision.DoubleEquivalence precision) {
247        return precision.eq(x, vec.x);
248    }
249
250    /**
251     * Get a hashCode for the vector.
252     * <p>All NaN values have the same hash code.</p>
253     *
254     * @return a hash code value for this object
255     */
256    @Override
257    public int hashCode() {
258        if (isNaN()) {
259            return 857;
260        }
261        return 403 * Double.hashCode(x);
262    }
263
264    /**
265     * Test for the equality of two vectors.
266     * <p>
267     * If all coordinates of two vectors are exactly the same, and none are
268     * <code>Double.NaN</code>, the two vectors are considered to be equal.
269     * </p>
270     * <p>
271     * <code>NaN</code> coordinates are considered to globally affect the vector
272     * and be equal to each other - i.e, if either (or all) coordinates of the
273     * vector are equal to <code>Double.NaN</code>, the vector is equal to
274     * {@link #NaN}.
275     * </p>
276     *
277     * @param other Object to test for equality to this
278     * @return true if two vector objects are equal, false if
279     *         object is null, not an instance of Vector1D, or
280     *         not equal to this Vector1D instance
281     *
282     */
283    @Override
284    public boolean equals(final Object other) {
285        if (this == other) {
286            return true;
287        }
288        if (other instanceof Vector1D) {
289            final Vector1D rhs = (Vector1D) other;
290            if (rhs.isNaN()) {
291                return this.isNaN();
292            }
293
294            return Double.compare(x, rhs.x) == 0;
295        }
296        return false;
297    }
298
299    /** {@inheritDoc} */
300    @Override
301    public String toString() {
302        return SimpleTupleFormat.getDefault().format(x);
303    }
304
305    /** Returns a vector with the given coordinate value.
306     * @param x vector coordinate
307     * @return vector instance
308     */
309    public static Vector1D of(final double x) {
310        return new Vector1D(x);
311    }
312
313    /** Parses the given string and returns a new vector instance. The expected string
314     * format is the same as that returned by {@link #toString()}.
315     * @param str the string to parse
316     * @return vector instance represented by the string
317     * @throws IllegalArgumentException if the given string has an invalid format
318     */
319    public static Vector1D parse(final String str) {
320        return SimpleTupleFormat.getDefault().parse(str, Vector1D::new);
321    }
322
323    /**
324     * Represent unit vectors.
325     * This allows optimizations to be performed for certain operations.
326     */
327    public static final class Unit extends Vector1D {
328        /** Unit vector (coordinates: 1). */
329        public static final Unit PLUS  = new Unit(1d);
330        /** Negation of unit vector (coordinates: -1). */
331        public static final Unit MINUS = new Unit(-1d);
332
333        /** Simple constructor. Callers are responsible for ensuring that the given
334         * values represent a normalized vector.
335         * @param x abscissa (first coordinate value)
336         */
337        private Unit(final double x) {
338            super(x);
339        }
340
341        /** {@inheritDoc} */
342        @Override
343        public double norm() {
344            return 1;
345        }
346
347        /** {@inheritDoc} */
348        @Override
349        public double normSq() {
350            return 1;
351        }
352
353        /** {@inheritDoc} */
354        @Override
355        public Unit normalize() {
356            return this;
357        }
358
359        /** {@inheritDoc} */
360        @Override
361        public Unit normalizeOrNull() {
362            return this;
363        }
364
365        /** {@inheritDoc} */
366        @Override
367        public Vector1D withNorm(final double mag) {
368            return multiply(mag);
369        }
370
371        /** {@inheritDoc} */
372        @Override
373        public Vector1D negate() {
374            return this == PLUS ? MINUS : PLUS;
375        }
376
377        /** Create a normalized vector.
378         * @param x Vector coordinate.
379         * @return a vector whose norm is 1.
380         * @throws IllegalArgumentException if the norm of the given value is zero, NaN, or infinite
381         */
382        public static Unit from(final double x) {
383            return tryCreateNormalized(x, true);
384        }
385
386        /** Create a normalized vector.
387         * @param v Vector.
388         * @return a vector whose norm is 1.
389         * @throws IllegalArgumentException if the norm of the given value is zero, NaN, or infinite
390         */
391        public static Unit from(final Vector1D v) {
392            return v instanceof Unit ?
393                (Unit) v :
394                from(v.getX());
395        }
396
397        /** Attempt to create a normalized vector from the given coordinate values. If {@code throwOnFailure}
398         * is true, an exception is thrown if a normalized vector cannot be created. Otherwise, null
399         * is returned.
400         * @param x x coordinate
401         * @param throwOnFailure if true, an exception will be thrown if a normalized vector cannot be created
402         * @return normalized vector or null if one cannot be created and {@code throwOnFailure}
403         *      is false
404         * @throws IllegalArgumentException if the computed norm is zero, NaN, or infinite
405         */
406        private static Unit tryCreateNormalized(final double x, final boolean throwOnFailure) {
407            final double norm = Vectors.norm(x);
408
409            if (Vectors.isRealNonZero(norm)) {
410                return x > 0 ? PLUS : MINUS;
411            } else if (throwOnFailure) {
412                throw Vectors.illegalNorm(norm);
413            }
414            return null;
415        }
416    }
417
418    /** Class used to create high-accuracy sums of vectors. Each vector component is
419     * summed using an instance of {@link org.apache.commons.numbers.core.Sum}.
420    *
421    * <p>This class is mutable and not thread-safe.
422    * @see org.apache.commons.numbers.core.Sum
423    */
424    public static final class Sum extends EuclideanVectorSum<Vector1D> {
425
426        /** X component sum. */
427        private final org.apache.commons.numbers.core.Sum xsum;
428
429        /** Construct a new instance with the given initial value.
430         * @param initial initial value
431         */
432        Sum(final Vector1D initial) {
433            this.xsum = org.apache.commons.numbers.core.Sum.of(initial.x);
434        }
435
436        /** {@inheritDoc} */
437        @Override
438        public Sum add(final Vector1D vec) {
439            xsum.add(vec.x);
440            return this;
441        }
442
443        /** {@inheritDoc} */
444        @Override
445        public Sum addScaled(final double scale, final Vector1D vec) {
446            xsum.addProduct(scale, vec.x);
447            return this;
448        }
449
450        /** {@inheritDoc} */
451        @Override
452        public Vector1D get() {
453            return Vector1D.of(xsum.getAsDouble());
454        }
455
456        /** Create a new instance with an initial value set to the {@link Vector1D#ZERO zero vector}.
457         * @return new instance set to zero
458         */
459        public static Sum create() {
460            return new Sum(Vector1D.ZERO);
461        }
462
463        /** Construct a new instance with an initial value set to the argument.
464         * @param initial initial sum value
465         * @return new instance
466         */
467        public static Sum of(final Vector1D initial) {
468            return new Sum(initial);
469        }
470
471        /** Construct a new instance from multiple values.
472         * @param first first vector
473         * @param more additional vectors
474         * @return new instance
475         */
476        public static Sum of(final Vector1D first, final Vector1D... more) {
477            final Sum s = new Sum(first);
478            for (final Vector1D v : more) {
479                s.add(v);
480            }
481            return s;
482        }
483    }
484}