001/* ***** BEGIN LICENSE BLOCK *****
002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003 *
004 * The contents of this file are subject to the Mozilla Public License Version
005 * 1.1 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 * http://www.mozilla.org/MPL/
008 *
009 * Software distributed under the License is distributed on an "AS IS" basis,
010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011 * for the specific language governing rights and limitations under the
012 * License.
013 *
014 * The Original Code is JTransforms.
015 *
016 * The Initial Developer of the Original Code is
017 * Piotr Wendykier, Emory University.
018 * Portions created by the Initial Developer are Copyright (C) 2007-2009
019 * the Initial Developer. All Rights Reserved.
020 *
021 * Alternatively, the contents of this file may be used under the terms of
022 * either the GNU General Public License Version 2 or later (the "GPL"), or
023 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
024 * in which case the provisions of the GPL or the LGPL are applicable instead
025 * of those above. If you wish to allow use of your version of this file only
026 * under the terms of either the GPL or the LGPL, and not to allow others to
027 * use your version of this file under the terms of the MPL, indicate your
028 * decision by deleting the provisions above and replace them with the notice
029 * and other provisions required by the GPL or the LGPL. If you do not delete
030 * the provisions above, a recipient may use your version of this file under
031 * the terms of any one of the MPL, the GPL or the LGPL.
032 *
033 * ***** END LICENSE BLOCK ***** */
034
035package edu.emory.mathcs.jtransforms.dht;
036
037import java.util.concurrent.Future;
038
039import edu.emory.mathcs.jtransforms.fft.FloatFFT_1D;
040import edu.emory.mathcs.utils.ConcurrencyUtils;
041
042/**
043 * Computes 1D Discrete Hartley Transform (DHT) of real, single precision data.
044 * The size of the data can be an arbitrary number. It uses FFT algorithm. This
045 * is a parallel implementation optimized for SMP systems.
046 * 
047 * @author Piotr Wendykier (piotr.wendykier@gmail.com)
048 */
049public class FloatDHT_1D {
050    private int n;
051    private FloatFFT_1D fft;
052
053    /**
054     * Creates new instance of FloatDHT_1D.
055     * 
056     * @param n
057     *            size of data
058     */
059    public FloatDHT_1D(int n) {
060        this.n = n;
061        fft = new FloatFFT_1D(n);
062    }
063
064    /**
065     * Computes 1D real, forward DHT leaving the result in <code>a</code>.
066     * 
067     * @param a
068     *            data to transform
069     */
070    public void forward(float[] a) {
071        forward(a, 0);
072    }
073
074    /**
075     * Computes 1D real, forward DHT leaving the result in <code>a</code>.
076     * 
077     * @param a
078     *            data to transform
079     * @param offa
080     *            index of the first element in array <code>a</code>
081     */
082    public void forward(final float[] a, final int offa) {
083        if (n == 1)
084            return;
085        fft.realForward(a, offa);
086        final float[] b = new float[n];
087        System.arraycopy(a, offa, b, 0, n);
088        int nd2 = n / 2;
089        int nthreads = ConcurrencyUtils.getNumberOfThreads();
090        if ((nthreads > 1) && (nd2 > ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
091            nthreads = 2;
092            final int k1 = nd2 / nthreads;
093            Future<?>[] futures = new Future[nthreads];
094            for (int i = 0; i < nthreads; i++) {
095                final int firstIdx = 1 + i * k1;
096                final int lastIdx = (i == (nthreads - 1)) ? nd2 : firstIdx + k1;
097                futures[i] = ConcurrencyUtils.submit(new Runnable() {
098
099                    @Override
100                                        public void run() {
101                        int idx1, idx2;
102                        for (int i = firstIdx; i < lastIdx; i++) {
103                            idx1 = 2 * i;
104                            idx2 = idx1 + 1;
105                            a[offa + i] = b[idx1] - b[idx2];
106                            a[offa + n - i] = b[idx1] + b[idx2];
107                        }
108                    }
109
110                });
111            }
112            ConcurrencyUtils.waitForCompletion(futures);
113        } else {
114            int idx1, idx2;
115            for (int i = 1; i < nd2; i++) {
116                idx1 = 2 * i;
117                idx2 = idx1 + 1;
118                a[offa + i] = b[idx1] - b[idx2];
119                a[offa + n - i] = b[idx1] + b[idx2];
120            }
121        }
122        if ((n % 2) == 0) {
123            a[offa + nd2] = b[1];
124        } else {
125            a[offa + nd2] = b[n - 1] - b[1];
126            a[offa + nd2 + 1] = b[n - 1] + b[1];
127        }
128
129    }
130
131    /**
132     * Computes 1D real, inverse DHT leaving the result in <code>a</code>.
133     * 
134     * @param a
135     *            data to transform
136     * @param scale
137     *            if true then scaling is performed
138     */
139    public void inverse(float[] a, boolean scale) {
140        inverse(a, 0, scale);
141    }
142
143    /**
144     * Computes 1D real, inverse DHT leaving the result in <code>a</code>.
145     * 
146     * @param a
147     *            data to transform
148     * @param offa
149     *            index of the first element in array <code>a</code>
150     * @param scale
151     *            if true then scaling is performed
152     */
153    public void inverse(final float[] a, final int offa, boolean scale) {
154        if (n == 1)
155            return;
156        forward(a, offa);
157        if (scale) {
158            scale(n, a, offa);
159        }
160    }
161
162    private void scale(final float m, final float[] a, int offa) {
163        final float norm = (float) (1.0 / m);
164        int nthreads = ConcurrencyUtils.getNumberOfThreads();
165        if ((nthreads > 1) && (n >= ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
166            nthreads = 2;
167            final int k = n / nthreads;
168            Future<?>[] futures = new Future[nthreads];
169            for (int i = 0; i < nthreads; i++) {
170                final int firstIdx = offa + i * k;
171                final int lastIdx = (i == (nthreads - 1)) ? offa + n : firstIdx + k;
172                futures[i] = ConcurrencyUtils.submit(new Runnable() {
173
174                    @Override
175                                        public void run() {
176                        for (int i = firstIdx; i < lastIdx; i++) {
177                            a[i] *= norm;
178                        }
179                    }
180                });
181            }
182            ConcurrencyUtils.waitForCompletion(futures);
183        } else {
184            int lastIdx = offa + n;
185            for (int i = offa; i < lastIdx; i++) {
186                a[i] *= norm;
187            }
188
189        }
190    }
191}