/* * hoidla: various algorithms for Big Data solutions * Author: Pranab Ghosh * * 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. */ package org.streaminer.stream.wavelet; /** * Calculates Haar wavelet coefficients * @author pranab * */ public class HaarWaveletTransform implements IWavelet { private int[] coeff; private int average; private int numLargest; private int dataSize; /** * @param numLargest */ public HaarWaveletTransform(int numLargest) { this.numLargest = numLargest; } /** * @param compactCoeff * @param dataSetSize */ public HaarWaveletTransform(CompactHaarCoefficient compactCoeff, int dataSetSize) { average = compactCoeff.getAverage(); coeff = compactCoeff.reconstructCoeff(dataSetSize); numLargest = compactCoeff.getCompactCoeff().length; dataSize = coeff.length + 1; } public void setNumLargest(int numLargest) { this.numLargest = numLargest; } /** * @param data */ public void transform(int[] data) { if (isMultipleOfTwo(data.length)) { coeff = new int[data.length - 1]; findCoeff(data, coeff, 0); dataSize = data.length; } } /** * @return */ public int[] untransform() { int[] data = new int[1]; data[0] = average; int coeffOffset = coeff.length - 1; data = getData(data, coeffOffset); return data; } /** * @return */ public int[] getCoefficients() { return coeff; } public int getAverage() { return average; } /** * @return */ public CompactHaarCoefficient getCompactTransform() { return new CompactHaarCoefficient(coeff, average, numLargest); } /** * @param value * @return */ public boolean isMultipleOfTwo(int value) { int count = 0; for (int i = 0; i < 32; ++i) { if ((value & 1) == 1) { ++count; } value >>= 1; } return count == 1; } /** * @param data * @param coeffOffset * @return */ private int[] getData(int[] data, int coeffOffset) { int[] newData = new int[data.length * 2]; for (int i = 0, j = 0, k = coeffOffset; i < data.length; ++i, ++k) { newData[j++] = data[i] + coeff[k]; newData[j++] = data[i] - coeff[k]; } if (newData.length < dataSize) { getData(newData, coeffOffset + data.length); } return newData; } /** * @param data * @param coeff * @param current */ private void findCoeff(int[] data, int[] coeff, int current) { int[] newData = new int[data.length/2]; for (int i = 0, j = 0; i < data.length; i += 2, ++j) { newData[j] = (data[i] + data[i+1]) >> 1; coeff[current++] = (data[i] - data[i+1]) >> 1; } if (newData.length > 1) { findCoeff(newData, coeff, current); } else { average = newData[0]; } } /** * Compact representation with only large coefficients * @author pranab */ public static class CompactHaarCoefficient { private int average; private int[] compactCoeff; private long coeffBitMap; /** * @param coeff * @param average * @param numLargest */ public CompactHaarCoefficient(int[] coeff, int average, int numLargest) { this.average = average; int[] coeffCopy = new int[coeff.length]; for (int i = 0; i < coeff.length; ++i) { coeffCopy[i] = Math.abs(coeff[i]); } int smallest = coeffCopy[coeff.length - numLargest - 1]; compactCoeff = new int[numLargest]; for (int i = 0, j = 0; i < coeff.length; ++i) { if (Math.abs(coeff[i]) > smallest) { compactCoeff[j++] = coeff[i]; coeffBitMap |= (1 << i); } } } /** * deserializes from string * @param data */ public CompactHaarCoefficient(String data) { String[] parts = data.split(","); average = Integer.parseInt(parts[0]); coeffBitMap = Long.parseLong(parts[1]); compactCoeff = new int[parts.length -2]; for (int i = 2; i < parts.length; ++i) { compactCoeff[i - 2] = Integer.parseInt(parts[i]); } } @Override public String toString() { StringBuilder stBld = new StringBuilder(); stBld.append(average).append(",").append(coeffBitMap); for (int i = 0; i < compactCoeff.length; ++i) { stBld.append(",").append(compactCoeff[i]); } return stBld.toString(); } public int getAverage() { return average; } public int[] getCompactCoeff() { return compactCoeff; } public long getCoeffBitMap() { return coeffBitMap; } public int[] reconstructCoeff(int dataSetSize) { int[] coeff = new int[dataSetSize - 1]; for (int i = 0, j = 0; i < dataSetSize - 1; ++i) { if ((coeffBitMap & (1 << i)) == 1) { coeff[i] = compactCoeff[j++]; } else { coeff[i] = 0; } } return coeff; } } }