InvalidSignalException.java

package com.morphiqlabs.wavelet.exception;

import static com.morphiqlabs.wavelet.util.WaveletConstants.MAX_SAFE_POWER_OF_TWO;
import static com.morphiqlabs.wavelet.util.WaveletConstants.calculateNextPowerOfTwo;

/**
 * Exception thrown when an invalid signal is provided to wavelet operations.
 * This includes signals with invalid lengths, NaN/Infinity values, or null signals.
 */
public class InvalidSignalException extends WaveletTransformException {
    private static final long serialVersionUID = 202501150002L; // Invalid signal exception v1.0

    /**
     * Constructs a new invalid signal exception with the specified detail message.
     *
     * @param message the detail message
     */
    public InvalidSignalException(String message) {
        super(message);
    }

    /**
     * Constructs a new invalid signal exception with the specified error code and detail message.
     *
     * @param errorCode the error code
     * @param message   the detail message
     */
    public InvalidSignalException(ErrorCode errorCode, String message) {
        super(errorCode, message);
    }

    /**
     * Creates an exception for null signal input.
     *
     * @param parameterName the name of the parameter that was null
     * @return a new InvalidSignalException
     */
    public static InvalidSignalException nullSignal(String parameterName) {
        return new InvalidSignalException(ErrorCode.VAL_NULL_ARGUMENT,
                String.format("%s cannot be null.", parameterName));
    }

    /**
     * Creates an exception for empty signal input.
     *
     * @param parameterName the name of the parameter that was empty
     * @return a new InvalidSignalException
     */
    public static InvalidSignalException emptySignal(String parameterName) {
        return new InvalidSignalException(ErrorCode.VAL_EMPTY,
                String.format("%s cannot be empty.", parameterName));
    }

    /**
     * Creates an exception for signal length that is not a power of two.
     *
     * @param actualLength the actual length of the signal
     * @return a new InvalidSignalException
     */
    public static InvalidSignalException notPowerOfTwo(int actualLength) {
        final String baseMessage = "Signal length must be a power of two. Found length: %d.";
        String message = String.format(baseMessage, actualLength);

        if (actualLength <= 0) {
            // No suggestion for non-positive lengths
            return new InvalidSignalException(ErrorCode.VAL_NOT_POWER_OF_TWO, message);
        }

        // Calculate next power of two suggestion
        if (actualLength > 0 && actualLength <= MAX_SAFE_POWER_OF_TWO) {
            int nextPower = calculateNextPowerOfTwo(actualLength);
            message = String.format("%s Consider padding the signal to length %d.", message, nextPower);
        } else if (actualLength > MAX_SAFE_POWER_OF_TWO) {
            message = String.format("%s The signal is too large to pad to the next power of two.", message);
        }

        return new InvalidSignalException(ErrorCode.VAL_NOT_POWER_OF_TWO, message);
    }

    /**
     * Creates an exception for signal containing NaN values.
     *
     * @param parameterName the name of the parameter containing NaN
     * @param index         the index where NaN was found
     * @return a new InvalidSignalException
     */
    public static InvalidSignalException nanValue(String parameterName, int index) {
        return new InvalidSignalException(ErrorCode.VAL_NON_FINITE_VALUES,
                String.format("%s contains NaN at index %d. All values must be finite numbers.", parameterName, index));
    }

    /**
     * Creates an exception for signal containing infinity values.
     *
     * @param parameterName the name of the parameter containing infinity
     * @param index         the index where infinity was found
     * @param value         the infinite value (positive or negative infinity)
     * @return a new InvalidSignalException
     */
    public static InvalidSignalException infinityValue(String parameterName, int index, double value) {
        String sign = value > 0 ? "positive" : "negative";
        return new InvalidSignalException(ErrorCode.VAL_NON_FINITE_VALUES,
                String.format("%s contains %s infinity at index %d. All values must be finite numbers.",
                        parameterName, sign, index));
    }

    /**
     * Creates an exception for mismatched coefficient array lengths.
     *
     * @param approxLength length of approximation coefficients
     * @param detailLength length of detail coefficients
     * @return a new InvalidSignalException
     */
    public static InvalidSignalException mismatchedCoefficients(int approxLength, int detailLength) {
        return new InvalidSignalException(ErrorCode.VAL_LENGTH_MISMATCH,
                String.format("Approximation and detail coefficient arrays must have the same length. " +
                        "Found approximation length: %d, detail length: %d.", approxLength, detailLength));
    }

}