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.core.precision.DoublePrecisionContext;
24 import org.apache.commons.geometry.euclidean.EuclideanVector;
25 import org.apache.commons.geometry.euclidean.internal.Vectors;
26 import org.apache.commons.numbers.angle.PlaneAngleRadians;
27 import org.apache.commons.numbers.arrays.LinearCombination;
28
29 /** This class represents vectors and points in one-dimensional Euclidean space.
30 * Instances of this class are guaranteed to be immutable.
31 */
32 public class Vector1D extends EuclideanVector<Vector1D> {
33
34 /** Zero vector (coordinates: 0). */
35 public static final Vector1D ZERO = new Vector1D(0.0);
36
37 // CHECKSTYLE: stop ConstantName
38 /** A vector with all coordinates set to NaN. */
39 public static final Vector1D NaN = new Vector1D(Double.NaN);
40 // CHECKSTYLE: resume ConstantName
41
42 /** A vector with all coordinates set to positive infinity. */
43 public static final Vector1D POSITIVE_INFINITY =
44 new Vector1D(Double.POSITIVE_INFINITY);
45
46 /** A vector with all coordinates set to negative infinity. */
47 public static final Vector1D NEGATIVE_INFINITY =
48 new Vector1D(Double.NEGATIVE_INFINITY);
49
50 /** Comparator that sorts vectors in component-wise ascending order.
51 * Vectors are only considered equal if their coordinates match exactly.
52 * Null arguments are evaluated as being greater than non-null arguments.
53 */
54 public static final Comparator<Vector1D> COORDINATE_ASCENDING_ORDER = (a, b) -> {
55 int cmp = 0;
56
57 if (a != null && b != null) {
58 cmp = Double.compare(a.getX(), b.getX());
59 } else if (a != null) {
60 cmp = -1;
61 } else if (b != null) {
62 cmp = 1;
63 }
64
65 return cmp;
66 };
67
68 /** Abscissa (coordinate value). */
69 private final double x;
70
71 /** Simple constructor.
72 * @param x abscissa (coordinate value)
73 */
74 private Vector1D(final double x) {
75 this.x = x;
76 }
77
78 /**
79 * Returns the abscissa (coordinate value) of the instance.
80 * @return the abscissa value
81 */
82 public double getX() {
83 return x;
84 }
85
86 /** {@inheritDoc} */
87 @Override
88 public int getDimension() {
89 return 1;
90 }
91
92 /** {@inheritDoc} */
93 @Override
94 public boolean isNaN() {
95 return Double.isNaN(x);
96 }
97
98 /** {@inheritDoc} */
99 @Override
100 public boolean isInfinite() {
101 return !isNaN() && Double.isInfinite(x);
102 }
103
104 /** {@inheritDoc} */
105 @Override
106 public boolean isFinite() {
107 return Double.isFinite(x);
108 }
109
110 /** {@inheritDoc} */
111 @Override
112 public Vector1D vectorTo(final Vector1D v) {
113 return v.subtract(this);
114 }
115
116 /** {@inheritDoc} */
117 @Override
118 public Unit directionTo(final Vector1D v) {
119 return vectorTo(v).normalize();
120 }
121
122 /** {@inheritDoc} */
123 @Override
124 public Vector1D lerp(final Vector1D p, final double t) {
125 return linearCombination(1.0 - t, this, t, p);
126 }
127
128 /** {@inheritDoc} */
129 @Override
130 public Vector1D getZero() {
131 return ZERO;
132 }
133
134 /** {@inheritDoc} */
135 @Override
136 public double norm() {
137 return Vectors.norm(x);
138 }
139
140 /** {@inheritDoc} */
141 @Override
142 public double normSq() {
143 return Vectors.normSq(x);
144 }
145
146 /** {@inheritDoc} */
147 @Override
148 public Vector1D withNorm(final double magnitude) {
149 getCheckedNorm(); // validate our norm value
150 return (x > 0.0) ? new Vector1D(magnitude) : new Vector1D(-magnitude);
151 }
152
153 /** {@inheritDoc} */
154 @Override
155 public Vector1D add(final Vector1D v) {
156 return new Vector1D(x + v.x);
157 }
158
159 /** {@inheritDoc} */
160 @Override
161 public Vector1D add(final double factor, final Vector1D v) {
162 return new Vector1D(x + (factor * v.x));
163 }
164
165 /** {@inheritDoc} */
166 @Override
167 public Vector1D subtract(final Vector1D v) {
168 return new Vector1D(x - v.x);
169 }
170
171 /** {@inheritDoc} */
172 @Override
173 public Vector1D subtract(final double factor, final Vector1D v) {
174 return new Vector1D(x - (factor * v.x));
175 }
176
177 /** {@inheritDoc} */
178 @Override
179 public Vector1D negate() {
180 return new Vector1D(-x);
181 }
182
183 /** {@inheritDoc} */
184 @Override
185 public Unit normalize() {
186 return Unit.from(x);
187 }
188
189 /** {@inheritDoc} */
190 @Override
191 public Vector1D multiply(final double a) {
192 return new Vector1D(a * x);
193 }
194
195 /** {@inheritDoc} */
196 @Override
197 public double distance(final Vector1D v) {
198 return Vectors.norm(x - v.x);
199 }
200
201 /** {@inheritDoc} */
202 @Override
203 public double distanceSq(final Vector1D v) {
204 return Vectors.normSq(x - v.x);
205 }
206
207 /** {@inheritDoc} */
208 @Override
209 public double dot(final Vector1D v) {
210 return x * v.x;
211 }
212
213 /** {@inheritDoc}
214 * <p>For the one-dimensional case, this method returns 0 if the vector x values have
215 * the same sign and {@code pi} if they are opposite.</p>
216 */
217 @Override
218 public double angle(final Vector1D v) {
219 // validate the norm values
220 getCheckedNorm();
221 v.getCheckedNorm();
222
223 final double sig1 = Math.signum(x);
224 final double sig2 = Math.signum(v.x);
225
226 // the angle is 0 if the x value signs are the same and pi if not
227 return (sig1 == sig2) ? 0.0 : PlaneAngleRadians.PI;
228 }
229
230 /** Convenience method to apply a function to this vector. This
231 * can be used to transform the vector inline with other methods.
232 * @param fn the function to apply
233 * @return the transformed vector
234 */
235 public Vector1D transform(final UnaryOperator<Vector1D> fn) {
236 return fn.apply(this);
237 }
238
239 /** {@inheritDoc} */
240 @Override
241 public boolean eq(final Vector1D vec, final DoublePrecisionContext precision) {
242 return precision.eq(x, vec.x);
243 }
244
245 /**
246 * Get a hashCode for the vector.
247 * <p>All NaN values have the same hash code.</p>
248 *
249 * @return a hash code value for this object
250 */
251 @Override
252 public int hashCode() {
253 if (isNaN()) {
254 return 857;
255 }
256 return 403 * Double.hashCode(x);
257 }
258
259 /**
260 * Test for the equality of two vectors.
261 * <p>
262 * If all coordinates of two vectors are exactly the same, and none are
263 * <code>Double.NaN</code>, the two vectors are considered to be equal.
264 * </p>
265 * <p>
266 * <code>NaN</code> coordinates are considered to globally affect the vector
267 * and be equal to each other - i.e, if either (or all) coordinates of the
268 * vector are equal to <code>Double.NaN</code>, the vector is equal to
269 * {@link #NaN}.
270 * </p>
271 *
272 * @param other Object to test for equality to this
273 * @return true if two vector objects are equal, false if
274 * object is null, not an instance of Vector1D, or
275 * not equal to this Vector1D instance
276 *
277 */
278 @Override
279 public boolean equals(final Object other) {
280 if (this == other) {
281 return true;
282 }
283 if (other instanceof Vector1D) {
284 final Vector1D rhs = (Vector1D) other;
285 if (rhs.isNaN()) {
286 return this.isNaN();
287 }
288
289 return Double.compare(x, rhs.x) == 0;
290 }
291 return false;
292 }
293
294 /** {@inheritDoc} */
295 @Override
296 public String toString() {
297 return SimpleTupleFormat.getDefault().format(x);
298 }
299
300 /** Returns a vector with the given coordinate value.
301 * @param x vector coordinate
302 * @return vector instance
303 */
304 public static Vector1D of(final double x) {
305 return new Vector1D(x);
306 }
307
308 /** Parses the given string and returns a new vector instance. The expected string
309 * format is the same as that returned by {@link #toString()}.
310 * @param str the string to parse
311 * @return vector instance represented by the string
312 * @throws IllegalArgumentException if the given string has an invalid format
313 */
314 public static Vector1D parse(final String str) {
315 return SimpleTupleFormat.getDefault().parse(str, Vector1D::new);
316 }
317
318 /** Returns a vector consisting of the linear combination of the inputs.
319 * <p>
320 * A linear combination is the sum of all of the inputs multiplied by their
321 * corresponding scale factors.
322 * </p>
323 *
324 * @param a scale factor for first vector
325 * @param c first vector
326 * @return vector calculated by {@code a * c}
327 */
328 public static Vector1D linearCombination(final double a, final Vector1D c) {
329 return new Vector1D(a * c.x);
330 }
331
332 /** Returns a vector consisting of the linear combination of the inputs.
333 * <p>
334 * A linear combination is the sum of all of the inputs multiplied by their
335 * corresponding scale factors.
336 * </p>
337 *
338 * @param a1 scale factor for first vector
339 * @param v1 first vector
340 * @param a2 scale factor for second vector
341 * @param v2 second vector
342 * @return vector calculated by {@code (a1 * v1) + (a2 * v2)}
343 */
344 public static Vector1D linearCombination(final double a1, final Vector1D v1,
345 final double a2, final Vector1D v2) {
346
347 return new Vector1D(
348 LinearCombination.value(a1, v1.x, a2, v2.x));
349 }
350
351 /** Returns a vector consisting of the linear combination of the inputs.
352 * <p>
353 * A linear combination is the sum of all of the inputs multiplied by their
354 * corresponding scale factors.
355 * </p>
356 *
357 * @param a1 scale factor for first vector
358 * @param v1 first vector
359 * @param a2 scale factor for second vector
360 * @param v2 second vector
361 * @param a3 scale factor for third vector
362 * @param v3 third vector
363 * @return vector calculated by {@code (a1 * v1) + (a2 * v2) + (a3 * v3)}
364 */
365 public static Vector1D linearCombination(final double a1, final Vector1D v1,
366 final double a2, final Vector1D v2,
367 final double a3, final Vector1D v3) {
368
369 return new Vector1D(
370 LinearCombination.value(a1, v1.x, a2, v2.x, a3, v3.x));
371 }
372
373 /** Returns a vector consisting of the linear combination of the inputs.
374 * <p>
375 * A linear combination is the sum of all of the inputs multiplied by their
376 * corresponding scale factors.
377 * </p>
378 *
379 * @param a1 scale factor for first vector
380 * @param v1 first vector
381 * @param a2 scale factor for second vector
382 * @param v2 second vector
383 * @param a3 scale factor for third vector
384 * @param v3 third vector
385 * @param a4 scale factor for fourth vector
386 * @param v4 fourth vector
387 * @return vector calculated by {@code (a1 * v1) + (a2 * v2) + (a3 * v3) + (a4 * v4)}
388 */
389 public static Vector1D linearCombination(final double a1, final Vector1D v1,
390 final double a2, final Vector1D v2,
391 final double a3, final Vector1D v3,
392 final double a4, final Vector1D v4) {
393
394 return new Vector1D(
395 LinearCombination.value(a1, v1.x, a2, v2.x, a3, v3.x, a4, v4.x));
396 }
397
398 /**
399 * Represent unit vectors.
400 * This allows optimizations to be performed for certain operations.
401 */
402 public static final class Unit extends Vector1D {
403 /** Unit vector (coordinates: 1). */
404 public static final Unit PLUS = new Unit(1d);
405 /** Negation of unit vector (coordinates: -1). */
406 public static final Unit MINUS = new Unit(-1d);
407
408 /** Simple constructor. Callers are responsible for ensuring that the given
409 * values represent a normalized vector.
410 * @param x abscissa (first coordinate value)
411 */
412 private Unit(final double x) {
413 super(x);
414 }
415
416 /**
417 * Creates a normalized vector.
418 *
419 * @param x Vector coordinate.
420 * @return a vector whose norm is 1.
421 * @throws IllegalArgumentException if the norm of the given value is zero, NaN, or infinite
422 */
423 public static Unit from(final double x) {
424 Vectors.checkedNorm(Vectors.norm(x));
425 return x > 0 ? PLUS : MINUS;
426 }
427
428 /**
429 * Creates a normalized vector.
430 *
431 * @param v Vector.
432 * @return a vector whose norm is 1.
433 * @throws IllegalArgumentException if the norm of the given value is zero, NaN, or infinite
434 */
435 public static Unit from(final Vector1D v) {
436 return v instanceof Unit ?
437 (Unit) v :
438 from(v.getX());
439 }
440
441 /** {@inheritDoc} */
442 @Override
443 public double norm() {
444 return 1;
445 }
446
447 /** {@inheritDoc} */
448 @Override
449 public double normSq() {
450 return 1;
451 }
452
453 /** {@inheritDoc} */
454 @Override
455 public Unit normalize() {
456 return this;
457 }
458
459 /** {@inheritDoc} */
460 @Override
461 public Vector1D withNorm(final double mag) {
462 return multiply(mag);
463 }
464
465 /** {@inheritDoc} */
466 @Override
467 public Vector1D negate() {
468 return this == PLUS ? MINUS : PLUS;
469 }
470 }
471 }