WaveletOperations.java

package com.morphiqlabs.wavelet;

import com.morphiqlabs.wavelet.internal.ScalarOps;
import com.morphiqlabs.wavelet.util.FftHeuristics;

/**
 * Public facade for wavelet transform operations (core module, scalar).
 *
 * <p>This class provides access to the core wavelet operations needed by
 * transform implementations while hiding internal implementation details.</p>
 *
 * <p><strong>SIMD Acceleration:</strong> Vector API–based acceleration lives in the optional
 * {@code vectorwave-extensions} module (Java 24 + incubator API). The core module remains
 * portable and scalar; use the extensions for high‑throughput SIMD paths.</p>
 */
public final class WaveletOperations {
    
    private WaveletOperations() {
        // Static utility class
    }
    
    /**
     * Performs circular convolution for MODWT without downsampling.
     * 
     * @param signal input signal
     * @param filter wavelet filter coefficients
     * @param output pre-allocated output array (same length as signal)
     */
    public static void circularConvolveMODWT(double[] signal, double[] filter, double[] output) {
        int n = signal.length;
        int l = filter.length;
        // Centralized heuristic: FFT wins when effective filter is large relative to N
        boolean useFFT = FftHeuristics.shouldUseModwtFFT(n, l);
        if (useFFT) {
            ScalarOps.circularConvolveMODWTFFT(signal, filter, output);
        } else {
            ScalarOps.circularConvolveMODWT(signal, filter, output);
        }
    }
    
    /**
     * Performs zero-padding convolution for MODWT without downsampling.
     * 
     * @param signal input signal
     * @param filter wavelet filter coefficients  
     * @param output pre-allocated output array (same length as signal)
     */
    public static void zeroPaddingConvolveMODWT(double[] signal, double[] filter, double[] output) {
        ScalarOps.zeroPaddingConvolveMODWT(signal, filter, output);
    }

    /**
     * Performs symmetric-extension convolution for MODWT without downsampling.
     *
     * @param signal input signal
     * @param filter wavelet filter coefficients
     * @param output pre-allocated output array (same length as signal)
     */
    public static void symmetricConvolveMODWT(double[] signal, double[] filter, double[] output) {
        ScalarOps.symmetricConvolveMODWT(signal, filter, output);
    }
    
    /**
     * Gets performance information about the current platform's capabilities.
     * 
     * @return performance information including vectorization status
     */
    public static PerformanceInfo getPerformanceInfo() {
        ScalarOps.PerformanceInfo internal = ScalarOps.getPerformanceInfo();
        String platformName = System.getProperty("os.arch", "unknown");
        // In core module, vectorization is always disabled
        String vectorSpecies = "N/A";
        String processingHint = "Vector API not available in core module";
        
        return new PerformanceInfo(
            false,  // vectorization always false in core module
            platformName,
            vectorSpecies,
            processingHint
        );
    }
    
    /**
     * Applies soft thresholding to wavelet coefficients.
     * 
     * <p>Soft thresholding shrinks coefficients toward zero by the threshold amount:
     * <ul>
     *   <li>If |x| ≤ threshold: result = 0</li>
     *   <li>If x {@literal >} threshold: result = x - threshold</li>
     *   <li>If x {@literal <} -threshold: result = x + threshold</li>
     * </ul>
     * 
     * @param coefficients the wavelet coefficients to threshold
     * @param threshold the threshold value (must be non-negative)
     * @return new array with thresholded coefficients
     * @throws IllegalArgumentException if threshold is negative
     */
    public static double[] softThreshold(double[] coefficients, double threshold) {
        if (coefficients == null) {
            throw new IllegalArgumentException("Coefficients array cannot be null");
        }
        if (threshold < 0) {
            throw new IllegalArgumentException("Threshold must be non-negative");
        }
        return ScalarOps.softThreshold(coefficients, threshold);
    }
    
    /**
     * Applies hard thresholding to wavelet coefficients.
     * 
     * <p>Hard thresholding sets coefficients to zero if their absolute value
     * is less than or equal to the threshold:
     * <ul>
     *   <li>If |x| ≤ threshold: result = 0</li>
     *   <li>If |x| > threshold: result = x</li>
     * </ul>
     * 
     * @param coefficients the wavelet coefficients to threshold
     * @param threshold the threshold value (must be non-negative)
     * @return new array with thresholded coefficients
     * @throws IllegalArgumentException if threshold is negative
     */
    public static double[] hardThreshold(double[] coefficients, double threshold) {
        if (coefficients == null) {
            throw new IllegalArgumentException("Coefficients array cannot be null");
        }
        if (threshold < 0) {
            throw new IllegalArgumentException("Threshold must be non-negative");
        }
        return ScalarOps.hardThreshold(coefficients, threshold);
    }
    
    /**
     * Performance information record about wavelet operations on this platform.
     *
     * @param vectorizationEnabled whether Vector API acceleration is enabled
     * @param platformName OS/arch name
     * @param vectorSpecies Vector species description (or N/A)
     * @param processingHint human-readable processing hint
     */
    public record PerformanceInfo(
        boolean vectorizationEnabled,
        String platformName,
        String vectorSpecies,
        String processingHint
    ) {
        /**
         * Returns a human-readable description of the performance capabilities.
         *
         * @return description of current performance configuration
         */
        public String description() {
            if (vectorizationEnabled) {
                return String.format("Vectorized operations enabled on %s with %s. %s",
                    platformName, vectorSpecies, processingHint);
            } else {
                return String.format("Scalar operations on %s. %s", 
                    platformName, processingHint);
            }
        }
        
        /**
         * Estimates the potential speedup for a given signal length.
         * 
         * @param signalLength the length of the signal
         * @return estimated speedup factor
         */
        public double estimateSpeedup(int signalLength) {
            if (signalLength < 0) {
                throw new IllegalArgumentException("Signal length cannot be negative");
            }
            if (!vectorizationEnabled || signalLength < 64) {
                return 1.0;
            }
            // Simplified estimate - real speedup depends on many factors
            return Math.min(4.0, 1.0 + (signalLength / 1024.0));
        }
    }
}