ReverseBiorthogonalSpline.java

package com.morphiqlabs.wavelet.api;

/**
 * Reverse Biorthogonal spline wavelets (rbioNr.Nd) family.
 *
 * <p>These are the dual wavelets of the biorthogonal family, where the
 * decomposition and reconstruction filters are swapped compared to BIOR wavelets.</p>
 *
 * <p>In RBIO wavelets:
 * <ul>
 *   <li>Decomposition uses the shorter filter (faster analysis)</li>
 *   <li>Reconstruction uses the longer filter (smoother synthesis)</li>
 *   <li>Useful when reconstruction quality is more important than analysis precision</li>
 * </ul>
 *
 * <p>The naming convention rbioNr.Nd means:
 * <ul>
 *   <li>Nr: order of the spline for reconstruction (same as BIOR)</li>
 *   <li>Nd: order of the spline for decomposition (same as BIOR)</li>
 * </ul>
 * Note: The filters are swapped compared to BIOR, but the naming convention remains the same.
 */
public final class ReverseBiorthogonalSpline implements BiorthogonalWavelet {
    
    /** Reverse Biorthogonal 1.1 wavelet (Haar-like). */
    public static final ReverseBiorthogonalSpline RBIO1_1 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR1_1, "rbio1.1");
    
    /** Reverse Biorthogonal 1.3 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO1_3 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR1_3, "rbio1.3");
    
    /** Reverse Biorthogonal 1.5 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO1_5 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR1_5, "rbio1.5");
    
    /** Reverse Biorthogonal 2.2 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO2_2 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR2_2, "rbio2.2");
    
    /** Reverse Biorthogonal 2.4 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO2_4 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR2_4, "rbio2.4");
    
    /** Reverse Biorthogonal 2.6 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO2_6 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR2_6, "rbio2.6");
    
    /** Reverse Biorthogonal 2.8 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO2_8 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR2_8, "rbio2.8");
    
    /** Reverse Biorthogonal 3.1 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO3_1 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR3_1, "rbio3.1");
    
    /** Reverse Biorthogonal 3.3 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO3_3 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR3_3, "rbio3.3");
    
    /** Reverse Biorthogonal 3.5 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO3_5 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR3_5, "rbio3.5");
    
    /** Reverse Biorthogonal 3.7 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO3_7 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR3_7, "rbio3.7");
    
    /** Reverse Biorthogonal 3.9 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO3_9 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR3_9, "rbio3.9");
    
    /** Reverse Biorthogonal 4.4 wavelet (CDF 9/7). */
    public static final ReverseBiorthogonalSpline RBIO4_4 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR4_4, "rbio4.4");
    
    /** Reverse Biorthogonal 5.5 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO5_5 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR5_5, "rbio5.5");
    
    /** Reverse Biorthogonal 6.8 wavelet. */
    public static final ReverseBiorthogonalSpline RBIO6_8 = new ReverseBiorthogonalSpline(
            BiorthogonalSpline.BIOR6_8, "rbio6.8");
    
    private final BiorthogonalSpline originalBior;
    private final String name;
    
    private ReverseBiorthogonalSpline(BiorthogonalSpline original, String name) {
        this.originalBior = original;
        this.name = name;
    }
    
    @Override
    public String name() {
        return name;
    }
    
    @Override
    public String description() {
        String originalDesc = originalBior.description();
        return originalDesc.replace("Biorthogonal", "Reverse Biorthogonal");
    }
    
    // RBIO swaps the decomposition and reconstruction filters
    
    @Override
    public double[] lowPassDecomposition() {
        // RBIO uses BIOR's reconstruction filter for decomposition
        return originalBior.lowPassReconstruction();
    }
    
    @Override
    public double[] highPassDecomposition() {
        // RBIO uses BIOR's reconstruction high-pass for decomposition
        return originalBior.highPassReconstruction();
    }
    
    @Override
    public double[] lowPassReconstruction() {
        // RBIO uses BIOR's decomposition filter for reconstruction
        return originalBior.lowPassDecomposition();
    }
    
    @Override
    public double[] highPassReconstruction() {
        // RBIO uses BIOR's decomposition high-pass for reconstruction
        return originalBior.highPassDecomposition();
    }
    
    @Override
    public int vanishingMoments() {
        // Vanishing moments are swapped in RBIO
        return originalBior.dualVanishingMoments();
    }
    
    @Override
    public int dualVanishingMoments() {
        // Dual vanishing moments are swapped in RBIO
        return originalBior.vanishingMoments();
    }
    
    @Override
    public int splineOrder() {
        // Spline order remains the same
        return originalBior.splineOrder();
    }
    
    @Override
    public boolean isSymmetric() {
        // Symmetry property is preserved
        return originalBior.isSymmetric();
    }
    
    @Override
    public int reconstructionLength() {
        // In RBIO, reconstruction uses the original decomposition filter
        return originalBior.lowPassDecomposition().length;
    }
    
    @Override
    public WaveletType getType() {
        return WaveletType.BIORTHOGONAL;
    }
    
    /**
     * Get the reconstruction scaling factor.
     * 
     * For RBIO wavelets, the reconstruction scaling is inverted compared to BIOR
     * because the filter roles are swapped. When BIOR uses a scaling factor s
     * for reconstruction, RBIO needs 1/s to maintain perfect reconstruction
     * after the filter swap.
     *
     * @return the reconstruction scaling factor
     */
    public double getReconstructionScale() {
        double biorScale = originalBior.getReconstructionScale();
        // Invert the scaling factor for RBIO
        // Special case: if scale is 1.0, it remains 1.0
        if (Math.abs(biorScale - 1.0) < 1e-10) {
            return 1.0;
        }
        return 1.0 / biorScale;
    }
    
    /**
     * Get the group delay of the wavelet.
     * 
     * The group delay is recalculated for RBIO wavelets because the filter
     * lengths are swapped. The delay is determined by the combined length
     * of the analysis and synthesis filters.
     * 
     * Formula: delay = (length(h0_decomp) - 1)/2 + (length(h0_recon) - 1)/2 - 1
     *
     * @return the group delay in samples
     */
    public int getGroupDelay() {
        // Calculate delay for the swapped filter configuration
        int decompLength = lowPassDecomposition().length;
        int reconLength = lowPassReconstruction().length;
        
        // Apply the standard group delay formula
        int delay = ((decompLength - 1) + (reconLength - 1)) / 2 - 1;
        
        // Ensure non-negative delay
        return Math.max(0, delay);
    }
    
    /**
     * Get the original BIOR wavelet this RBIO is based on.
     *
     * @return the original BiorthogonalSpline wavelet
     */
    public BiorthogonalSpline getOriginalBior() {
        return originalBior;
    }
}