/** * JWave - Java implementation of wavelet transform algorithms * * Copyright 2010-2012 Christian Scheiblich * * 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. * * This file AncientEgyptianDecomposition.java is part of JWave. * * @author Christian Scheiblich * date 14.08.2010 10:43:28 * contact graetz@mailfish.de */ package math.transform.jwave.handlers; /** * A wavelet transform method for arrays and signals of arbitrary lengths, even * odd lengths. The array is decomposed in several parts of optimal lengths by * applying the ancient Egyptian decomposition. Hereby, the array or signal is * decomposed to the largest possible sub arrays of two the power of p. * Afterwards each sub array is transformed forward and copied back to the * discrete position of the input array. The reverse transform applies the same * vice versa. In more detail the ancient Egyptian Multiplication can be easily * explained by the following example: 42 = 2^5 + 2^3 + 2^1 = 32 + 8 + 2. * However, an array or signal of odd length produces the smallest ancient * Egyptian multiplier 2^0 which is actually 1. Therefore, the matching sub * array or signal is untouched an the coefficient is actually the wavelet * coefficient of wavelet space of level 0. For an "orthonormal" wavelet this * holds. See: http://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication * * @date 14.08.2010 10:43:28 * @author Christian Scheiblich */ public class AncientEgyptianDecomposition extends WaveletTransform { /** * The selected Transform (FWT or WPT) used for the sub arrays of the ancient * Egyptian decomposition. Actually, this displays somehow the Composite * Pattern of software design pattern. See: * http://en.wikipedia.org/wiki/Composite_pattern#Java */ protected WaveletTransform _waveTransform; /** * Constructor taking the * * @date 14.08.2010 10:43:28 * @author Christian Scheiblich */ public AncientEgyptianDecomposition( WaveletTransform waveTransform ) { _waveTransform = waveTransform; } // FastWaveletTransformArbitrary /** * The method converts a positive integer to the ancient Egyptian multipliers * which are actually the multipliers to display the number by a sum of the * largest possible powers of two. E.g. 42 = 2^5 + 2^3 + 2^1 = 32 + 8 + 2. * However, odd numbers always 2^0 = 1 as the last entry. Also see: * http://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication * * @date 14.08.2010 13:40:54 * @author Christian Scheiblich * @param number * positive integer * @return an integer array keeping the ancient Egyptian multipliers */ public int[ ] convertInteger2AncientEgyptianMultiplipliers( int number ) { if( number < 1 ) return null; int power = getExponent( (double)number ); int[ ] tmpArr = new int[ power + 1 ]; // max no of possible multipliers int pos = 0; double current = (double)number; while( current >= 1. ) { power = getExponent( current ); tmpArr[ pos ] = power; current = current - scalb( 1., power ); // 1. * 2 ^ power pos++; } // while int[ ] ancientEgyptianMultipliers = new int[ pos ]; // shrink for( int c = 0; c < pos; c++ ) ancientEgyptianMultipliers[ c ] = tmpArr[ c ]; return ancientEgyptianMultipliers; } // convertInteger2AncientEgyptianMultiplipliers /** * The method converts a list of ancient Egyptian multipliers to the * corresponding integer. The ancient Egyptian multipliers are actually the * multipliers to display am integer by a sum of the largest possible powers * of two. E.g. 43 = 2^5 + 2^3 + 2^1 + 1^0 = 32 + 8 + 2 + 1. Also see: * http://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication * * @date 14.08.2010 16:48:44 * @author Christian Scheiblich * @param ancientEgyptianMultipliers * an integer array keeping the ancient Egyptian multipliers * @return resulting integer as sum of powers of two */ public int convertAncientEgyptianMultiplipliers2Integer( int[ ] ancientEgyptianMultipliers ) { int number = 0; int noOfAncientEgyptianMultipliers = ancientEgyptianMultipliers.length; for( int m = 0; m < noOfAncientEgyptianMultipliers; m++ ) { int ancientEgyptianMultiplier = ancientEgyptianMultipliers[ m ]; number += (int)scalb( 1., ancientEgyptianMultiplier ); // 1. * 2^p } // m return number; } // convertAncientEgyptianMultiplipliers2Integer /** * This forward method decomposes the given array of arbitrary length to sub * arrays while applying the ancient Egyptian decomposition. Each sub array is * transformed by the selected basic transform and the resulting wavelet * coefficients are copied back to their original discrete positions. * * @date 14.08.2010 10:43:28 * @author Christian Scheiblich * @see math.transform.jwave.handlers.BasicTransform#forward(double[]) */ @Override public double[ ] forwardWavelet( double[ ] arrTime ) { double[ ] arrHilb = new double[ arrTime.length ]; int[ ] ancientEgyptianMultipliers = convertInteger2AncientEgyptianMultiplipliers( arrTime.length ); int offSet = 0; for( int m = 0; m < ancientEgyptianMultipliers.length; m++ ) { int ancientEgyptianMultiplier = ancientEgyptianMultipliers[ m ]; int arrTimeSubLength = (int)scalb( 1., ancientEgyptianMultiplier ); double[ ] arrTimeSub = new double[ arrTimeSubLength ]; for( int i = 0; i < arrTimeSub.length; i++ ) arrTimeSub[ i ] = arrTime[ i + offSet ]; double[ ] arrHilbSub = _waveTransform.forward( arrTimeSub ); for( int i = 0; i < arrHilbSub.length; i++ ) arrHilb[ i + offSet ] = arrHilbSub[ i ]; offSet += arrHilbSub.length; } // m - no of sub transforms return arrHilb; } // forward /** * This reverse method awaits an array of arbitrary length in wavelet space * keeping the wavelet already decomposed by the ancient Egyptian * decomposition. Therefore, each of the existing sub arrays of length 2^p is * reverse transformed by the selected basic transform and the resulting * coefficients of time domain are copied back to their original discrete * positions. * * @date 14.08.2010 10:43:28 * @author Christian Scheiblich * @see math.transform.jwave.handlers.BasicTransform#reverse(double[]) */ @Override public double[ ] reverseWavelet( double[ ] arrHilb ) { double[ ] arrTime = new double[ arrHilb.length ]; int[ ] ancientEgyptianMultipliers = convertInteger2AncientEgyptianMultiplipliers( arrHilb.length ); int offSet = 0; for( int m = 0; m < ancientEgyptianMultipliers.length; m++ ) { int ancientEgyptianMultiplier = ancientEgyptianMultipliers[ m ]; int arrHilbSubLength = (int)scalb( 1., ancientEgyptianMultiplier ); double[ ] arrHilbSub = new double[ arrHilbSubLength ]; for( int i = 0; i < arrHilbSub.length; i++ ) arrHilbSub[ i ] = arrHilb[ i + offSet ]; double[ ] arrTimeSub = _waveTransform.reverse( arrHilbSub ); for( int i = 0; i < arrTimeSub.length; i++ ) arrTime[ i + offSet ] = arrTimeSub[ i ]; offSet += arrHilbSub.length; } // m - no of sub transforms return arrTime; } // reverse /** * TODO Christian Scheiblich explainMeShortly * * @date 14.08.2010 10:43:28 * @author Christian Scheiblich * @see math.transform.jwave.handlers.BasicTransform#forward(double[], int) */ @Override public double[ ] forwardWavelet( double[ ] arrTime, int toLevel ) { double[ ] arrHilb = new double[ arrTime.length ]; int[ ] ancientEgyptianMultipliers = convertInteger2AncientEgyptianMultiplipliers( arrTime.length ); int offSet = 0; for( int m = 0; m < ancientEgyptianMultipliers.length; m++ ) { int ancientEgyptianMultiplier = ancientEgyptianMultipliers[ m ]; int arrTimeSubLength = (int)scalb( 1., ancientEgyptianMultiplier ); double[ ] arrTimeSub = new double[ arrTimeSubLength ]; for( int i = 0; i < arrTimeSub.length; i++ ) arrTimeSub[ i ] = arrTime[ i + offSet ]; double[ ] arrHilbSub = _waveTransform.forwardWavelet( arrTimeSub, toLevel ); for( int i = 0; i < arrHilbSub.length; i++ ) arrHilb[ i + offSet ] = arrHilbSub[ i ]; offSet += arrHilbSub.length; } // m - no of sub transforms return arrHilb; } // forward /** * TODO Christian Scheiblich explainMeShortly * * @date 14.08.2010 10:43:28 * @author Christian Scheiblich * @see math.transform.jwave.handlers.BasicTransform#reverse(double[], int) */ @Override public double[ ] reverseWavelet( double[ ] arrHilb, int fromLevel ) { double[ ] arrTime = new double[ arrHilb.length ]; int[ ] ancientEgyptianMultipliers = convertInteger2AncientEgyptianMultiplipliers( arrHilb.length ); int offSet = 0; for( int m = 0; m < ancientEgyptianMultipliers.length; m++ ) { int ancientEgyptianMultiplier = ancientEgyptianMultipliers[ m ]; int arrHilbSubLength = (int)scalb( 1., ancientEgyptianMultiplier ); double[ ] arrHilbSub = new double[ arrHilbSubLength ]; for( int i = 0; i < arrHilbSub.length; i++ ) arrHilbSub[ i ] = arrHilb[ i + offSet ]; double[ ] arrTimeSub = _waveTransform.reverseWavelet( arrHilbSub, fromLevel ); for( int i = 0; i < arrTimeSub.length; i++ ) arrTime[ i + offSet ] = arrTimeSub[ i ]; offSet += arrHilbSub.length; } // m - no of sub transforms return arrTime; } // reverse /** * Replaced Math.scalb due to google's Android OS is not supporting it in Math * lib. * * @date 19.04.2011 15:43:11 * @author sashi * @param f * @param scaleFactor * @return f times 2^(scaleFactor) */ public static double scalb( double f, int scaleFactor ) { return f * Math.pow( 2, scaleFactor ); } // scalb /** * Replaced Math.getExponent due to google's Android OS is not supporting it * in Math lib. * * @date 19.04.2011 15:43:16 * @author sashi * @param f * @return return p of 2^p <= f < 2^(p+1) */ public static int getExponent( double f ) { return (int)( Math.log( f ) / Math.log( 2 ) ); } // getExponent } // class