1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.geometry.euclidean.oned;
18
19 import java.util.Comparator;
20 import java.util.function.UnaryOperator;
21
22 import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
23 import org.apache.commons.geometry.euclidean.EuclideanVector;
24 import org.apache.commons.geometry.euclidean.EuclideanVectorSum;
25 import org.apache.commons.geometry.euclidean.internal.Vectors;
26 import org.apache.commons.numbers.core.Precision;
27
28 /** This class represents vectors and points in one-dimensional Euclidean space.
29 * Instances of this class are guaranteed to be immutable.
30 */
31 public class Vector1D extends EuclideanVector<Vector1D> {
32
33 /** Zero vector (coordinates: 0). */
34 public static final Vector1D ZERO = new Vector1D(0.0);
35
36 /** A vector with all coordinates set to NaN. */
37 public static final Vector1D NaN = new Vector1D(Double.NaN);
38
39 /** A vector with all coordinates set to positive infinity. */
40 public static final Vector1D POSITIVE_INFINITY =
41 new Vector1D(Double.POSITIVE_INFINITY);
42
43 /** A vector with all coordinates set to negative infinity. */
44 public static final Vector1D NEGATIVE_INFINITY =
45 new Vector1D(Double.NEGATIVE_INFINITY);
46
47 /** Comparator that sorts vectors in component-wise ascending order.
48 * Vectors are only considered equal if their coordinates match exactly.
49 * Null arguments are evaluated as being greater than non-null arguments.
50 */
51 public static final Comparator<Vector1D> COORDINATE_ASCENDING_ORDER = (a, b) -> {
52 int cmp = 0;
53
54 if (a != null && b != null) {
55 cmp = Double.compare(a.getX(), b.getX());
56 } else if (a != null) {
57 cmp = -1;
58 } else if (b != null) {
59 cmp = 1;
60 }
61
62 return cmp;
63 };
64
65 /** Abscissa (coordinate value). */
66 private final double x;
67
68 /** Simple constructor.
69 * @param x abscissa (coordinate value)
70 */
71 private Vector1D(final double x) {
72 this.x = x;
73 }
74
75 /**
76 * Returns the abscissa (coordinate value) of the instance.
77 * @return the abscissa value
78 */
79 public double getX() {
80 return x;
81 }
82
83 /** {@inheritDoc} */
84 @Override
85 public int getDimension() {
86 return 1;
87 }
88
89 /** {@inheritDoc} */
90 @Override
91 public boolean isNaN() {
92 return Double.isNaN(x);
93 }
94
95 /** {@inheritDoc} */
96 @Override
97 public boolean isInfinite() {
98 return !isNaN() && Double.isInfinite(x);
99 }
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 }