FirstDegreePolynomialRootsEstimator.java

/*
 * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
 *
 * Licensed 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
 *
 *         http://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 com.irurueta.numerical.roots;

import com.irurueta.algebra.Complex;
import com.irurueta.numerical.LockedException;
import com.irurueta.numerical.NotAvailableException;
import com.irurueta.numerical.NotReadyException;

/**
 * Class to estimate the root of a first degree polynomial along with other
 * polynomial properties.
 * A first degree polynomial is defined by its parameters as p(x) = a * x + b,
 * hence the polynomial can be simply be defined by an array of length 2 [b, a].
 */
@SuppressWarnings("DuplicatedCode")
public class FirstDegreePolynomialRootsEstimator extends PolynomialRootsEstimator {

    /**
     * Constant defining machine precision.
     */
    public static final double EPS = 1e-10;

    /**
     * Number of parameters valid for a first degree polynomial.
     */
    public static final int VALID_POLY_PARAMS_LENGTH = 2;

    /**
     * Array containing parameters of a first degree polynomial.
     */
    private double[] realPolyParams;

    /**
     * Empty constructor.
     */
    public FirstDegreePolynomialRootsEstimator() {
        super();
        realPolyParams = null;
    }

    /**
     * Constructor.
     *
     * @param polyParams Array containing polynomial parameters.
     * @throws IllegalArgumentException Raised if the length of the provided
     *                                  array is not valid.
     */
    public FirstDegreePolynomialRootsEstimator(final double[] polyParams) {
        super();
        internalSetPolynomialParameters(polyParams);
    }

    /**
     * Set array of first degree polynomial parameters.
     * A first degree polynomial is defined by p(x) = a * x + b, and the array
     * must be provided as [b, a].
     * Note: This class only supports real polynomial parameters.
     *
     * @param polyParams Array containing polynomial parameters.
     * @throws LockedException          Raised if this instance is locked.
     * @throws IllegalArgumentException Raised if the length of the provided
     *                                  array is not valid.
     */
    public void setPolynomialParameters(final double[] polyParams) throws LockedException {
        if (isLocked()) {
            throw new LockedException();
        }
        internalSetPolynomialParameters(polyParams);
    }

    /**
     * Internal method to set array of first degree polynomial parameters.
     * A first degree polynomial is defined by p(x) = a * x + b, and the array
     * must be provided as [b, a].
     * Note: This class only supports real polynomial parameters.
     * This method does not check if this instance is locked.
     *
     * @param polyParams Array containing polynomial parameters.
     * @throws IllegalArgumentException Raised if the length of the provided
     *                                  array is not valid.
     */
    private void internalSetPolynomialParameters(final double[] polyParams) {
        if (polyParams.length < VALID_POLY_PARAMS_LENGTH) {
            throw new IllegalArgumentException();
        }
        if (!isFirstDegree(polyParams)) {
            throw new IllegalArgumentException();
        }

        this.realPolyParams = polyParams;
    }

    /**
     * Returns array of first degree polynomial parameters.
     * A first degree polynomial is defined by p(x) = a * x + b, and the array
     * is returned as [b, a].
     * Note: This class only supports real polynomial parameters.
     *
     * @return Array of first degree polynomial parameters.
     * @throws NotAvailableException if parameters are not available for retrieval.
     */
    public double[] getRealPolynomialParameters() throws NotAvailableException {
        if (!arePolynomialParametersAvailable()) {
            throw new NotAvailableException();
        }
        return realPolyParams;
    }

    /**
     * Returns boolean indicating whether REAL polynomial parameters have been
     * provided and is available for retrieval.
     * Note: This class only supports real polynomial parameters.
     *
     * @return True if available, false otherwise.
     */
    @Override
    public boolean arePolynomialParametersAvailable() {
        return realPolyParams != null;
    }

    /**
     * This method will always raise a NotAvailableException because this class
     * only supports REAL polynomial parameters.
     *
     * @return throws NotAvailableException.
     * @throws NotAvailableException always throws this exception.
     */
    @Override
    public Complex[] getPolynomialParameters() throws NotAvailableException {
        throw new NotAvailableException();
    }


    /**
     * This method will always raise an IllegalArgumentException because this
     * class only supports REAL polynomial parameters.
     */
    @Override
    protected void internalSetPolynomialParameters(final Complex[] polyParams) {
        // complex values are not supported
        throw new IllegalArgumentException();
    }

    /**
     * Estimates the root of provided polynomial.
     *
     * @throws LockedException   Raised if this instance is locked estimating a
     *                           root.
     * @throws NotReadyException Raised if this instance is not ready because
     *                           polynomial parameters have not been provided.
     */
    @Override
    public void estimate() throws LockedException, NotReadyException {

        if (isLocked()) {
            throw new LockedException();
        }
        if (!isReady()) {
            throw new NotReadyException();
        }

        locked = true;

        roots = new Complex[VALID_POLY_PARAMS_LENGTH - 1];

        final var b = realPolyParams[0];
        final var a = realPolyParams[1];

        final var x = solveLinear(a, b);

        roots[0] = new Complex(x, 0.0);

        locked = false;
    }

    /**
     * Returns boolean indicating whether provided array of polynomial
     * parameters correspond to a valid first degree polynomial.
     * A first degree polynomial is defined by p(x) = a * x + b, and the array
     * is returned as [b, a].
     * Note: This class only supports real polynomial parameters
     *
     * @param polyParams Array containing polynomial parameters
     * @return True if is a first degree polynomial, false otherwise
     */
    public static boolean isFirstDegree(final double[] polyParams) {
        final var length = polyParams.length;
        if (length >= VALID_POLY_PARAMS_LENGTH && Math.abs(polyParams[VALID_POLY_PARAMS_LENGTH - 1]) > EPS) {
            for (var i = VALID_POLY_PARAMS_LENGTH; i < length; i++) {
                if (Math.abs(polyParams[i]) > EPS) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    /**
     * Returns boolean indicating whether polynomial parameters provided to this
     * instance correspond to a valid first degree polynomial.
     * A first degree polynomial is defined by p(x) = a * x + b, and the array
     * is returned as [b, a].
     * Note: This class only supports real polynomial parameters.
     *
     * @return True if is a first degree polynomial, false otherwise.
     * @throws NotReadyException Raised if this instance is not ready because
     *                           an array of polynomial parameters has not yet been provided.
     */
    public boolean isFirstDegree() throws NotReadyException {
        if (!isReady()) {
            throw new NotReadyException();
        }
        return isFirstDegree(realPolyParams);
    }

    /**
     * Returns boolean indicating whether estimated root is real.
     * Because this class only accepts real polynomial parameters, then the
     * estimated root will always be real, and consequently this method always
     * returns true.
     *
     * @return True if estimated root is real, false otherwise.
     */
    public boolean isRealSolution() {
        return true;
    }

    /**
     * Internal method to estimate a root on a first degree polynomial.
     *
     * @param a A parameter.
     * @param b B parameter.
     * @return Root.
     */
    private double solveLinear(final double a, final double b) {
        //a * x + b = 0
        return -b / a;
    }
}