WaveletEvaluationUtils.java

package com.morphiqlabs.wavelet.util;

import com.morphiqlabs.wavelet.api.ContinuousWavelet;
import com.morphiqlabs.wavelet.cwt.MorletWavelet;
import com.morphiqlabs.wavelet.math.Complex;

/**
 * Utility class for evaluating continuous wavelets and working with complex-valued wavelets.
 * 
 * <p>This class provides static methods for evaluating continuous wavelets,
 * including complex-valued wavelets that have both real and imaginary components.</p>
 */
public final class WaveletEvaluationUtils {

    private WaveletEvaluationUtils() {
        // Utility class, prevent instantiation
    }

    /**
     * Evaluates a continuous wavelet at a given point and returns the result as a complex number.
     * 
     * <p>This method uses pattern matching with instanceof to handle different wavelet types:</p>
     * <ul>
     *   <li>For complex wavelets like Morlet: returns both real and imaginary parts</li>
     *   <li>For real-valued wavelets: returns the real part with zero imaginary part</li>
     * </ul>
     *
     * @param wavelet the continuous wavelet to evaluate (must not be null)
     * @param t the time/position parameter
     * @return the complex-valued wavelet evaluation at point t
     * @throws NullPointerException if wavelet is null
     */
    public static Complex evaluateAsComplex(ContinuousWavelet wavelet, double t) {
        if (wavelet == null) {
            throw new NullPointerException("The wavelet parameter cannot be null.");
        }

        // Use pattern matching with instanceof to handle different wavelet types
        if (wavelet instanceof MorletWavelet morlet) {
            // Morlet wavelet is complex-valued, so we need both real and imaginary parts
            double realPart = morlet.psi(t);
            double imaginaryPart = morlet.psiImaginary(t);
            return new Complex(realPart, imaginaryPart);
        } else {
            // For other continuous wavelets that are real-valued
            double realPart = wavelet.psi(t);
            return Complex.real(realPart);
        }
    }
    
    /**
     * Evaluates a continuous wavelet at a given point with scale and translation,
     * returning the result as a complex number.
     * 
     * @param wavelet the continuous wavelet to evaluate (must not be null)
     * @param t the time/position parameter
     * @param scale the scale parameter (a > 0)
     * @param translation the translation parameter
     * @return the complex-valued wavelet evaluation
     * @throws NullPointerException if wavelet is null
     * @throws IllegalArgumentException if scale {@literal <=} 0
     */
    public static Complex evaluateAsComplex(ContinuousWavelet wavelet, double t, double scale, double translation) {
        if (wavelet == null) {
            throw new NullPointerException("The wavelet parameter cannot be null.");
        }
        
        if (scale <= 0) {
            throw new IllegalArgumentException("Scale must be positive");
        }

        // Transform the time parameter according to scale and translation
        double transformedT = (t - translation) / scale;
        double scaleFactor = 1.0 / Math.sqrt(scale);
        
        // Use pattern matching with instanceof to handle different wavelet types
        if (wavelet instanceof MorletWavelet morlet) {
            // Morlet wavelet is complex-valued
            double realPart = scaleFactor * morlet.psi(transformedT);
            double imaginaryPart = scaleFactor * morlet.psiImaginary(transformedT);
            return new Complex(realPart, imaginaryPart);
        } else {
            // For other continuous wavelets that are real-valued
            double realPart = scaleFactor * wavelet.psi(transformedT);
            return Complex.real(realPart);
        }
    }
}