Tuple.java

/*
 * Licensed to the Hipparchus project under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The Hipparchus project licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.hipparchus.util;

import java.util.Arrays;

import org.hipparchus.CalculusFieldElement;
import org.hipparchus.Field;
import org.hipparchus.exception.MathIllegalArgumentException;

/**
 * This class allows to perform the same computation of all components of a Tuple at once.
 * @since 1.2
 */
public class Tuple implements CalculusFieldElement<Tuple> {

    /** Components of the tuple. */
    private final double[] values;

    /** Field the instance belongs to. */
    private final transient TupleField field;

    /** Creates a new instance from its components.
     * @param x components of the tuple
     */
    public Tuple(final double... x) {
        this(new TupleField(x.length), x.clone());
    }

    /** Creates a new instance from its components.
     * @param field field the instance belongs to
     * @param x components of the tuple (beware, it is <em>not</em> copied, it is shared with caller)
     */
    private Tuple(final TupleField field, final double[] x) {// NOPMD - storing user-supplied array is intentional and documented here
        this.values = x;
        this.field  = field;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple newInstance(final double value) {
        final Tuple t = new Tuple(field, new double[values.length]);
        Arrays.fill(t.values, value);
        return t;
    }

    /** Get the dimension of the tuple.
     * @return dimension of the tuple
     */
    public int getDimension() {
        return values.length;
    }

    /** Get one component of the tuple.
     * @param index index of the component, between 0 and {@link #getDimension() getDimension()} - 1
     * @return value of the component
     */
    public double getComponent(final int index) {
        return values[index];
    }

    /** Get all components of the tuple.
     * @return all components
     */
    public double[] getComponents() {
        return values.clone();
    }

    /** {@inheritDoc} */
    @Override
    public Field<Tuple> getField() {
        return field;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple add(final Tuple a) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = values[i] + a.values[i];
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple subtract(final Tuple a) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = values[i] - a.values[i];
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple negate() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = -values[i];
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple multiply(final Tuple a) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = values[i] * a.values[i];
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple multiply(final int n) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = values[i] * n;
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple square() {
        return multiply(this);
    }

    /** {@inheritDoc} */
    @Override
    public Tuple divide(final Tuple a) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = values[i] / a.values[i];
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple reciprocal() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = 1.0 / values[i];
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public boolean equals(final Object obj) {
        if (obj instanceof Tuple) {
            final Tuple that = (Tuple) obj;
            if (getDimension() == that.getDimension()) {
                boolean equals = true;
                for (int i = 0; i < values.length; ++i) {
                    equals &= Double.doubleToRawLongBits(values[i]) == Double.doubleToRawLongBits(that.values[i]);
                }
                return equals;
            }
        }
        return false;
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        return  0x34b1a444 + Arrays.hashCode(values);
    }

    /** {@inheritDoc} */
    @Override
    public double getReal() {
        return values[0];
    }

    /** {@inheritDoc} */
    @Override
    public Tuple add(final double a) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = values[i] + a;
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple subtract(final double a) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = values[i] - a;
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple multiply(final double a) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = values[i] * a;
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple divide(final double a) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = values[i] / a;
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple remainder(final double a) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.IEEEremainder(values[i], a);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple remainder(final Tuple a) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.IEEEremainder(values[i], a.values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple abs() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.abs(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple ceil() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.ceil(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple floor() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.floor(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple rint() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.rint(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple sign() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.signum(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple copySign(final Tuple sign) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.copySign(values[i], sign.values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple copySign(final double sign) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.copySign(values[i], sign);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple scalb(final int n) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.scalb(values[i], n);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple ulp() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.ulp(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple hypot(final Tuple y) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.hypot(values[i], y.values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple sqrt() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.sqrt(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple cbrt() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.cbrt(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple rootN(final int n) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            if (values[i] < 0) {
                result.values[i] = -FastMath.pow(-values[i], 1.0 / n);
            } else {
                result.values[i] = FastMath.pow(values[i], 1.0 / n);
            }
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple pow(final double p) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.pow(values[i], p);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple pow(final int n) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.pow(values[i], n);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple pow(final Tuple e) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.pow(values[i], e.values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple exp() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.exp(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple expm1() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.expm1(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple log() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.log(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple log1p() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.log1p(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple log10() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.log10(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple cos() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.cos(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple sin() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.sin(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public FieldSinCos<Tuple> sinCos() {
        final Tuple sin = new Tuple(field, new double[values.length]);
        final Tuple cos = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            final SinCos sc = FastMath.sinCos(values[i]);
            sin.values[i] = sc.sin();
            cos.values[i] = sc.cos();
        }
        return new FieldSinCos<>(sin, cos);
    }

    /** {@inheritDoc} */
    @Override
    public Tuple tan() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.tan(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple acos() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.acos(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple asin() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.asin(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple atan() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.atan(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple atan2(final Tuple x) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.atan2(values[i], x.values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple cosh() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.cosh(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple sinh() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.sinh(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public FieldSinhCosh<Tuple> sinhCosh() {
        final Tuple sinh = new Tuple(field, new double[values.length]);
        final Tuple cosh = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            final SinhCosh sch = FastMath.sinhCosh(values[i]);
            sinh.values[i] = sch.sinh();
            cosh.values[i] = sch.cosh();
        }
        return new FieldSinhCosh<>(sinh, cosh);
    }

    /** {@inheritDoc} */
    @Override
    public Tuple tanh() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.tanh(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple acosh() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.acosh(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple asinh() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.asinh(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple atanh() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.atanh(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple toDegrees() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.toDegrees(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple toRadians() {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = FastMath.toRadians(values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple linearCombination(final Tuple[] a, final Tuple[] b)
        throws MathIllegalArgumentException {
        final Tuple result = new Tuple(field, new double[values.length]);
        MathUtils.checkDimension(a.length, b.length);
        final double[] aDouble = new double[a.length];
        final double[] bDouble = new double[b.length];
        for (int i = 0; i < values.length; ++i) {
            for (int j = 0; j < a.length; ++j) {
                aDouble[j] = a[j].values[i];
                bDouble[j] = b[j].values[i];
            }
            result.values[i] = MathArrays.linearCombination(aDouble, bDouble);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple linearCombination(final double[] a, final Tuple[] b)
        throws MathIllegalArgumentException {
        final Tuple result = new Tuple(field, new double[values.length]);
        MathUtils.checkDimension(a.length, b.length);
        final double[] bDouble = new double[b.length];
        for (int i = 0; i < values.length; ++i) {
            for (int j = 0; j < a.length; ++j) {
                bDouble[j] = b[j].values[i];
            }
            result.values[i] = MathArrays.linearCombination(a, bDouble);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple linearCombination(final Tuple a1, final Tuple b1,
                                   final Tuple a2, final Tuple b2) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = MathArrays.linearCombination(a1.values[i], b1.values[i],
                                                            a2.values[i], b2.values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple linearCombination(final double a1, final Tuple b1,
                                   final double a2, final Tuple b2) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = MathArrays.linearCombination(a1, b1.values[i],
                                                            a2, b2.values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple linearCombination(final Tuple a1, final Tuple b1,
                                   final Tuple a2, final Tuple b2,
                                   final Tuple a3, final Tuple b3) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = MathArrays.linearCombination(a1.values[i], b1.values[i],
                                                            a2.values[i], b2.values[i],
                                                            a3.values[i], b3.values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple linearCombination(final double a1, final Tuple b1,
                                   final double a2, final Tuple b2,
                                   final double a3, final Tuple b3) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = MathArrays.linearCombination(a1, b1.values[i],
                                                            a2, b2.values[i],
                                                            a3, b3.values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple linearCombination(final Tuple a1, final Tuple b1,
                                   final Tuple a2, final Tuple b2,
                                   final Tuple a3, final Tuple b3,
                                   final Tuple a4, final Tuple b4) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = MathArrays.linearCombination(a1.values[i], b1.values[i],
                                                            a2.values[i], b2.values[i],
                                                            a3.values[i], b3.values[i],
                                                            a4.values[i], b4.values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple linearCombination(final double a1, final Tuple b1,
                                   final double a2, final Tuple b2,
                                   final double a3, final Tuple b3,
                                   final double a4, final Tuple b4) {
        final Tuple result = new Tuple(field, new double[values.length]);
        for (int i = 0; i < values.length; ++i) {
            result.values[i] = MathArrays.linearCombination(a1, b1.values[i],
                                                            a2, b2.values[i],
                                                            a3, b3.values[i],
                                                            a4, b4.values[i]);
        }
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public Tuple getPi() {
        final Tuple result = new Tuple(field, new double[values.length]);
        Arrays.fill(result.values, FastMath.PI);
        return result;
    }

    /** Field for {link Tuple} instances.
     */
    private static class TupleField implements Field<Tuple> {

        /** Constant function evaluating to 0.0. */
        private final Tuple zero;

        /** Constant function evaluating to 1.0. */
        private final Tuple one;

        /** Simple constructor.
         * @param dimension dimension of the tuple
         */
        TupleField(final int dimension) {
            final double[] zeroData = new double[dimension];
            final double[] oneData  = new double[dimension];
            Arrays.fill(oneData, 1.0);
            this.zero = new Tuple(this, zeroData);
            this.one  = new Tuple(this, oneData);
        }

        /** {@inheritDoc} */
        @Override
        public Tuple getZero() {
            return zero;
        }

        /** {@inheritDoc} */
        @Override
        public Tuple getOne() {
            return one;
        }

        /** {@inheritDoc} */
        @Override
        public Class<Tuple> getRuntimeClass() {
            return Tuple.class;
        }

        /** {@inheritDoc} */
        @Override
        public boolean equals(final Object other) {
            if (other instanceof TupleField) {
                return zero.getDimension() == ((TupleField) other).zero.getDimension();
            } else {
                return false;
            }
        }

        /** {@inheritDoc} */
        @Override
        public int hashCode() {
            return 0x6672493d ^ zero.getDimension();
        }

    }

}